# P2-2 (C-3a + C-4) 레거시 코드 정리 — 구현 계획서 **Rev.2**

- **Job**: `84300b64` · **Role**: Planner (`MULTI_AGENT_RULES.md` §1 — 저장소 코드 미수정)
- **선행 계획**: `07740cc7` (Rev.1) · **반영 챌린지**: `e63843ca` (`agy`, `[VERDICT: PASS WITH CHALLENGE]`)
- **기준 커밋**: `5e519e2` (Rev.1 과 **동일** — HEAD 이동 없음, §3.3 참조) · **작성 시각**: 2026-08-16
- **산출물**: 본 계획서 + `proposed/p2-2_c3a_c4.rev2.patch` + `proposed/test_legacy_isolate_flags_noop.py` + `proposed/pytest_rev2.log`
- **검증 방식**: `git clone --local --no-hardlinks` 로 만든 스크래치패드 사본에 패치를 적용해 전체 스위트 + 변이 검사(mutation check)를 실행했습니다. 본 저장소 워킹 트리는 계획 수립 전후 모두 clean 입니다.

---

## 0. 챌린지 판정 요약

| # | 챌린지 | 판정 | 근거 |
|---|---|---|---|
| **1** | `--isolate`/`--no-isolate` 자동화 회귀 테스트 부재 | **✅ 수용 + 강화** | 제시된 테스트를 그대로 실행 → 통과(0.09s). 변이 4종 중 3종 검출. 나머지 1종(usage 문서 줄 삭제)을 잡도록 **assert 1줄 추가** |
| **2** | `test_tier1_unit.py:31` 섹션 헤더 `(7 Test Cases)` 동기화 | **✅ 수용** | 현재 5개 헤더 **전부 정확**(7/6/5/5/6 = 29 = 실측)함을 확인. 방치하면 이 파일 최초의 불일치가 됨. `(5 Test Cases)` 로 갱신 |
| **3** | `IMPROVEMENTS.md` 라인 번호를 최신 HEAD 로 동기화 | **⚖️ 사실관계는 반박, 우려는 수용** | HEAD 는 `5e519e2` 로 **이동하지 않았고** Rev.1 의 20개 인용 라인은 **전부 현행 일치**. 챌린지의 "문두 완료 **15건**" 은 실측 **16건**. 다만 §6.1 편집들이 **서로의 오프셋을 밀어내는** 문제는 실재하므로 **편집 순서 명세를 신설**(§4.3) |

**Rev.1 대비 순증분**: 테스트 1건 추가(순감 4 → 순감 3), 섹션 헤더 1줄, 편집 순서 명세 1개 절. 수집 개수 **259 → 256**.

---

## 1. Challenge 1 검증 — 수용, 그리고 한 줄 강화

### 1.1 제안된 테스트를 그대로 실행

챌린저가 제시한 코드를 **한 글자도 고치지 않고** 패치된 사본에 넣어 실행했습니다.

```
1 passed in 0.13s
0.09s call     test_create_session_legacy_isolate_flags_noop
0.02s setup
```

동작합니다. 다만 **실측 0.09s** 로, 챌린지가 적은 `<0.05s` 보다 약 2배입니다. 원인은 `create_session.sh:25` 가 인자 파싱 **이전에** `source "$_lib_sh"` 를 하기 때문이며(플래그 2개 × 서브프로세스 2회), 절대값이 미미하므로 채택에는 영향이 없습니다. 계획에는 실측값으로 적습니다.

### 1.2 변이 검사 — 이 테스트가 실제로 무엇을 잡는가

"통과한다" 는 것만으로는 가드가 되지 못하므로, 이 테스트가 막으려는 회귀를 직접 주입해 **실패하는지** 확인했습니다.

