fix(cleanup): remove empty isolation stubs and dead symbols (P2-2 / C-3a / C-4)
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
# 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 는 구현자가 적용할 명세입니다.
|
||||
@@ -0,0 +1,191 @@
|
||||
# 📋 Cross-Code Review Report: P2-2 (C-3a + C-4 Legacy Code Cleanup)
|
||||
|
||||
- **Job ID**: `34b928df`
|
||||
- **Reviewer**: cline (session: herdr:canary-projects-multi-agent-mux-creator-cline)
|
||||
- **Role**: Reviewer
|
||||
- **Review Subject**: P2-2 implementation — C-3a (4 isolation stub removal) + C-4 (3 dead symbol removal)
|
||||
- **Base Commit**: `5e519e2` (HEAD)
|
||||
- **Working Tree**: 7 modified files (uncommitted)
|
||||
- **Report Path**: `.mam/jobs/34b928df/cline-reports/report-final.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
The P2-2 implementation is a **clean, surgical legacy code cleanup** that precisely matches the task specification. All 4 isolation stubs (C-3a) and all 3 dead symbols (C-4) were removed with zero collateral damage. The C-3b boundary (`isolation.root` consumers) was respected — no touched lines. A thoughtful regression guard test (`test_create_session_legacy_isolate_flags_noop`) was added to replace the 4 removed vacuous tests, pinning the `--isolate`/`--no-isolate` no-op contract against future arg-parser regressions.
|
||||
|
||||
**No lint, operability, or loss issues found.** Documentation (IMPROVEMENTS.md, LOG.md) updated with accurate counts and descriptions.
|
||||
|
||||
---
|
||||
|
||||
## 2. Scope — Files Changed (7 files, +46/-94 lines)
|
||||
|
||||
| File | Change | Lines |
|
||||
|---|---|---|
|
||||
| `.agents/skills/lib.sh` | Removed 4 stubs + `_REAL_HERDR_PATH` assignment/export; updated comment | 28 changed |
|
||||
| `.agents/skills/multi-agent-mux-create/scripts/create_session.sh` | Removed `ISOLATE=1` | 1 removed |
|
||||
| `.agents/skills/multi-agent-mux-delegate-job/scripts/registry.py` | Removed `TERMINAL_STATUSES` | 1 removed |
|
||||
| `tests/test_tier1_unit.py` | Removed 3 vacuous tests, added 1 regression guard, synced header | 47 changed |
|
||||
| `tests/test_tier2_component.py` | Removed 1 vacuous test | 10 removed |
|
||||
| `IMPROVEMENTS.md` | C-3a/C-4 completion, counts updated (9→8 open, 16→17 done) | 39 changed |
|
||||
| `LOG.md` | P2-2 session entry added | 14 added |
|
||||
|
||||
---
|
||||
|
||||
## 3. C-3a Verification — 4 Isolation Stub Removal
|
||||
|
||||
### 3.1 Stubs Removed ✅
|
||||
All 4 empty stubs removed from `lib.sh` (was at lines 1369-1385, now gone):
|
||||
- `provision_isolation()` — was `printf ''` (no-op)
|
||||
- `isolation_lever()` — was `echo "none"` for all agents (no consumer read the output)
|
||||
- `isolation_env_prefix()` — was `:` (true no-op)
|
||||
- `isolation_cmd_args()` — was `:` (true no-op)
|
||||
|
||||
**Orphan check**: `grep -rn` across `.agents/`, `deploy/`, `tests/` for all 4 function names returns **zero production-code references** (only historical reports in `.mam/` and the new explanatory comment in `lib.sh:1364-1368`). ✅
|
||||
|
||||
### 3.2 Comment Block Updated ✅
|
||||
The old "Stubbed isolation functions kept for backward compatibility" comment was replaced with an accurate removal record that explicitly names the C-3b boundary:
|
||||
|
||||
```
|
||||
# The backward-compat stubs (provision_isolation / isolation_lever /
|
||||
# isolation_env_prefix / isolation_cmd_args) were removed in P2-2 (C-3a);
|
||||
# they had zero production callers. The `isolation.root` row field is still
|
||||
# consumed (C-3b) — see verify_session_uuid / find_workspace_uuid /
|
||||
# mam_session_iso_root / stop_session.sh purge guard.
|
||||
```
|
||||
|
||||
All 4 referenced C-3b consumers confirmed present in live code:
|
||||
- `verify_session_uuid` — `lib.sh:1260` (via Python import) ✅
|
||||
- `find_workspace_uuid` — `lib.sh:1331` ✅
|
||||
- `mam_session_iso_root` — `lib.sh:1103` ✅
|
||||
- `stop_session.sh` purge guard — `stop_session.sh:62` (`--purge-conversation`) ✅
|
||||
|
||||
### 3.3 Tests Removed (4) ✅
|
||||
- `test_create_isolation_lever` (test_tier1_unit.py) — vacuous: asserted `isolation_lever` returns "none"
|
||||
- `test_create_isolation_env_prefix` (test_tier1_unit.py) — vacuous: asserted empty stdout
|
||||
- `test_create_isolation_cmd_args` (test_tier1_unit.py) — vacuous: asserted empty stdout
|
||||
- `test_comp_create_isolation_folder_setup` (test_tier2_component.py) — vacuous: asserted `provision_isolation` returns empty stdout
|
||||
|
||||
**Note on "5 tests" in brief**: The brief mentions "5 vacuous tests" but only 4 existed. The 5th was a non-existent test — the remaining `isolation` hits in `tests/` are all C-3b contract verifications (which must NOT be touched). This discrepancy was pre-acknowledged in the planner's Rev.2 document (§1.2). ✅
|
||||
|
||||
### 3.4 Regression Guard Added (1) ✅
|
||||
New test `test_create_session_legacy_isolate_flags_noop` replaces the 4 removed vacuous tests with a meaningful contract: `--isolate` and `--no-isolate` must remain accepted no-op flags (rc=0, stderr notice, present in usage help). This prevents future arg-parser refactors from silently breaking legacy callers.
|
||||
|
||||
**Test verified**: `pytest tests/test_tier1_unit.py::test_create_session_legacy_isolate_flags_noop` → **PASSED** (0.12s) ✅
|
||||
|
||||
### 3.5 Section Header Sync ✅
|
||||
`test_tier1_unit.py:31` header updated: `(7 Test Cases)` → `(5 Test Cases)`. Verified: 7 - 3 removed + 1 added = 5. ✅
|
||||
|
||||
---
|
||||
|
||||
## 4. C-4 Verification — 3 Dead Symbol Removal
|
||||
|
||||
### 4.1 `_REAL_HERDR_PATH` (lib.sh) ✅
|
||||
- **Removed**: Lines 126-127 (`_REAL_HERDR_PATH="$real_path"` + `export _REAL_HERDR_PATH`)
|
||||
- **Function invariant**: `_resolve_real_herdr_path()` (lib.sh:111-127) still returns the resolved path via **stdout** (`printf '%s\n' "$real_path"`) and **exit code** (`return 1` on not found). The removed global variable was a write-only side-effect — no consumer ever read `$_REAL_HERDR_PATH`.
|
||||
- **`has_real_herdr()`** (lib.sh:129-131) calls `_resolve_real_herdr_path >/dev/null 2>&1` — uses exit code only, not the variable. ✅
|
||||
- **Orphan check**: `grep -rn '_REAL_HERDR_PATH'` across `.agents/`, `deploy/`, `tests/` → zero production-code references (only historical reports). ✅
|
||||
- **`_` prefix**: Denotes private/internal symbol. External consumers outside repo not searched, but `_resolve_real_herdr_path` is the public contract, not the variable.
|
||||
|
||||
### 4.2 `TERMINAL_STATUSES` (registry.py) ✅
|
||||
- **Removed**: Line 38 (`TERMINAL_STATUSES = ("completed", "error", "cancelled")`)
|
||||
- **`__all__` check**: `registry.py:175-178` — `TERMINAL_STATUSES` is **NOT** in `__all__`. `from registry import *` contract is invariant. ✅
|
||||
- **`VALID_STATUSES`** (now line 38) — still present and used at lines 149-150. **Not touched**. ✅
|
||||
- **Orphan check**: `grep -rn 'TERMINAL_STATUSES'` in registry.py → not found (exit code 1). Zero references in production code. ✅
|
||||
|
||||
### 4.3 `ISOLATE` (create_session.sh) ✅
|
||||
- **Removed**: Line 57 (`ISOLATE=1`)
|
||||
- **`set -euo pipefail`** at line 20 — if any code referenced `$ISOLATE` after removal, the script would fail with "unbound variable". No such reference exists. ✅
|
||||
- **`--isolate`/`--no-isolate` arg parsing** (lines 70-71) — these are **separate no-op branches** that echo a notice to stderr and `shift`. They never set or read `$ISOLATE`. They remain untouched and functional. ✅
|
||||
- **Usage help** (lines 42-43) — `--isolate` and `--no-isolate` documented as legacy no-op flags. Still present. ✅
|
||||
- **Deploy scripts** (`deploy/install_mam.sh:326`, `deploy/install.sh:613`) — reference `--isolate` in example commands. Since `--isolate` is still accepted as a no-op, these examples still work correctly. ✅
|
||||
|
||||
### 4.4 `_HERDR_SHIM_DIR_PATTERN` NOT Touched ✅
|
||||
Confirmed: `_HERDR_SHIM_DIR_PATTERN` (lib.sh:83) and `_HERDR_SKILLS_BIN_PATTERN` (lib.sh:84) are **not in the diff**. Both are still defined and used at lib.sh:105 (`_is_shim_path`). ✅
|
||||
|
||||
## 5. Syntax & Static Analysis
|
||||
|
||||
| Check | Command | Result |
|
||||
|---|---|---|
|
||||
| Shell syntax (lib.sh) | `bash -n .agents/skills/lib.sh` | ✅ SYNTAX OK |
|
||||
| Shell syntax (create_session.sh) | `bash -n .../create_session.sh` | ✅ SYNTAX OK |
|
||||
| Python AST (registry.py) | `python3 -c "import ast; ast.parse(...)"` | ✅ AST OK |
|
||||
| `shellcheck` | Not installed in environment | ⚠️ Not available (same as prior jobs) |
|
||||
|
||||
---
|
||||
|
||||
## 6. Test Verification
|
||||
|
||||
| Check | Expected | Result |
|
||||
|---|---|---|
|
||||
| Collection count | 256 (259 → 256, net -3 = 4 removed - 1 added) | ✅ **256 tests collected** |
|
||||
| test_tier1_unit.py full | All pass | ✅ **27 passed in 6.22s** |
|
||||
| New test standalone | PASS | ✅ **1 passed in 0.12s** |
|
||||
| test_tier2_component.py collection | 25 (was 26, -1 removed) | ✅ **25 collected** |
|
||||
| test_tier2_component.py adjacent test | PASS | ✅ `test_comp_create_sqlite_tables_created` passed (12.67s) |
|
||||
| Full 256-test suite | 256 passed | ⚠️ Not run to completion — timeout in review environment (same limitation as prior jobs 143de35c, 120ffb08) |
|
||||
|
||||
---
|
||||
|
||||
## 7. Documentation Review (IMPROVEMENTS.md / LOG.md)
|
||||
|
||||
### 7.1 IMPROVEMENTS.md ✅
|
||||
- **Header counts**: Open tasks 9→8 (레거시 3→2), Completed 16→17. Arithmetic verified: 2+4+0+2=8 ✅
|
||||
- **Section 4 title**: "3건 → 2건" (C-3a completed, C-4 completed, C-3b + C-6 remain = 2) ✅
|
||||
- **Section 5 title**: "13건 → 14건" (P2-2 added) ✅
|
||||
- **New P2-2 section**: Accurately describes all changes including mutation-test verification of the new regression guard.
|
||||
- **Pre-existing discrepancy**: Header says 17 completed but Section 5 says 14 (gap of 3). This gap was pre-existing (was 16 vs 13 = 3) and is **not introduced by P2-2**. Both counts incremented by exactly +1.
|
||||
|
||||
### 7.2 LOG.md ✅
|
||||
- P2-2 entry added with implementation summary and "256 passed (100%)" verification claim.
|
||||
- Date updated: 2026-08-15 → 2026-08-16.
|
||||
- Previous P2-1 entry renumbered from "1)" to "2)".
|
||||
|
||||
---
|
||||
## 8. Lint / Operability / Loss Analysis
|
||||
|
||||
### 8.1 Lint ✅
|
||||
- No syntax errors in any modified file.
|
||||
- No unused imports/variables introduced (removals only made the code cleaner).
|
||||
- `run_lib_func` helper still used 15× in test_tier1_unit.py — not orphaned by test removals.
|
||||
- `subprocess` import in test_tier1_unit.py — still used by new test and other existing tests. ✅
|
||||
|
||||
### 8.2 Operability ✅
|
||||
- `_resolve_real_herdr_path()` return channel (stdout/rc) is invariant — `has_real_herdr()` and all callers unaffected.
|
||||
- `create_session.sh` arg parser unchanged — `--isolate`/`--no-isolate` still accepted as no-ops.
|
||||
- `registry.py` public API (`__all__`) unchanged — `VALID_STATUSES` retained.
|
||||
- No function signatures changed, no calling conventions altered.
|
||||
|
||||
### 8.3 Loss ✅
|
||||
- **No functionality lost**: The 4 stubs were empty/no-op with zero production callers. Removing them changes no runtime behavior.
|
||||
- **No test coverage lost**: The 4 removed tests verified empty output from empty functions — their removal is co-dependent with the code removal. The new regression guard test adds meaningful coverage.
|
||||
- **No backward compatibility lost**: `--isolate`/`--no-isolate` CLI flags still accepted (no-op with notice). External scripts using these flags (e.g., `deploy/install_mam.sh`, `deploy/install.sh`) still work.
|
||||
|
||||
---
|
||||
|
||||
## 9. Boundary Compliance
|
||||
|
||||
| Boundary | Required | Verified |
|
||||
|---|---|---|
|
||||
| C-3b (`isolation.root` consumers) | Do NOT touch | ✅ No diff lines in `verify_session_uuid`, `find_workspace_uuid`, `mam_session_iso_root`, `stop_session.sh` purge |
|
||||
| `_HERDR_SHIM_DIR_PATTERN` | Do NOT touch | ✅ Not in diff; still at lib.sh:83, used at :105 |
|
||||
| `_HERDR_SKILLS_BIN_PATTERN` | Do NOT touch | ✅ Not in diff; still at lib.sh:84, used at :105 |
|
||||
| `VALID_STATUSES` | Do NOT touch | ✅ Not in diff; still at registry.py:38, used at :149-150 |
|
||||
| `--isolate`/`--no-isolate` branches | Do NOT touch | ✅ Not in diff; still at create_session.sh:70-71 |
|
||||
|
||||
---
|
||||
|
||||
## 10. Limitations
|
||||
|
||||
1. **Full 256-test suite not run to completion** — times out in review environment (~6 min). Ran directly-relevant subsets: test_tier1_unit.py (27/27 pass), test_tier2_component.py collection (25) + adjacent test, new test standalone. Same limitation as prior review jobs.
|
||||
2. **`shellcheck` not installed** — static analysis limited to `bash -n` syntax check.
|
||||
3. **`_REAL_HERDR_PATH` external consumers** (outside repo) not searched — `_` prefix denotes private symbol; repo tree, generated shims, hooks, and `~/.claude/settings.json` all confirmed no references by prior planner analysis.
|
||||
4. **macOS / serial execution** — Linux / `pytest-xdist` parallel not verified (xdist not installed).
|
||||
|
||||
---
|
||||
|
||||
## 11. Conclusion
|
||||
|
||||
The P2-2 implementation is a textbook dead-code cleanup: every removed line traces directly to the task specification, no collateral lines were touched, C-3b boundaries were respected, and a meaningful regression guard was added to replace vacuous tests. The documentation updates are accurate and consistent. All syntax checks pass, all runnable test subsets pass, and the collection count (256) matches the documented expectation.
|
||||
|
||||
[VERDICT: PASS]
|
||||
+5
-23
@@ -123,8 +123,6 @@ _resolve_real_herdr_path() {
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
[ -n "$real_path" ] || return 1
|
||||
_REAL_HERDR_PATH="$real_path"
|
||||
export _REAL_HERDR_PATH
|
||||
printf '%s\n' "$real_path"
|
||||
}
|
||||
|
||||
@@ -1363,28 +1361,12 @@ capture_conversation_id() {
|
||||
# ~/.hermes, ~/.cline user configuration and credential stores.
|
||||
# 2. Process Isolation: each agent-workspace pair runs in its own herdr pane.
|
||||
# 3. Conversation Isolation: session UUIDs discriminate conversation history.
|
||||
#
|
||||
# Stubbed isolation functions kept for backward compatibility:
|
||||
# The backward-compat stubs (provision_isolation / isolation_lever /
|
||||
# isolation_env_prefix / isolation_cmd_args) were removed in P2-2 (C-3a);
|
||||
# they had zero production callers. The `isolation.root` row field is still
|
||||
# consumed (C-3b) — see verify_session_uuid / find_workspace_uuid /
|
||||
# mam_session_iso_root / stop_session.sh purge guard.
|
||||
# ---------------------------------------------------------------------------
|
||||
provision_isolation() {
|
||||
local agent="$1" root="$2"
|
||||
printf ''
|
||||
}
|
||||
|
||||
isolation_lever() {
|
||||
case "$1" in
|
||||
claude|agy|hermes|cline) echo "none" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
isolation_env_prefix() {
|
||||
:
|
||||
}
|
||||
|
||||
isolation_cmd_args() {
|
||||
:
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# is_already_stopped <session_name>
|
||||
|
||||
@@ -54,7 +54,6 @@ DRY_RUN=0
|
||||
HERDR_SERVER_OPT=""
|
||||
SUBMIT_JOB_PROMPT=""
|
||||
ONBOARD=1
|
||||
ISOLATE=1
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
|
||||
@@ -35,7 +35,6 @@ from mqtt_common import (
|
||||
|
||||
logger = logging.getLogger("delegate_job.registry")
|
||||
|
||||
TERMINAL_STATUSES = ("completed", "error", "cancelled")
|
||||
VALID_STATUSES = ("pending", "running", "completed", "error", "cancelled")
|
||||
|
||||
|
||||
|
||||
+20
-19
@@ -1,9 +1,9 @@
|
||||
# 🛠️ Multi-Agent Mux 종합 개선 및 미해결 과제 백로그 (`IMPROVEMENTS.md`)
|
||||
|
||||
- **최종 갱신일**: 2026-08-16 (P2-1/B-6 delegate_job_safe 임시 사본 제거, B-12 서브셸 트랩 락 누출 해결, Z-9 행위 테스트 4종 27/27 통과 및 전체 259/259 회귀 통과 반영)
|
||||
- **최종 갱신일**: 2026-08-16 (P2-2/C-3a/C-4 격리 빈 스텁 4종 및 미사용 심볼 3종 제거, --isolate no-op 회귀 가드 신설, 전체 256/256 회귀 통과 반영)
|
||||
- **통합 관리 대상**: 기존 `CODEBASE_REVIEW_REPORT.md` + `OPTIMIZATION.md`
|
||||
- **총 추적 미해결 과제**: **9건** (아키텍처 2건, 엣지케이스 4건, 오케스트레이션 0건, 레거시 잔재 3건)
|
||||
- **완료된 과제**: **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)
|
||||
- **총 추적 미해결 과제**: **8건** (아키텍처 2건, 엣지케이스 4건, 오케스트레이션 0건, 레거시 잔재 2건)
|
||||
- **완료된 과제**: **17건** (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, P2-2-C3a-C4-LegacyCleanup)
|
||||
|
||||
---
|
||||
|
||||
@@ -104,23 +104,24 @@
|
||||
|
||||
---
|
||||
|
||||
## 4. ⚪ 레거시 잔재 및 죽은 코드 (Legacy Remnants — 3건)
|
||||
## 4. ⚪ 레거시 잔재 및 죽은 코드 (Legacy Remnants — 2건)
|
||||
|
||||
### **C-3: 격리 관련 잔재** — ⚠️ **C-3a / C-3b 로 분리 필요 (§6.5 참조)**
|
||||
- **C-3a (즉시 실행 가능)**: `provision_isolation` / `isolation_lever` / `isolation_env_prefix` / `isolation_cmd_args` 4종 **빈 스텁**. 프로덕션 호출자 0건. 이를 고정하던 공허한 테스트 5건(`test_tier1_unit.py` 3, `test_tier2_component.py` 1 등)도 함께 제거 대상.
|
||||
- **C-3b (보류 — A-4 M2 결정 사항)**: `isolation.root` 행 필드 소비자(`verify_session_uuid` 의 `iso_root` 분기, `mam_session_iso_root`, `find_workspace_uuid` 격리 분기, `stop_session.sh:277` purge 가드). **b4a1d094 / 44062a63 에서 의도적으로 되살린 코드**이므로 지우면 그 수정이 회귀합니다.
|
||||
|
||||
### **C-4: 참조 0회 미사용 심볼** — ⚠️ **목록 정정됨 (7종 → 실질 3종)**
|
||||
- **실제 대상 3종**: `_REAL_HERDR_PATH`(대입·export 만), `TERMINAL_STATUSES`(`registry.py:38` 정의만), `ISOLATE`(`create_session.sh:57` 대입만).
|
||||
- **목록에서 제외**: `_HERDR_SHIM_DIR_PATTERN` 은 **사용 중**입니다(`lib.sh:57` 정의 → `lib.sh:79` 사용). 지우면 shim 경로 판정이 깨집니다. `local_herdr` 은 참조 0건으로 **이미 제거**되었습니다.
|
||||
- `provision_isolation` 은 **C-3a 와 중복**이므로 그쪽에서 함께 처리합니다.
|
||||
### **C-3b: `isolation.root` 소비자 처분 (보류 — A-4 M2 결정 사항)**
|
||||
- `isolation.root` 행 필드 소비자(`verify_session_uuid` 의 `iso_root` 분기, `mam_session_iso_root`, `find_workspace_uuid` 격리 분기, `stop_session.sh:277` purge 가드). **b4a1d094 / 44062a63 에서 의도적으로 되살린 코드**이므로 지우면 그 수정이 회귀합니다. (C-3a 빈 스텁 4종은 P2-2 에서 완료되었습니다.)
|
||||
|
||||
### **C-6: `stop_session.sh` 도움말 문서 구버전 표기**
|
||||
- 스크립트 도움말에는 `--mode soft|hard` 등이 서술되어 있으나 실제 옵션 파서는 `exit 2`로 거부합니다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 🎉 완료된 과제 (Completed Tasks — 13건)
|
||||
## 5. 🎉 완료된 과제 (Completed Tasks — 14건)
|
||||
|
||||
### **P2-2 (C-3a / C-4): 격리 빈 스텁 4종·공허한 테스트 4건·미사용 심볼 3종 제거** — ✅ 완료
|
||||
- `.agents/skills/lib.sh` 의 백워드 호환 빈 스텁 `provision_isolation` / `isolation_lever` / `isolation_env_prefix` / `isolation_cmd_args` 4종(프로덕션 호출자 0건)을 제거하고, 주석 블록에 C-3b(`isolation.root` 행 필드) 경계를 명시해 후속 정리 시 오삭제를 차단했습니다.
|
||||
- 위 스텁의 빈 출력만 재확인하던 공허한 테스트 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)` 로 동기화했습니다.
|
||||
- 참조 0회 미사용 심볼 3종을 제거했습니다: `_REAL_HERDR_PATH`(`lib.sh:126-127`, 대입+export만 — `_resolve_real_herdr_path` 의 stdout/rc 반환 채널은 불변임을 실측 확인), `TERMINAL_STATUSES`(`registry.py:38`, `__all__` 미포함으로 임포트 계약 불변), `ISOLATE`(`create_session.sh:57`, `set -u` 하 숨은 확장 불가능).
|
||||
- `_HERDR_SHIM_DIR_PATTERN` / `_HERDR_SKILLS_BIN_PATTERN`(`lib.sh:83-84` → `:105` 사용 중), `VALID_STATUSES`(`registry.py:150-151` 사용 중), `--isolate`/`--no-isolate` 레거시 호환 분기, C-3b 소비자 4곳은 계획대로 미접촉입니다.
|
||||
- 전체 회귀 **256/256 PASS (100%)** 로 입증했습니다 (259 → 256, 순감 3 = 제거 4 − 신설 1).
|
||||
|
||||
### **P2-1 (B-6 / B-12): `delegate_job_safe` 임시 사본 제거 및 서브셸 루프 락 조기 해제 차단 조치** — ✅ 완료
|
||||
- `.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh` 내 `delegate_job_safe` 가 `.agents/skills/...` 경로 내에 `.tmp` 사본을 생성하던 방식을 제거하고 원본 래퍼 스크립트를 인플레이스로 직접 실행(`bash "$orig_script" "$@"`)하도록 개선하여 버전 관리 트리 오염 및 rsync 배포 유출(B-6)을 완전히 해소했습니다.
|
||||
@@ -246,7 +247,7 @@
|
||||
| **P1-1** | **A-4 M0~M1** | `PYTHONPATH` 부트스트랩·배포/CI 등록·`own_key` 이관. B-8/B-10/C-3b 로직을 싸게 만듦 **(✅ 완료 — tests/test_a4_adapter_contract.py 3/3 PASS)** | 중 | B-7 |
|
||||
| **P1-2** | **B-8** | agy 주입 시 `return 0` 우회 제거 및 제출 검증 루프 이관 **(✅ 완료 — tests/test_b8_send_keys_verification.py 1/1 PASS)** | 소 | A-4 M0 |
|
||||
| **P2-1** | **B-6 / B-12** | 스킬 트리 내 임시 사본 및 서브셸 EXIT 트랩으로 인한 루프 락 조기 해제 차단 **(✅ 완료 — tests/test_o3_scoped_guard.py 27/27 PASS, commit b490713)** | 소 | — |
|
||||
| **P2-2** | **C-3a + C-4** | 빈 스텁 4종 + 이를 고정하던 **공허한 테스트 5건** + 죽은 심볼 3종 제거 (회귀 시간 단축 효과) | 소 | — |
|
||||
| **P2-2** | **C-3a + C-4** | 빈 스텁 4종 + 공허한 테스트 4건 + 죽은 심볼 3종 제거 및 `--isolate` no-op 회귀 가드 신설 **(✅ 완료 — tests/test_tier1_unit.py + test_tier2_component.py 256/256 PASS)** | 소 | — |
|
||||
| **P2-3** | **C-6** | 도움말 3줄 정정 | 극소 | — |
|
||||
| **P3-1** | **A-4 M2~M7** | 어댑터 본이관. 진행 중 **B-10 · C-3b 처분 결정** | 대 | P1-1 |
|
||||
| **P3-2** | **B-10** | tier-3 신원 캐시 존치/제거 결정 + PyYAML 의존 완화 | 중 | A-4 M2 |
|
||||
@@ -257,7 +258,7 @@
|
||||
|
||||
**A-2 과제의 후순위 배치 사유**: 사용자 지침에 따라 차후 자체 전용 MQTT 브로커 서빙 환경 구축 시점에 맞춰 진행하기 위해 **최하위(P5-1)**로 배치하였습니다.
|
||||
|
||||
**정리(C 계열)를 P2 에 두는 이유**: (a) C-3a 는 공허한 테스트 5건을 함께 제거해 이후 모든 전체 회귀를 단축하고, (b) C-4 는 **잘못 실행하면 버그를 만듭니다**. 방치할수록 누군가 "쉬운 정리"로 집어 들 확률이 올라갑니다.
|
||||
**정리(C 계열)를 P2 에 두는 이유**: (a) C-3a 는 죽은 코드를 고정하던 테스트를 함께 제거해 C-3a 를 실행 가능하게 만들고, (b) C-4 는 **잘못 실행하면 버그를 만듭니다**. 방치할수록 누군가 "쉬운 정리"로 집어 들 확률이 올라갑니다.
|
||||
|
||||
### 6.3 병렬 실행 — 파일 소유권 슬롯 (Rev.2 교체)
|
||||
|
||||
@@ -314,12 +315,12 @@ CHANGES_DIFF=$(
|
||||
|
||||
|
||||
1. **C-3 은 그대로 실행하면 회귀를 만듭니다.** 항목이 성격이 다른 둘을 묶고 있습니다.
|
||||
- **C-3a (즉시 실행 가능)**: `provision_isolation` / `isolation_lever` / `isolation_env_prefix` / `isolation_cmd_args` 4종 빈 스텁 — 프로덕션 호출자 0건. 이를 고정하던 `tests/test_tier1_unit.py` 3건 + `tests/test_tier2_component.py` 1건도 함께 제거 대상.
|
||||
- **C-3a (✅ 완료)**: `provision_isolation` / `isolation_lever` / `isolation_env_prefix` / `isolation_cmd_args` 4종 빈 스텁 및 이를 고정하던 공허한 테스트 4건을 제거하고 `--isolate`/`--no-isolate` no-op 회귀 가드로 대체했습니다.
|
||||
- **C-3b (보류 — A-4 M2 결정 사항)**: `isolation.root` 소비자(`lib.sh` `verify_session_uuid` 의 `iso_root` 분기, `mam_session_iso_root`, `find_workspace_uuid` 격리 분기, `stop_session.sh:277` purge 가드). **b4a1d094 / 44062a63 에서 방금 의도적으로 되살린 코드**이며, 지우면 그 수정이 되돌아갑니다.
|
||||
2. **C-4 의 "7종" 중 2종은 사실과 다릅니다.**
|
||||
- `_HERDR_SHIM_DIR_PATTERN` — **사용 중입니다** (`lib.sh:57` 정의, `lib.sh:79` 사용). 목록대로 지우면 shim 경로 판정이 깨집니다.
|
||||
2. **C-4 (✅ 완료)**:
|
||||
- `_HERDR_SHIM_DIR_PATTERN` — **사용 중입니다** (`lib.sh:83` 정의, `lib.sh:105` 사용). 목록대로 지우면 shim 경로 판정이 깨집니다.
|
||||
- `local_herdr` — 참조 0건, **이미 제거됨**.
|
||||
- 실제 대상은 `_REAL_HERDR_PATH`, `TERMINAL_STATUSES`, `ISOLATE` 3종이며 `provision_isolation` 은 C-3a 와 중복입니다.
|
||||
- 실제 대상 `_REAL_HERDR_PATH`, `TERMINAL_STATUSES`, `ISOLATE` 3종이 안전하게 제거되었습니다(`provision_isolation` 은 C-3a 에서 처리).
|
||||
3. **A-2 의 원인 표현 정정** — `verify_hmac` 구현 자체는 정상입니다(토큰이 있으면 `hmac.compare_digest` 로 검증). 원인은 **토큰이 아무 데서도 발급되지 않아 검증이 공허해지는 것** + 발행자가 워크스페이스 지문 토픽을 채택하지 않은 것입니다. 수정은 ① 발행자 토픽 교체 ② `reconcile.sh:237` 레거시 전역 구독 제거 ③ `verify_hmac` fail-closed + 토큰 발급 순입니다.
|
||||
4. **B-5 잔여분** — 폴백으로 감지는 정상화됐으나 `mount | grep -E "$mountpoint.*(nfs|...)"` 가 마운트포인트를 **이스케이프 없이 ERE 에 보간**합니다(경로의 `.` 이 임의 문자로 해석). 이것만 신규 항목(B-11)으로 분리 권고.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 📝 Multi-Agent Mux 작업 세션 기록 (`LOG.md`)
|
||||
|
||||
- **최종 기록일시**: 2026-08-15 10:45 (KST)
|
||||
- **최종 기록일시**: 2026-08-16 10:00 (KST)
|
||||
- **작업 저장소**: `tmpl/multi-agent-mux` (Branch: `main`)
|
||||
- **작업 상태**: 모든 작업 완료, 세션 안전 종료(stopped), 저장소 상태 Clean!
|
||||
|
||||
@@ -8,7 +8,17 @@
|
||||
|
||||
## 📌 1. 금일 작업 내용 요약
|
||||
|
||||
### 1) **P2-1 (B-6 / B-12): `delegate_job_safe` 임시 사본 제거 및 서브셸 루프 락 조기 해제 차단 조치** — **완료**
|
||||
### 1) **P2-2 (C-3a / C-4): 레거시 격리 스텁 및 미사용 심볼 제거** — **완료**
|
||||
- **배경**: 격리 구조가 Universal Global Config(Job `536a6625`)로 전환되며 남은 빈 스텁 4종과, 그 빈 출력만 재확인하던 공허한 테스트 4건, 그리고 참조 0회 심볼 3종 정리.
|
||||
- **주요 구현**:
|
||||
- [`.agents/skills/lib.sh`](file:///Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.agents/skills/lib.sh): 빈 스텁 4종(`provision_isolation`, `isolation_lever`, `isolation_env_prefix`, `isolation_cmd_args`) 및 `_REAL_HERDR_PATH` 대입·export 제거, 주석에 C-3b 경계 명시.
|
||||
- [`.agents/skills/multi-agent-mux-delegate-job/scripts/registry.py`](file:///Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.agents/skills/multi-agent-mux-delegate-job/scripts/registry.py): `TERMINAL_STATUSES` 제거.
|
||||
- [`.agents/skills/multi-agent-mux-create/scripts/create_session.sh`](file:///Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.agents/skills/multi-agent-mux-create/scripts/create_session.sh): `ISOLATE` 제거.
|
||||
- [`tests/test_tier1_unit.py`](file:///Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/tests/test_tier1_unit.py) / [`tests/test_tier2_component.py`](file:///Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/tests/test_tier2_component.py): 공허한 테스트 4건 제거 및 `--isolate`/`--no-isolate` no-op 회귀 가드 1건 신설(변이 4종 검출 입증), 섹션 헤더 동기화.
|
||||
- [`IMPROVEMENTS.md`](file:///Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/IMPROVEMENTS.md): C-4 완결 및 C-3a 완료 반영, C-3b 존속.
|
||||
- **검증**: `pytest tests/ -q` **256 passed (100%)**.
|
||||
|
||||
### 2) **P2-1 (B-6 / B-12): `delegate_job_safe` 임시 사본 제거 및 서브셸 루프 락 조기 해제 차단 조치** — **완료**
|
||||
- **배경**: `run_loop.sh::delegate_job_safe` 가 `.agents/skills/...` 내부에 `.tmp` 사본을 생성하여 트리 오염 및 배포 시 유출(B-6)되던 문제와, 명령 치환 서브셸 내의 `trap _mam_release_guard EXIT` 로 인해 첫 번째 잡 위임 시 루프 락 마커(`.mam/loop-guard-active`)가 조기 삭제되어 O-3 가드레일이 무력화되던 결함(**B-12 / D1**) 조치.
|
||||
- **주요 구현**:
|
||||
- [`.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh`](file:///Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh): `delegate_job_safe` 를 임시 사본 및 서브셸 트랩 없이 인플레이스로 직접 실행(`bash "$orig_script" "$@"`)하도록 개선하고 실패 시 진단 로깅 추가. 루프 기동 시 기존 잔여 `.tmp` 스윕 구문 추가.
|
||||
|
||||
@@ -28,7 +28,7 @@ def get_mqtt_common(mam_sandbox):
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# FEATURE 1: Create Session (7 Test Cases)
|
||||
# FEATURE 1: Create Session (5 Test Cases)
|
||||
# ==============================================================================
|
||||
|
||||
def test_create_derive_session_name_standard(mam_sandbox):
|
||||
@@ -49,43 +49,14 @@ def test_create_derive_session_name_weird_characters(mam_sandbox):
|
||||
assert res.returncode == 0
|
||||
assert res.stdout.strip() == "bc-d-ef-creator-hermes"
|
||||
|
||||
def test_create_isolation_lever(mam_sandbox):
|
||||
"""Test isolation_lever outputs for each supported agent."""
|
||||
agents = {
|
||||
"claude": "none",
|
||||
"cline": "none",
|
||||
"agy": "none",
|
||||
"hermes": "none",
|
||||
"unknown": ""
|
||||
}
|
||||
for agent, expected in agents.items():
|
||||
res = run_lib_func(mam_sandbox, "isolation_lever", agent)
|
||||
assert res.returncode == 0
|
||||
assert res.stdout.strip() == expected
|
||||
|
||||
def test_create_isolation_env_prefix(mam_sandbox):
|
||||
"""Test isolation_env_prefix format outputs."""
|
||||
res = run_lib_func(mam_sandbox, "isolation_env_prefix", "claude", "/tmp/iso")
|
||||
assert res.returncode == 0
|
||||
assert res.stdout == ""
|
||||
|
||||
res2 = run_lib_func(mam_sandbox, "isolation_env_prefix", "agy", "/tmp/iso")
|
||||
assert res2.returncode == 0
|
||||
assert res2.stdout == ""
|
||||
|
||||
res3 = run_lib_func(mam_sandbox, "isolation_env_prefix", "cline", "/tmp/iso")
|
||||
assert res3.returncode == 0
|
||||
assert res3.stdout == ""
|
||||
|
||||
def test_create_isolation_cmd_args(mam_sandbox):
|
||||
"""Test isolation_cmd_args format outputs."""
|
||||
res = run_lib_func(mam_sandbox, "isolation_cmd_args", "cline", "/tmp/iso")
|
||||
assert res.returncode == 0
|
||||
assert res.stdout == ""
|
||||
|
||||
res2 = run_lib_func(mam_sandbox, "isolation_cmd_args", "claude", "/tmp/iso")
|
||||
assert res2.returncode == 0
|
||||
assert res2.stdout == ""
|
||||
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"
|
||||
|
||||
def test_create_validate_env_key(mam_sandbox):
|
||||
"""Test _validate_env_key function with valid and blocked environment keys."""
|
||||
|
||||
@@ -96,16 +96,6 @@ d['herdr_sessions'] = [
|
||||
assert res.returncode != 0
|
||||
assert "Duplicate running conversation ID" in res.stderr
|
||||
|
||||
def test_comp_create_isolation_folder_setup(mam_sandbox):
|
||||
"""Verify that provision_isolation runs cleanly as a stub for global config isolation."""
|
||||
lib_path = mam_sandbox / ".agents" / "skills" / "lib.sh"
|
||||
iso_root = mam_sandbox / "iso_home_test"
|
||||
|
||||
cmd_str = f"source {lib_path} && provision_isolation claude {iso_root}"
|
||||
res = subprocess.run(["bash", "-c", cmd_str], capture_output=True, text=True)
|
||||
assert res.returncode == 0
|
||||
assert res.stdout == ""
|
||||
|
||||
def test_comp_create_sqlite_tables_created(mam_sandbox, mock_herdr, mock_agents):
|
||||
"""Verify that tables exist and contain records after a full create_session.sh run."""
|
||||
script_path = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-create" / "scripts" / "create_session.sh"
|
||||
|
||||
Reference in New Issue
Block a user