Files
multi-agent-mux/.agents/reports/canary-projects-multi-agent-mux-creator-claude/plan-f3b10c00.md
T

334 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 구현 계획서 Rev.2 — Job 7e5d9f2d
- **Job ID**: 7e5d9f2d (원 계획서: `f3b10c00`)
- **Role**: Planner (claude)
- **입력**: Worker Challenge `48a9416f` (`agy`, `[VERDICT: PASS WITH CHALLENGE]`)
- **Output**: `.mam/jobs/7e5d9f2d/claude-reports/report-final.md`
- **첨부**:
- `proposed/probe_new_session.sh` — new-session 호출 패턴 하네스 (Rev.1 에서 이월)
- `proposed/probe_layout_policy.sh`**신규**. 이의제기의 전제 5가지를 격리 herdr 세션에서 실측하는 하네스
---
## 0. 이의제기 판정 요약
이의제기를 **주장별로 분리해서 각각 실측**했다. 라이브 herdr 세션은 건드리지 않고 격리 세션(`mam-probe-*`)을 띄워
측정 후 `session stop` + `session delete` 로 정리했다(라이브 워크스페이스 3페인 불변 확인).
| 이의제기 항목 | 판정 | 근거 |
|---|---|---|
| **[맹점 1-a] `--split right` 반복으로 인한 패널 폭 고갈** | ✅ **채택** (수치 정정 후) | 실측으로 재현. 4페인 시 폭 14/14/13/27 col 까지 붕괴 |
| **[맹점 1-b] 종료된 유휴 패널 재활용 불가 → 고아 패널 증식** | ❌ **반박** | herdr 이 프로세스 종료 시 페인을 **자동 삭제**함(실측). `kill-session` 도 이미 `pane close` 수행. 그리고 `agent start` 에는 `--pane` 이 없어 재할당 자체가 불가능 |
| **[제약 1] `mock_herdr``save_state()` flock 필요** | ⚠️ **이미 구현됨** — 단, 이의제기가 놓친 **다른** 결함을 발견 | `conftest.py:90-91` 이 이미 `fcntl.flock(LOCK_EX)` 취득. 실제 위험은 `agents` 딕셔너리 **통째 덮어쓰기**(l.121) |
이의제기의 **핵심 지적(1-a)은 옳고, 원 계획서의 실질적 결함이었다.** 다만 근거 수치와 제안 해법은 둘 다 정정이 필요하다.
그리고 이의제기가 놓친 **더 중요한 메커니즘**을 하나 찾았다 — §2.2 의 "앵커가 전진하지 않는다".
---
## 1. 이의제기에 대한 실측 (신규 측정분)
재현 명령 (자체 정리 포함, 라이브 세션 무영향):
```bash
bash .mam/jobs/7e5d9f2d/claude-reports/proposed/probe_layout_policy.sh
```
### 1.1 프로덕션 현 상태 — 이의제기가 옳다
```
$ herdr pane layout # 라이브 workspace wM
area {height: 78, width: 184}
wM:p4 62 x 78 (agy)
wM:p3 61 x 78 (cline)
wM:p2 61 x 78 (claude)
splits: right(0.668) → right(0.5) ← 수평 분할 체인
```
**이미 수평 분할 체인 구조이며, 에이전트당 61 col 이다.** 이의제기가 지적한 구조가 실재한다.
단, 이의제기의 산술은 정정한다:
| 이의제기 | 실측 |
|---|---|
| "터미널 폭 `140col`" | **184 col**. `create_session.sh``-x 140 -y 40` 은 shim 이 `-x\|-y) shift 2`**버린다**(`lib.sh:238`). herdr 이 실제 터미널 크기를 쓴다 |
| "최소 렌더링 필요 폭 40~80 col" | **미검증 값**. 반면 **61 col 에서 3개 에이전트가 현재 정상 동작 중**이라는 것은 검증된 사실이다 |
따라서 임계점은 이의제기가 시사한 N=3 이 아니라 **N=4 (46 col) ~ N=5 (37 col)** 구간이다.
계획서는 이 임계값을 **하드코딩하지 않고 환경변수로 노출**하며, 기본값은 "현재 동작이 확인된 값"에서 취한다.
### 1.2 4페인 붕괴 직접 재현
격리 세션(area 54×23, 헤드리스 기본 크기)에서 `agent start` 3회:
```
area {height: 23, width: 54}
w1:p1 14 x 12 ← 3번 연속 분할당한 페인
w1:p4 14 x 11
w1:p3 13 x 23
w1:p2 27 x 23
splits: right(0.5) → right(0.5) → down(0.5)
```
폭 13~14 col. **이의제기의 우려는 추측이 아니라 재현 가능한 현상이다.**
### 1.3 [이의제기가 놓친 부분] 분할 앵커가 전진하지 않는다
위 레이아웃을 보면 `w1:p1`**세 번 모두** 분할 대상이 되었다(54→27→14 폭, 그 뒤 23→12 행).
`w1:p2`(27 col)는 한 번도 분할되지 않았다. 원인:
- `agent start --split <dir>` 의 분할 **앵커는 `focused_pane_id`** 다 (실측: 포커스가 `w1:p1` 에 고정된 채 모든 분할이 p1 에 누적).
- MAM 은 `--no-focus` 를 쓰므로 **포커스가 새 페인으로 이동하지 않는다** → 앵커가 영원히 제자리.
즉 실제 열화는 이의제기가 말한 "균등한 N분할(184/N)"보다 **더 나쁜 기하급수 분할(184/2^N)** 이다.
그리고 이 사실은 해법에도 영향을 준다: **`agent start` 로는 앵커를 고를 수 없고 방향만 고를 수 있다.**
(`agent start``--pane` 없음 — Rev.1 §2.1 실측. `pane focus` 는 방향 기반뿐, 임의 pane_id 지정 불가.)
### 1.4 [맹점 1-b 반박 근거] 종료된 페인은 herdr 이 자동으로 닫는다
```
임시 페인 생성: w1:p5
pane run w1:p5 "exit" → 3초 후 pane list: ['w1:p1','w1:p4','w1:p3','w1:p2','w2:p1']
→ w1:p5 없음 (herdr 이 자동 정리함)
```
추가로 MAM 자체 경로에서도 누적되지 않는다:
- `lib.sh:390-404` `kill-session``agent get` 으로 `pane_id` 를 얻어 **`pane close` 를 이미 수행**한다.
- 라이브 워크스페이스는 여러 차례 stop/start 를 거쳤음에도(LOG.md 세션 상태표) **에이전트 3개에 페인 정확히 3개**다. 잔여 페인 0.
**"고아/유휴 페인 증식"의 발생 경로가 존재하지 않는다.**
### 1.5 [맹점 1-b 반박 근거] 유휴 페인 재할당은 `has-session` 을 깨뜨린다
이의제기의 권고("유휴 패널의 `pane_id` 를 대상으로 에이전트를 재할당")를 실행할 수 있는 유일한 경로는
`pane split` + `pane run` 이다(`agent start``--pane` 이 없으므로). 그 결과를 측정했다:
```
pane run w1:p6 "sleep 300"
$ herdr agent get w1:p6
{"error":{"code":"agent_not_found","message":"agent target w1:p6 not found"}}
$ herdr agent list → 등록된 agent: ['probeC','probeB','probeA'] # p6 없음
```
`pane run` 으로 띄운 프로세스는 **agent 로 등록되지 않는다.**
shim 의 `has-session``_real_herdr agent get "$sess"`(`lib.sh:215`)이고, `kill-session``agent get` 으로 pane_id 를 찾는다.
따라서 이 권고를 채택하면 **세션 생존 확인과 세션 종료가 동시에 깨진다.**
> **`[REBUT: canary-projects-multi-agent-mux-creator-agy]`** — 맹점 1-b (유휴 패널 재활용) 항목에 한해 반박한다.
> 근거: (i) herdr 이 종료 페인을 자동 삭제하므로 증식 전제가 성립하지 않음(§1.4), (ii) `kill-session` 이 이미 `pane close` 수행(§1.4),
> (iii) `agent start` 에 `--pane` 이 없어 권고 자체가 표현 불가이며, 우회로인 `pane run` 은 agent 미등록으로 `has-session`/`kill-session` 을 파괴함(§1.5).
> 이는 **본 계획서 전체가 제거하려는 결함(존재하지 않는 CLI 능력을 전제한 설계)과 동일한 유형**이다.
> 맹점 1-a 는 반박하지 않고 전면 채택한다.
### 1.6 [제약 1 검증] mock 의 flock 은 이미 있다 — 진짜 결함은 다른 곳
```python
# tests/conftest.py:90-91 (프로세스 시작 시점, 상태 읽기 전)
lock_f = open(state_file + ".lock", "a")
fcntl.flock(lock_f, fcntl.LOCK_EX) # ← 이미 존재. lock_f 는 close 되지 않음
```
`lock_f` 를 닫지 않으므로 **락이 mock 프로세스 수명 전체를 덮는다.** 읽기(l.93-104)도 락 안에서 일어나고,
`save_state()`(l.109-135)는 디스크를 재조회한 뒤 `calls` 를 병합하고 `os.replace` 로 원자 치환한다.
**이의제기가 요구한 "flock 하 재조회 후 병합"은 이미 구현되어 있다.**
다만 이의제기가 지목하지 않은 실제 결함이 같은 함수에 있다:
```python
disk_state["agents"] = state.get("agents", {}) # l.121 — 키별 병합이 아니라 통째 덮어쓰기
if "workspaces" in state:
disk_state["workspaces"] = state["workspaces"] # l.122-123 — 동일
```
`calls` 만 병합되고 `agents`/`workspaces` 는 **이 프로세스가 시작 시점에 읽은 스냅샷으로 통째 교체**된다.
현재 안전한 이유는 오직 **락이 프로세스 수명 전체를 덮어 mock 호출이 완전 직렬화되기 때문**이다.
즉 이 락은 성능 최적화가 아니라 **정합성의 유일한 근거(load-bearing)** 다. 이 사실이 코드 어디에도 적혀 있지 않다.
부작용도 있다: 모든 mock herdr 호출이 전역 직렬화되므로 병렬 pytest 의 이득이 사라진다
(Rev.1 §7.3 에서 보고한 `test_tier3_integration.py` 10분 초과의 후보 원인 중 하나이나, **분리 측정하지 않았다**).
---
## 2. Rev.1 → Rev.2 변경 요약
| 구분 | 내용 |
|---|---|
| 신규 | **W2a** 분할 방향 정책 (`pane layout` 기반), **W2b** 오버플로 시 신규 워크스페이스, **W17** mock 락 불변식 명문화 + 테스트 |
| 신규 | 테스트 **H-11 ~ H-14** (분할 정책 / 오버플로 / 앵커 / mock 락 불변식) |
| 수정 | **W2** 재사용 분기에서 `--split right` **무조건 부착 → 정책 기반 선택**으로 변경 |
| 수정 | §7 리스크 2번(`--workspace` 권위 여부 미측정) → **실측 완료, 리스크 해소** |
| 유지 | W1, W3~W16 및 Phase 2/3/4 전부 Rev.1 그대로 (이의제기가 다루지 않았고, 새 측정으로도 흔들리지 않음) |
| 불채택 | 유휴 페인 재할당 분기 (§1.5) |
Rev.1 의 핵심 결론(§0: `--kind`/`--pane` 부재, `WorkspaceInfo.cwd` 부재, mock 계약 불일치)은 **전부 그대로 유효**하며
이의제기 역시 이 부분은 "매우 정확하고 타당"하다고 인정했다. 아래에는 변경/추가된 부분만 상세히 적고,
변경 없는 항목은 표로만 재수록한다. **원문 전체는 `.mam/jobs/f3b10c00/claude-reports/report-final.md` 를 병행 참조할 것.**
---
## 3. 리팩토링 작업 계획 (Rev.2)
> 역할 경계: 본 계획서는 Planner 산출물이며 코드 수정은 Creator(agy)가 수행한다 (`.agents/MULTI_AGENT_RULES.md` §1).
### Phase 1 — herdr 실계약 기준 new-session 재작성 (P0)
대상: `.agents/skills/lib.sh` `new-session` case (현행 246-374).
| ID | 작업 | 수용 기준 | 변경 |
|----|------|-----------|------|
| **W1** | 워크스페이스 해석을 `workspace list`(cwd 없음) → **`pane list` 기반 cwd 매칭**으로 교체. `realpath(pane.cwd) == realpath(target)` 인 페인의 `workspace_id` 채택 | probe `reuse` 모드에서 `existing_ws` 결정, cwd 불일치 시 빈 값 | 유지 |
| **W2** | `--kind`/`--pane` 분기 전량 삭제. `agent start` 단일 문법:<br>재사용: `agent start "$name" --workspace "$ws_id" --cwd "$ws" $split_arg $env_flags -- $final_cmd`<br>신규: `workspace create --cwd "$ws" --no-focus``result.workspace.workspace_id` → 동일 형태(`$split_arg` 없음)<br>**`$split_arg` 는 W2a 정책이 결정** | `--kind`/`--pane` 0회. 신규 경로에서 `--workspace` 필수 포함 | **수정** |
| **W2a** | **[신규]** 분할 방향 정책. 재사용 분기에서만 동작:<br>1. `pane layout --pane <타깃 ws 의 페인 하나>``area` / `panes[].rect` / `focused_pane_id` 취득<br>2. **앵커 = `focused_pane_id` 의 rect** (실측 §1.3)<br>3. `anchor.width / 2 >= MAM_MIN_PANE_COLS``--split right`<br>4. elif `anchor.height / 2 >= MAM_MIN_PANE_ROWS``--split down`<br>5. else → **W2b 로 위임**<br>기본값 `MAM_MIN_PANE_COLS=60`, `MAM_MIN_PANE_ROWS=20` | H-11. 184×78 앵커 → `right`; 61×78 앵커 → `down`; 61×39 앵커 → W2b | **신규** |
| **W2b** | **[신규]** 오버플로 정책. W2a 5번에 도달하면 기존 워크스페이스를 **분할하지 않고** 신규 워크스페이스 생성 경로로 전환한다(= `workspace create``--workspace <new>`, `--split` 없음) | H-12. 포화 상태에서 `--split` 미부착 + 신규 `workspace create` 발생 | **신규** |
| **W3** | pane_id 탐지 기구(315-358) 및 `pane split` 선행 호출 전량 삭제. `w1:p1` 하드코딩 폴백 삭제. `root_pane or workspace_id` 타입 혼동 폴백 삭제 | probe `reuse` 에서 `pane split` 0회, 고아 자원 0 | 유지 |
| **W4** | `kind` 캐스케이드(267-280) + strip 블록(282-294) + `${final_cmd:-}` 삭제. `final_cmd` 절대경로 보존 | `-- ` 뒤 첫 토큰 = 입력 절대경로 | 유지 |
| **W5** | 재시도 최대 3회, `0.5→1→2` 백오프. 출력이 `usage:` / unknown flag 계열이면 **즉시 중단** | probe `create/fail` 에서 `agent start` 1회, <1s | 유지 |
| **W6** | 실패 시 `echo "$res" >&2``exit 1` | probe `create/fail` rc=1 + stderr | 유지 |
| **W7** | 가드 없는 명령치환에 `\|\| echo ""` 부착 (shim 은 `set -euo pipefail`) | herdr 부재 시 W6 형태로 명시적 종료 | 유지 |
**W2a 설계 근거 및 한계 (반드시 구현자에게 전달)**
- 방향만 고를 수 있고 **앵커는 고를 수 없다.** `agent start``--pane` 이 없고, `pane focus` 는 방향 기반이라
임의 pane_id 를 포커스할 수 없다. 따라서 정책은 "앵커(=현재 포커스 페인)의 rect 를 반으로 나눴을 때 살아남는 축"을 고르는 것이다.
- `--split` **을 생략해도 새 페인은 생긴다**(실측 §1.2 P3: `--split` 없이도 `w1:p3` 신규 생성). 즉 `--split` 은 재사용 스위치가 아니라 방향 지정자다.
- 기본값 근거: `MAM_MIN_PANE_COLS=60` 은 "**61 col 에서 3개 에이전트가 현재 정상 동작**"이라는 검증된 사실에서 취한 값이다.
이의제기의 40~80 은 근거가 제시되지 않았으므로 채택하지 않는다. 값이 틀렸다고 판단되면 **환경변수로 조정 가능**하게 만든 것이 이 항목의 요점이다.
- `pane layout``--workspace` 필터가 없다. 타깃 워크스페이스의 페인 하나를 `pane list` 에서 골라 `--pane <id>` 로 조회한다.
- `agent start` 응답의 `result.agent.{workspace_id,pane_id}` 로 **배치 결과를 사후 검증**할 수 있다(실측 확인). W6 의 성공 판정에 활용할 것.
### Phase 2 — mock 계약 정합화 (P0, Phase 1 과 동시)
대상: `tests/conftest.py`
| ID | 작업 | 수용 기준 | 변경 |
|----|------|-----------|------|
| **W8** | `agent start`**미지 플래그 거부**(화이트리스트 `--cwd --workspace --tab --split --env --focus --no-focus`). 위반 시 usage 배너 출력 + 에이전트 미생성 | `--kind`/`--pane` 전달 테스트가 실패함을 선확인 | 유지 |
| **W9** | 성공 시 `{"result":{"type":"agent_started","agent":{...,"pane_id","workspace_id"},"argv":[...]}}` 출력 | shim 의 `grep -q agent_started` 성립 | 유지 |
| **W10** | `workspace list` 에서 **`cwd` 제거**. `workspace create``workspace_created` 전체 응답 출력 | §3.1 죽은 코드가 mock 에서도 죽음 | 유지 |
| **W11** | `pane list`(+`--workspace` 필터), `pane split`, **`pane layout`** 핸들러 추가. `pane layout``area`/`panes[].rect`/`focused_pane_id` 반환. **분할 시 앵커를 `focused_pane_id` 로 두고 rect 를 실제로 반분**하여 W2a 를 검증 가능하게 할 것 | W2a 정책이 mock 에서 검증 가능 | **수정** |
| **W17** | **[신규]** `save_state()` 의 락 불변식 명문화: (a) "`lock_f` 는 의도적으로 close 하지 않으며, 이 전역 직렬화가 `agents`/`workspaces` 통째 덮어쓰기의 유일한 정합성 근거"라는 주석 추가, (b) 락을 세분화하려면 **반드시 키별 병합으로 먼저 전환**해야 한다는 경고, (c) H-14 테스트 추가 | H-14 PASS | **신규** |
> W17 은 이의제기 [제약 1]에 대한 응답이다. 요구된 flock 자체는 이미 있으므로 추가 구현이 아니라
> **불변식 문서화 + 회귀 테스트**로 대응한다. 코드를 바꾸지 않는 이유는 §1.6 참조.
### Phase 3 — 계약 회귀 테스트 (P1)
신규 파일: `tests/test_herdr_shim_contract.py`
| ID | 케이스 | 목적 | 변경 |
|----|--------|------|------|
| H-1 | `agent start` 에 화이트리스트 외 플래그를 절대 넘기지 않는다 | §3.2 재발 방지 | 유지 |
| H-2 | 신규 경로에서 `workspace create` id 가 `--workspace` 로 전달된다 | 고아 워크스페이스 방지 | 유지 |
| H-3 | 재사용 경로에서 `pane split` 을 선행 호출하지 않는다 | 고아 페인 방지 | 유지 |
| H-4 | `-- ` 뒤 첫 토큰 = 입력 절대경로 | §3.5 재발 방지 | 유지 |
| H-5 | usage 배너 → 재시도 없이 rc=1 + stderr | §3.4 재발 방지 | 유지 |
| H-6 | 일시 실패에만 최대 3회 재시도, 총 <3s | §3.4 재발 방지 | 유지 |
| H-7 | `env FOO=bar``--env FOO=bar` 정확히 1회 | `b0c2c08` 회귀 방지 | 유지 |
| H-8 | herdr 부재 시 무성 종료하지 않는다 | W7 | 유지 |
| H-9 | mock 응답이 `tests/fixtures/herdr_contract.json` 을 만족 | mock 표류 감지 | 유지 |
| H-10 | (herdr 미설치 시 skip) `herdr api schema` 실측 = 고정 픽스처 | 바이너리 업그레이드 감지 | 유지 |
| **H-11** | 앵커 rect 가 넓으면 `--split right`, 좁고 높으면 `--split down` | **W2a** | **신규** |
| **H-12** | 양축 모두 임계 미만이면 `--split` 미부착 + 신규 `workspace create` | **W2b** | **신규** |
| **H-13** | `MAM_MIN_PANE_COLS` / `MAM_MIN_PANE_ROWS` 환경변수가 실제로 정책을 바꾼다 | 임계값 하드코딩 방지 | **신규** |
| **H-14** | mock 을 동시 다중 프로세스로 호출해도 `agents` 유실 0 (락 불변식) | **W17** | **신규** |
`proposed/probe_new_session.sh` 를 H-1~H-8/H-11~H-13 의 구동 기반으로 재사용한다.
`proposed/probe_layout_policy.sh` 는 **실 바이너리 계약이 바뀌지 않았음을 확인하는 수동 점검용**이다(CI 비포함 — herdr 서버가 필요).
### Phase 4 — 미리뷰 항목 정리 (P1) — Rev.1 그대로
| ID | 작업 |
|----|------|
| **W12** | `resolve_herdr_session``val != 'default'` 스킵 동작 확정 + 테스트 2건 |
| **W13** | `ws``HERDR_SESSION_NAME` 동시 지정 시 슬러그 우선 고정 (cline `83181aad` 지적) |
| **W14** | `.mam/shim/` 잔여 임시파일 정리 (`_init_herdr_isolation` 에 선행 `rm -f`) |
### Phase 5 — 선택 (P2)
- **W15**: `loop_lock.sh` steal 락 순서 교정/제거 (별도 잡 권장)
- **W16**: `agent start --no-focus` 부착 검토. **주의**: §1.3 에 따라 `--no-focus` 는 분할 앵커를 고정시켜 열화를 가속한다.
W2a 와 상호작용하므로 **W16 은 W2a 확정 이후에 재검토**할 것. (Rev.1 대비 성격 변경)
---
## 4. 리뷰 라운드 운영 계획
### 4.1 cline 리뷰 브리프 필수 요구사항
1. **실 바이너리 대조 필수**: `herdr agent --help`, `herdr api schema --json` 을 인용하고 전달 플래그를 1:1 대조. "코드가 논리적으로 맞다"를 PASS 근거로 삼지 말 것.
2. **probe 하네스 실행 필수**: `probe_new_session.sh``create/fail`, `create/ok`, `reuse/ok` 3모드로 실행하고 herdr 호출 내역 전량 첨부.
3. **누수 점검 필수**: 생성/재사용 경로 각각에서 미소비 workspace/pane 이 0 임을 호출 내역으로 입증.
4. **실패 경로 점검 필수**: 전량 실패 시 rc≠0 + stderr 확인.
5. **mock 정합성 점검**: Rev.1 §2.1 계약표와 항목별 대조.
6. **[신규] 레이아웃 정책 점검**: `probe_layout_policy.sh` 실행 후 (a) 분할 앵커가 `focused_pane_id` 라는 전제가 유지되는지,
(b) W2a 가 앵커의 실제 rect 를 읽는지(워크스페이스 평균이나 `pane_count` 가 아니라), (c) 포화 시 W2b 로 빠지는지 확인.
7. **[신규] 회귀 앵커 확인**: `agent start``--pane` 을 넘기는 코드가 **한 줄도 없는지** grep 으로 확인.
### 4.2 라운드 구성
| 라운드 | 담당 | 입력 | 산출 |
|--------|------|------|------|
| R0 (완료) | claude | 전 작업분 + herdr 실계약 | 계획서 Rev.1 (`f3b10c00`) |
| R0.5 (완료) | agy | Rev.1 | 이의제기 `48a9416f` |
| **R0.6 (본 문서)** | claude | 이의제기 + 신규 실측 | **계획서 Rev.2** |
| R1 | agy | Phase 1 + Phase 2 | 구현 + probe 2종 결과 |
| R2 | cline | R1 결과물 | §4.1 7개 항목 대조 리포트 |
| R3 | agy | Phase 3 + Phase 4 | `test_herdr_shim_contract.py` (H-1~H-14) + W12~W14 |
| R4 | cline + claude | 전체 | 최종 판정 |
R2 에서 NOT PASS 시 R1 로 복귀. Phase 3 는 Phase 1/2 PASS 이후 착수(계약 확정 전 테스트를 먼저 쓰면 잘못된 계약을 고정하게 된다).
---
## 5. 검증 계획
| 단계 | 명령 | 통과 기준 |
|------|------|-----------|
| 문법 | `bash -n .agents/skills/lib.sh`, `bash -n .mam/shim/herdr` | PASS |
| 정적 | CI shellcheck (`deploy/gitea-ci.yml:31`) — 로컬 미설치 | 신규 경고 0 |
| 계약 | `probe_new_session.sh` × 3모드 | Phase 1 수용 기준 충족 |
| **레이아웃** | `probe_layout_policy.sh` (격리 세션, 자체 정리) | P0~P5 전제가 §1 실측과 일치 |
| 단위 | `pytest tests/test_herdr_shim_contract.py` | H-1~H-14 전부 PASS |
| 회귀 | `pytest tests/test_orc_onboard.py tests/test_workspace_scope.py tests/test_tier1_unit.py tests/test_deploy_layout.py tests/test_deploy_freshness.py` | 기존 통과 수 유지 |
| 통합 | `pytest tests/test_tier3_integration.py tests/test_tier4_e2e.py` | HEAD 기준으로도 10분 초과(실측). 별도 시간 예산 필요 |
| 실환경 | 실 herdr 세션에서 `create_session.sh` 1회 → `herdr pane layout` | 고아 자원 0, 신규 페인 폭 ≥ `MAM_MIN_PANE_COLS` |
---
## 6. 리스크 및 범위 밖 항목 (Rev.2 갱신)
1. **herdr 버전 종속성**: 모든 계약은 **herdr 0.7.4 (protocol 16)** 실측. H-10 이 업그레이드 시점을 잡아준다.
2. ~~`agent start --workspace` 의 배치 권위 미측정~~**해소.** `agent start --workspace w1` 이 포커스가 다른 상태에서도 w1 에 배치함을 실측(§1.2 P1).
3. **에이전트 TUI 최소 렌더링 폭은 여전히 미측정**이다. `MAM_MIN_PANE_COLS=60` 은 "61 col 3에이전트 정상 동작"이라는 **관측 사실**에서 취한 값이지,
claude/agy TUI 의 실제 하한을 측정한 값이 **아니다**. 하한을 정확히 알려면 폭을 줄여가며 렌더 깨짐을 관찰해야 하는데,
이는 라이브 에이전트 세션을 손상시키므로 수행하지 않았다. **환경변수로 노출한 이유가 이것이다.**
4. **분할 앵커 = `focused_pane_id`** 는 격리 세션 4회 관측에 기반한다. herdr 이 포커스 외 다른 규칙(예: 최근 생성 페인)을 쓰는 경계 조건은 확인하지 않았다.
W2a 는 앵커를 **가정하지 않고 `pane layout` 에서 읽으므로**, 이 전제가 틀려도 정책이 잘못된 페인을 기준 삼을 뿐 크래시하지는 않는다.
5. **mock 직렬화가 tier3 런타임에 미치는 영향은 분리 측정하지 못했다**(§1.6). tier3 는 HEAD 기준으로도 10분을 초과하는 선재 문제다.
6. **`loop_lock.sh` 는 정적 분석 기반 리뷰**이며 실제 경합을 재현하지 않았다.
7. **범위 밖**: orc-onboard 스킬(리뷰 완료), `agent_identities` 최상위 키 미검증(`5650172e` §7.1 이월), W15/W16.
8. **측정 부작용 없음 확인**: 격리 세션 `mam-probe-f3b` / `mam-verify-7e5``session stop` + `session delete` 로 제거했고,
라이브 워크스페이스 `wM` 은 측정 전후 동일하게 3페인(agy/cline/claude)을 유지했다. `git status` 는 측정 전과 동일하다.
---
## 7. 결론
이의제기의 핵심(맹점 1-a)은 **옳고, 원 계획서의 실질적 결함이었다.** 재사용 분기에서 `--split right` 를 무조건 부착하면
패널 폭이 고갈된다 — 격리 세션에서 13~14 col 까지 붕괴하는 것을 직접 재현했다. W2a/W2b 로 전면 채택한다.
동시에 두 가지를 정정한다. 첫째, 실제 열화는 이의제기가 상정한 `184/N` 균등 분할이 아니라 **`--no-focus` 때문에 앵커가 고정되어 발생하는 `184/2^N` 분할**이며,
`agent start` 로는 방향만 고를 수 있고 앵커는 고를 수 없다. 둘째, **유휴 페인 재활용은 필요하지도, 가능하지도 않다**
herdr 이 종료 페인을 자동 삭제하고(실측), `kill-session` 은 이미 `pane close` 를 수행하며, `pane run` 우회로는 agent 미등록으로 `has-session` 을 파괴한다.
이 권고를 그대로 채택했다면 **존재하지 않는 CLI 능력을 전제한 설계**라는, 본 계획서가 제거하려는 바로 그 결함을 다시 도입했을 것이다.
`mock_herdr` 의 flock 요구는 이미 충족되어 있었다. 대신 같은 함수에서 **`agents` 통째 덮어쓰기가 전역 락에 의존하고 있다는 미문서화 불변식**을 발견해
W17/H-14 로 고정한다.
> 아래 토큰은 **본 계획서 산출물의 완료 표시**다. 검토 대상 코드에 대한 판정은 Rev.1 §3 그대로
> — 미커밋 working tree = **NOT PASS**, `20e2e9b`/`b0c2c08` = **판정 정정 필요**.
[REBUT: canary-projects-multi-agent-mux-creator-agy]
[AGREEMENT: REACHED]