| 변이 | 내용 | 챌린지 원안 | 강화안 |
|---|---|---|---|
| **A** | `--isolate` · `--no-isolate` 분기 **둘 다 삭제** | ✅ FAIL (`rc=2`, `ERROR: unknown arg: --isolate`) | ✅ FAIL |
| **B** | `--no-isolate` **한쪽만** 삭제 | ✅ FAIL (`ERROR: unknown arg: --no-isolate`) | ✅ FAIL |
| **C** | 분기는 두되 `echo` 를 지워 **조용한 no-op** 으로 | ✅ FAIL (stderr assert) | ✅ FAIL |
| **D** | 분기는 두되 `usage()` 의 문서 줄(`:42-43`) 삭제 | ❌ **PASS (놓침)** | ✅ FAIL |
| **E** | 무변이 대조군 | ✅ PASS | ✅ PASS |

변이 A/B/C 를 잡는다는 점에서 챌린지의 지적은 **정확하고 실효적**입니다. 특히 B(한쪽만 삭제)를 잡는 것은 `for flag in [...]` 루프 덕분이며, 원안 설계가 이미 이 경우를 고려했음을 보여줍니다.

**D 만 빠져나갑니다.** `--isolate`/`--no-isolate` 는 `create_session.sh:42-43` 에서 **usage 에 정식 문서화되어 있는** 옵션입니다. 챌린지가 지목한 "누군가 미사용으로 오판하여 삭제" 시나리오에서, 가장 먼저 지워질 후보는 실행 분기가 아니라 **도움말 줄**입니다(C-6 이 정확히 "도움말과 실제 파서의 불일치" 과제인 점을 상기하십시오). 그리고 `-h` 를 이미 실행하고 있으므로 그 출력은 **이미 `res.stdout` 에 잡혀 있습니다** — 서브프로세스 추가 없이 assert 한 줄이면 닫힙니다.

### 1.3 채택 최종본

```python
def test_create_session_legacy_isolate_flags_noop(mam_sandbox):
    """Legacy --isolate/--no-isolate must stay a documented no-op, not an arg-parser error."""
    create_script = mam_sandbox / "skills" / "multi-agent-mux-create" / "scripts" / "create_session.sh"
    for flag in ["--isolate", "--no-isolate"]:
        res = subprocess.run(["bash", str(create_script), flag, "-h"], capture_output=True, text=True)
        assert res.returncode == 0, f"{flag} rejected by arg parser: {res.stderr}"
        assert "NOTE: --isolate/--no-isolate is a no-op" in res.stderr
        assert flag in res.stdout, f"{flag} missing from usage() help text"
```

원안 대비 변경은 **3줄**입니다.

1. `assert flag in res.stdout` **신설** — 변이 D 를 닫습니다. 부분 문자열 오탐 우려가 있어 확인했으나 **`"--isolate" in "--no-isolate"` 는 `False`** 입니다(`--no-isolate` 는 `--no` 다음에 하이픈이 하나뿐이므로 `--isolate` 를 부분 문자열로 포함하지 않음). 따라서 단순 `in` 으로 두 플래그가 모호함 없이 구분됩니다.
2. `assert res.returncode == 0` 에 **실패 메시지 추가** — 실패 시 `assert 2 == 0` 대신 어느 플래그가 왜 거부됐는지 즉시 보이게 합니다(루프라서 어느 회차인지 모호해집니다).
3. docstring 을 계약 문장으로 교체 — "documented no-op" 이 assert 3개의 의도를 그대로 서술합니다.

### 1.4 배치 결정 — `test_tier1_unit.py` FEATURE 1

챌린지의 제안대로 tier1 에 둡니다. 스크립트를 실행하는 테스트라 tier2 도 후보였으나, **동일 파일에 정확한 선례가 있습니다**:

```python
def test_resume_script_invalid_args(mam_sandbox):          # tier1:114 (현행)
    script_path = mam_sandbox / "skills" / "multi-agent-mux-resume" / "scripts" / "resolve_session_id.sh"
    res = subprocess.run(["bash", str(script_path), ...], capture_output=True, text=True)
    assert res.returncode == 2
    assert "ERROR: --agent required" in res.stderr
```

`mam_sandbox / "skills" / ...` 경로 관례, `subprocess.run`, rc + stderr assert — 신규 테스트가 이 관용구를 그대로 따릅니다. tier1 은 이미 **인자 파서 단위 테스트의 자리**입니다. `subprocess` 는 `tests/test_tier1_unit.py:2` 에서 이미 임포트되어 있어 추가 임포트도 없습니다.

**삭제되는 3건이 있던 바로 그 자리**(`test_create_derive_session_name_weird_characters` 와 `test_create_validate_env_key` 사이)에 넣습니다.

### 1.5 격리 검증 — 신규 테스트는 저장소를 오염시키지 않는가

이 테스트는 `create_session.sh` 를 실행하고, 그 스크립트는 `:25` 에서 `lib.sh` 를 source 하며, `lib.sh` 는 `_init_herdr_isolation` 으로 `$WORKSPACE_ROOT/.mam/shim/herdr` 를 **씁니다**. 실제로 쓰기가 일어나는 테스트이므로 확인했습니다.

```
rm -rf <clone>/.mam
pytest ...::test_create_session_legacy_isolate_flags_noop  →  1 passed
after run, .mam exists?  NO
```

`conftest.py:44` 의 `monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))` 가 서브프로세스까지 상속되어 쓰기가 `tmp_path` 안에 갇힙니다. **저장소 트리에 흔적 0건.**

(참고: 전체 스위트를 돌리면 사본에 `.mam/shim/` 이 생깁니다. 이는 **다른 기존 테스트**들이 만드는 것으로 P2-2 이전부터의 성질이며 `.gitignore:14` 대상입니다. 신규 테스트가 원인이 아님을 위 실험이 분리해 보여 줍니다.)

### 1.6 이 테스트가 여전히 잡지 못하는 것 (명시)

- `create_session.sh` **본문**의 동작(세션 생성 자체)은 검증하지 않습니다. `-h` 로 조기 종료하므로 파서 진입 지점까지만 봅니다. 이는 의도된 범위입니다 — 챌린지가 요구한 것은 "인자 파서 게이트" 입니다.
- 다른 레거시 no-op 플래그가 생기면 이 테스트는 자동으로 커버하지 않습니다. `for flag in [...]` 목록에 추가해야 합니다.

---

## 2. Challenge 2 검증 — 수용, 범위 명확화

`tests/test_tier1_unit.py:31` 의 `# FEATURE 1: Create Session (7 Test Cases)` 를 갱신하라는 지적입니다. 파일 전체의 헤더 정합성을 실측했습니다.

| 헤더 라인 | 섹션 | 선언 | 실측 |
|---|---|---|---|
| 31 | FEATURE 1: Create Session | 7 | **7** ✅ |
| 106 | FEATURE 2: Resume Session | 6 | **6** ✅ |
| 152 | FEATURE 3: Stop Session | 5 | **5** ✅ |
| 197 | FEATURE 4: Status Query | 5 | **5** ✅ |
| 283 | FEATURE 5: Monitor/Reconcile | 6 | **6** ✅ |
| | 합계 | 29 | **29** (`grep -c "^def test_"` = 29) ✅ |

**5개 헤더 전부 현재 정확합니다.** 이 파일은 메타데이터를 성실하게 유지해 온 파일이고, 따라서 `(7 Test Cases)` 를 방치하면 그것이 **이 파일 최초의 불일치**가 됩니다. 챌린지 판단이 옳습니다.

**갱신값은 `(5 Test Cases)`** 입니다 — 7 − 3(삭제) + 1(신규) = 5. 다른 4개 헤더는 손대지 않습니다(변동 없음).

패치 적용 후 재실측:

```
  31 FEATURE 1: Create Session      claimed=5 actual=5 OK
  77 FEATURE 2: Resume Session      claimed=6 actual=6 OK
 123 FEATURE 3: Stop Session        claimed=5 actual=5 OK
 168 FEATURE 4: Status Query        claimed=5 actual=5 OK
 254 FEATURE 5: Monitor/Reconcile   claimed=6 actual=6 OK
 file total: 27
```

`tests/test_tier2_component.py` 에는 이런 개수 선언 헤더가 없으므로 해당 파일은 추가 조치 불필요합니다.

---

## 3. Challenge 3 판정 — 사실관계 반박, 우려는 §4.3 으로 수용

### 3.1 HEAD 는 이동하지 않았습니다

```
$ git rev-parse --short HEAD
5e519e2
$ git log --oneline -1
5e519e2 docs(improvements): synchronize header counts and roadmap table with completed P2-1 task
```

Rev.1 의 기준 커밋이 `5e519e2` 이고 현재 HEAD 도 `5e519e2` 입니다. 챌린지가 지목한 `b490713`(P2-1 수정)은 **4 커밋 이전**이며, 그 이후의 `af3dc16` → `a875b13` → `5e519e2` 가 전부 문서 커밋입니다. 그중 `5e519e2` 는 커밋 제목 그대로 **"헤더 개수와 로드맵 표를 P2-1 완료와 동기화"** 한 커밋 — 즉 챌린지가 요구하는 동기화는 **Rev.1 작성 시점에 이미 반영된 상태**였습니다.

### 3.2 Rev.1 의 인용 라인 20개 전수 재검증

챌린지를 계기로 §6.1·§6.3 이 인용한 모든 라인을 다시 대조했습니다.

| 인용 | 현행 내용 | 판정 |
|---|---|---|
| `:5` | `총 추적 미해결 과제: 9건 (아키텍처 2, 엣지케이스 4, 오케스트레이션 0, 레거시 잔재 3)` | ✅ |
| `:6` | `완료된 과제: **16건** (A-1 … P2-1-DelegateJobSafe-TrapFix)` | ✅ |
| `:70` | `## 2. 엣지 케이스 및 런타임 버그 (Edge-case Bugs — 5건)` | ✅ |
| `:107` | `## 4. 레거시 잔재 및 죽은 코드 (Legacy Remnants — 3건)` | ✅ |
| `:109-111` | C-3 제목 / C-3a / C-3b | ✅ |
| `:113-116` | C-4 제목 / 실제 대상 3종 / 목록 제외 / provision_isolation 중복 | ✅ |
| `:123` | `## 5. 완료된 과제 (Completed Tasks — 13건)` | ✅ |
| `:249` | 로드맵 P2-2 행 ("공허한 테스트 5건") | ✅ |
| `:260` | "정리(C 계열)를 P2 에 두는 이유" | ✅ |
| `:317` `:319-322` | §6.5-1 / §6.5-2 | ✅ |
| `:328` | §6.6 결론 ("총 12건") | ✅ |

**20/20 일치.** 오프셋 충돌은 발생하지 않습니다.

### 3.3 챌린지의 수치 주장은 사실과 다릅니다

챌린지 §Challenge 3 은 *"완료 과제 개수도 13건(문두 완료 **15건**)으로 갱신되었습니다"* 라고 적었습니다. 실측:

```
:6  - **완료된 과제**: **16건** (A-1, A-3, A-5, B-1, B-3, B-4, B-7, B-8, C-1, C-2,
                                 O-1, O-2, O-3, O-4-OrcOnboard,
                                 Herdr-0.8.0-Compat-SanitizeHash, P2-1-DelegateJobSafe-TrapFix)
```

쉼표 구분 항목 수 = **16개**, 선언값 = **16건**. 문두는 15가 아니라 **16**이며 목록과 자체 정합합니다. Rev.1 §6.1 의 "16건 → 17건" 이 맞습니다.

한편 챌린지가 같은 문장에서 언급한 *"C-3/C-4 섹션의 시작 위치가 `IMPROVEMENTS.md:107`"* 은 Rev.1 §6.1 이 이미 `:107` 로 적고 있는 값과 동일합니다 — 이 대목은 정정이 아니라 **Rev.1 의 확인**입니다.

### 3.4 그럼에도 수용하는 부분 — 편집 상호 간섭

챌린지가 우려한 "오프셋 충돌" 은 **HEAD 대비**로는 존재하지 않지만, **편집 도중**에는 실재합니다. §6.1 의 지시 11개가 **전부 같은 파일**을 대상으로 하고, 그중 3개가 줄 수를 바꿉니다:

- `:113-116` C-4 블록 **삭제** (−4줄) → 이후 모든 라인 상향 이동
- `:123` 직후 P2-2 완료 항목 **삽입** (+16줄) → 이후 모든 라인 하향 이동
- `:109-111` C-3 축소 (줄 수 변동 가능)

따라서 구현자가 `:5` → `:328` 순으로 위에서 아래로 편집하면 **`:249` 이후의 라인 번호가 전부 어긋납니다.** 이것이 챌린지가 감지한 실제 위험이며, 해법은 "HEAD 동기화" 가 아니라 **편집 순서 규정**입니다. §4.3 에 신설했습니다.

---

## 4. Rev.1 대비 변경 명세

> Rev.1(`07740cc7`)의 §1~§4(실측·경계·위험), §7.1 게이트, §8 비용·효과 정정, §9 예상 지적은 **전부 유효하며 변경 없습니다.** 아래는 델타만 기술합니다.

### 4.1 S5 개정 — 테스트 4건 제거 → **4건 제거 + 1건 추가 + 헤더 1줄**

```
tests/test_tier1_unit.py
  :31          "(7 Test Cases)" → "(5 Test Cases)"                        [Challenge 2]
  :52-88       test_create_isolation_lever
               test_create_isolation_env_prefix                            삭제
               test_create_isolation_cmd_args
  같은 자리     test_create_session_legacy_isolate_flags_noop               신설 [Challenge 1]

tests/test_tier2_component.py
  :99-107      test_comp_create_isolation_folder_setup                     삭제
```

패치 전체(`proposed/p2-2_c3a_c4.rev2.patch`): **5 files, +14 / −72**. Rev.1 은 +5/−72 였습니다.

### 4.2 §7.2 개정 — 수동 스모크 항목 정리

Rev.1 §7.2 의 3개 요구 중 **3번(`--isolate`/`--no-isolate` 각 1회 수동 실행)은 자동화되었으므로 삭제**합니다. 이것이 Challenge 1 의 핵심 성과입니다 — 수동 절차가 CI 게이트로 승격되었습니다.

구현자가 여전히 직접 해야 할 것:

1. **`pytest tests/ -q` 재실행** — 사본에는 `.mam/`(gitignore)이 없습니다. **256 passed** 재현 확인.
2. **`create_session.sh` 실경로 스모크 1회** (`--dry-run` 가능) — `ISOLATE` 제거가 파서 본류에 영향 없음을 실행으로 확인. (신규 테스트는 `-h` 조기 종료 경로까지만 봅니다 — §1.6)

### 4.3 §6.1 신설 — 편집 순서 (Challenge 3 수용)

`IMPROVEMENTS.md` 의 11개 지시는 **반드시 아래 순서(= 라인 번호 내림차순)로** 적용하십시오. 그러면 앞선 편집이 뒤이을 편집의 라인 번호를 바꾸지 않습니다.

| 순 | 대상 | 작업 | 줄 수 변화 |
|---|---|---|---|
| 1 | `:319-322` §6.5-2 | C-4 완료 표기. **`:320` 의 `lib.sh:57`→`:79` 를 `lib.sh:83`→`:105` 로 정정** | ±0 |
| 2 | `:317` §6.5-1 | C-3a 완료 표기. 총계 표현 있으면 "4건" | ±0 |
| 3 | `:260` | 근거 문장 교체 (Rev.1 §8) | ±0 |
| 4 | `:249` 로드맵 행 | "5건"→"4건", `(✅ 완료 — 256/256 PASS)` | ±0 |
| 5 | `:123` 직후 | §5 최상단에 P2-2 완료 항목 삽입 (§4.4) | **+16** |
| 6 | `:123` §5 제목 | 항목 수 갱신 | ±0 |
| 7 | `:113-116` C-4 블록 | §4 에서 **삭제** (내용은 5번에서 이미 §5 로 이관) | **−4** |
| 8 | `:109-111` C-3 | 제목을 `C-3b: isolation.root 소비자 처분 (보류 — A-4 M2)` 으로 축소, C-3a 줄 제거 | −1 내외 |
| 9 | `:107` §4 제목 | `Legacy Remnants — 3건` → **2건** | ±0 |
| 10 | `:6` | 완료 `16건` → **17건**, 목록에 `P2-2-C3a-C4-LegacyCleanup` 추가 | ±0 |
| 11 | `:5` | 미해결 `9건` → **8건**, `레거시 잔재 3건` → **2건** | ±0 |

**대안 (권장)**: 라인 번호 대신 **고유 문자열 앵커**로 편집하면 순서 제약이 사라집니다. 위 11개 지시는 모두 유일 문자열을 갖고 있습니다(예: `Legacy Remnants — 3건`, `공허한 테스트 5건`, `Completed Tasks — 13건`). 도구가 문자열 치환을 지원한다면 그쪽이 안전합니다.

> ⚠️ Rev.1 §6.3 은 "`:115`/`:320` 의 라인 번호를 정정" 하라고 했으나, **`:115` 는 7번에서 삭제되는 C-4 블록 안에 있습니다.** 따라서 정정 대상은 `:320` **하나**이며, `:115` 의 내용은 §5 로 이관될 때(§4.4 마지막 항목) 이미 올바른 `lib.sh:83-84 → :105` 로 적혀 나갑니다. Rev.2 에서 정정합니다.

### 4.4 §6.2 개정 — §5 완료 항목 (테스트 문구 수정)

Rev.1 초안에서 **두 번째 불릿만** 교체합니다.

```markdown
- 위 스텁의 빈 출력만 재확인하던 공허한 테스트 4건(`tests/test_tier1_unit.py` 3,
  `tests/test_tier2_component.py` 1)을 제거하고, 그 자리에 `--isolate`/`--no-isolate`
  레거시 no-op 플래그의 인자 파서 계약을 고정하는
  `test_create_session_legacy_isolate_flags_noop` 1건을 신설했습니다. 신규 테스트는
  분기 삭제·한쪽만 삭제·조용한 no-op 화·usage 문서 줄 삭제 4종 변이를 모두 검출함을
  변이 검사로 입증했습니다. `test_tier1_unit.py:31` 섹션 헤더도 `(5 Test Cases)` 로
  동기화했습니다.
```

마지막 불릿의 수치도 갱신합니다: **`전체 회귀 256/256 PASS (100%)` (259 → 256, 순감 3 = 제거 4 − 신설 1)**.

### 4.5 §6.4 개정 — `LOG.md`

주요 구현 목록의 테스트 줄을 교체하고 검증 수치를 갱신합니다.

```markdown
  - `tests/test_tier1_unit.py` / `tests/test_tier2_component.py`: 공허한 테스트 4건 제거 및
    `--isolate`/`--no-isolate` no-op 회귀 가드 1건 신설(변이 4종 검출 입증), 섹션 헤더 동기화.
- **검증**: `pytest tests/ -q` **256 passed (100%)**.
```

### 4.6 §3 미접촉 경계 — 한 줄 보강

Rev.1 §3 표의 `--isolate`/`--no-isolate` 행 사유를 다음으로 대체합니다.

> 레거시 호환 경고이자 **`create_session.sh:42-43` 에 정식 문서화된 옵션**. 제거하면 기존 호출자가 `unknown arg` 로 `exit 2`. **P2-2 이후로는 `test_create_session_legacy_isolate_flags_noop` 이 CI 게이트로 이를 고정한다.**

---

## 5. Rev.2 검증 결과

| # | 검증 | 기대 | 실측 |
|---|---|---|---|
| V1 | `bash -n lib.sh` / `create_session.sh` | rc=0 | ✅ (Rev.1 에서 확인, 해당 hunk 무변경) |
| V2 | `ast.parse(registry.py)` | rc=0 | ✅ (동상) |
| V3 | 신규 테스트 단독 실행 | pass | ✅ **1 passed, 0.09s call** |
| V4 | 변이 A (분기 2개 삭제) | FAIL | ✅ FAIL |
| V5 | 변이 B (한쪽만 삭제) | FAIL | ✅ FAIL |
| V6 | 변이 C (조용한 no-op) | FAIL | ✅ FAIL |
| V7 | 변이 D (usage 문서 줄 삭제) | FAIL | ✅ FAIL *(강화 후. 원안은 PASS)* |
| V8 | 변이 E (무변이 대조군) | PASS | ✅ PASS |
| V9 | 신규 테스트의 저장소 오염 | 0건 | ✅ `.mam` 미생성 |
| V10 | tier1 섹션 헤더 5개 정합 | 전부 일치 | ✅ 5/5 |
| V11 | 미사용화되는 헬퍼·임포트 | 없음 | ✅ `run_lib_func` 15회, `get_mqtt_common` 7회, `subprocess`/`shlex`/`hmac`/`hashlib` 전부 잔존 사용 |
| V12 | 수집 개수 | 259 → 256 | ✅ **256 collected** |
| V13 | `pytest tests/ -q` 전체 | 256 passed | ✅ **256 passed in 392.29s** |

### 5.1 전체 회귀 (Rev.2 사본)

```
256 passed in 392.29s (0:06:32)
```

원본 로그는 `proposed/pytest_rev2.log` 입니다. 참고로 Rev.1(255건) 은 376.08s 였습니다 — 차이 16s 는 신규 테스트 1건(0.09s)으로 설명되지 않는 **실행 간 편차**이며, Rev.1 §8 에서 이미 밝혔듯 이 스위트의 총 실행 시간은 P2-2 의 판단 근거가 아닙니다.

---

## 6. 검증 한계 (Rev.1 §10 갱신)

1. **실측은 `5e519e2` 로컬 클론에서 수행**. 실제 트리에서의 256 passed 는 **미확인** — §4.2-1 이 요구합니다.
2. **`create_session.sh` 본류 실행 스모크 미수행.** 신규 테스트는 `-h` 조기 종료 경로까지만 검증합니다(§1.6). §4.2-2 가 요구합니다.
3. **변이 검사는 `create_session.sh` 4종에 한정.** `lib.sh` 스텁 제거·`registry.py`·`_REAL_HERDR_PATH` 에는 변이 검사를 적용하지 않았습니다(제거 대상이라 고정할 계약이 없음 — Rev.1 §4.3).
4. **`_REAL_HERDR_PATH` 의 저장소 외부 소비자 미검색.** 확인 범위는 저장소 트리, 생성된 `.mam/shim/herdr`, `.agents/hooks/`, `~/.claude/settings.json` (Rev.1 §10-4 유지).
5. **`shellcheck` 미설치** — 정적 분석은 `bash -n` 까지.
6. **macOS · 직렬 실행**. Linux · `pytest-xdist` 병렬 미검증(xdist 미설치). 신규 테스트는 `mam_sandbox`(`tmp_path`) 안에서만 쓰기하므로 병렬 안전할 것으로 **판단**하나 실측은 아닙니다.
7. **챌린지 §Challenge 3 의 "15건" 반박은 `IMPROVEMENTS.md` 현행 파일 대조에 근거**합니다. 챌린저가 다른 시점의 파일을 봤을 가능성은 배제하지 못하나, HEAD 가 `5e519e2` 로 고정되어 있고 워킹 트리가 clean 이므로 두 에이전트가 본 파일은 동일해야 합니다.
8. 본 계획은 Planner 산출물이므로 **`IMPROVEMENTS.md` / `LOG.md` / 소스를 직접 수정하지 않았습니다.** §4 는 구현자가 적용할 명세입니다.
