- create_session.sh: add explicit case fallback for delegate_agent (R1) - lib_py.agents: add spawn-spec, resume-spec, exit-key argv CLI subcommands (R2) - resume_session.sh, stop_session.sh: replace python string interpolation with safe argv subcommands (R2) - stop_session.sh: dynamically iterate adapter.identity_cache_fields (R3) - verify_session.py, workspace_uuid.py: remove dead imports (R9/N4) - tests/test_a4_adapter_contract.py: add CLI bridge subcommand, clean-env PYTHONPATH safety, and fallback contract tests (R12/N1) - SKILL.md, docs, logs: synchronize IMPROVEMENTS.md, LOG.md, and resolve_session_id wording (R8/R10/R11) - promote verified peer review reports for cline (e7b9812b) and claude (31730364)
12 KiB
🔎 교차 코드 리뷰 리포트 (3차) — A-4 Phase 2 후속 수정분
- Job ID:
31730364 - Reviewer: claude (session:
herdr:canary-projects-multi-agent-mux-creator-claude) - Role: Reviewer (
MULTI_AGENT_RULES.md§1 — 저장소 파일 0건 변경) - 리뷰 대상:
b4821fa+7708d3a위의 작업 트리 미커밋 수정분 12파일 (직전 리뷰6360d5e4대응분) - 기준: HEAD
7708d3a
0. 요약
직전 리뷰(6360d5e4)의 유일한 차단 사유 N1 이 해소되었고, 실측으로 확인했습니다. 부수 지적 N3·N4 도 정리되었습니다. 1차(ae4f2ef3)의 차단 사유 R1·R2 는 이번에도 재현 프로브로 재검증했으며 그대로 유지됩니다.
이번 라운드의 검증은 뮤테이션 테스트로 수행했습니다 — 신규 가드 3종 각각에 대해 대응하는 생산 코드를 고의로 되돌린 뒤, 그 가드가 실제로 죽는지 격리 클론에서 확인했습니다. 결과:
| 가드 | 대응 뮤테이션 | 결과 |
|---|---|---|
test_cli_bridge_subcommands_and_quote_safety |
__main__.py 의 3개 서브커맨드 제거 |
FAIL ✅ 진짜 가드 |
test_wait_for_tui_ready_missing_tokens_diagnostic |
lib.sh 의 빈 토큰 가드 제거 |
FAIL ✅ 진짜 가드 |
test_delegate_agent_resolution_and_fallback |
create_session.sh 의 R1 수정을 원래 결함으로 되돌림 |
PASS ❌ 가드 아님 |
즉 N2 는 형태만 갖춰졌을 뿐 여전히 미해결입니다. 다만 이는 이미 올바른 생산 코드에 대한 회귀 가드 부재이지 동작 결함이 아니고, 직전 리뷰에서도 비차단으로 분류했던 항목이므로 판정은 유지합니다.
| # | 등급 | 요지 |
|---|---|---|
| N2 | 🟡 필수 후속 | test_delegate_agent_resolution_and_fallback 이 create_session.sh 를 실행하지 않고 테스트 안에 복사한 스니펫을 실행합니다. R1 수정을 완전히 되돌려도 전 스위트가 녹색 — 뮤테이션으로 증명 |
| N5 | ⚪ | _MAM_READY_TOKENS_CLAUDE 중복 존치 (3라운드 연속 비차단) |
| R6·R7 | ⚪ | 두 건의 동작 변경이 여전히 커밋 메시지·LOG.md 에 미기록 |
1. N1 — 해소 확인 ✅
test_cli_bridge_subcommands_and_quote_safety 가 env = os.environ.copy() + env["PYTHONPATH"] 를 구성해 3개 subprocess.run 전부에 env=env 를 넘기도록 수정되었습니다. test_facts_bridge_eval_contract:73-76 의 기존 선례를 정확히 따랐습니다.
실측 — 직전 라운드와 동일 조건에서 대조:
$ env -u PYTHONPATH .venv/bin/python -m pytest tests/test_a4_adapter_contract.py -q
직전: 1 failed, 11 passed (ModuleNotFoundError: No module named 'lib_py')
현재: 12 passed in 0.44s ✅
deploy/gitea-ci.yml 의 pytest tests/ -q 가 적색이 되던 원인이 제거되었습니다.
2. N3 · N4 — 해소 확인 ✅
- N3:
create_session.sh의 중복 화이트리스트가 제거되어 preflight:85하나만 남았습니다. (제가 1차 리포트에서 "검증이 없다"고 잘못 쓴 데 대응해 추가되었던 블록입니다.) - N4:
verify_session.py에서resolve_home참조가 0건이 되었습니다. 모듈 레벨 import 제거가 안전함도 확인했습니다 —from lib_py.verify_session import …전수 조사 결과resolve_home을 이 모듈에서 가져다 쓰는 곳은 없습니다.
죽은 import 재스캔 결과, 이번 리팩터가 만든 것은 전부 정리되었습니다.
| 파일 | 잔여 | 귀속 |
|---|---|---|
verify_session.py |
0건 ✅ | — |
workspace_uuid.py |
0건 ✅ | — |
atomic_yaml.py |
5건 | 리팩터 이전부터 존재 |
agents/__main__.py |
json 1건 |
리팩터 이전부터 존재 |
agents/base.py |
json·sqlite3·List 3건 |
리팩터 이전부터 존재 |
3. R1 · R2 — 재검증 유지 ✅
| 검사 | 결과 |
|---|---|
| R1: 브리지 사용 불가 시 위임 키 | claude→claude-code, agy→antigravity-cli, hermes→hermes-agent, cline→cline-agent (4/4) |
| R2: 1차에서 코드 실행에 성공했던 페이로드 재투입 | /bin/claude --dangerously-skip-permissions --session-id u1 — 실행 흔적 없음 |
bash -n (변경된 셸 5종) |
5/5 OK |
4. 🟡 N2 (필수 후속) — 위임 폴백 테스트가 자기 자신을 검사함
위치: tests/test_a4_adapter_contract.py:315-347
추가된 §2 블록은 주석에 Shell fallback resolution when MAM_DELEGATE_AGENT_KEY is unset (R1 fallback) 이라 적혀 있으나, 실행 대상이 create_session.sh 가 아니라 테스트 파일 안에 f-string 으로 복사해 둔 case 문입니다.
sh_snippet = f'''
AGENT="{agent}"
...
case "$AGENT" in
claude) delegate_agent="claude-code" ;; # ← 테스트가 스스로 써 넣은 코드
...
'''
res = subprocess.run(["bash", "-c", sh_snippet], ...)
assert res.stdout.strip() == expected_key
생산 코드를 한 줄도 읽지 않으므로, 단언하는 것은 "테스트가 방금 작성한 case 문이 작성된 대로 동작한다" 뿐입니다.
뮤테이션 증명
격리 클론(git clone --local --no-hardlinks)에 작업 트리 상태를 복사한 뒤, create_session.sh:249-259 의 R1 수정을 원래 결함 형태로 완전히 되돌렸습니다.
- delegate_agent="${MAM_DELEGATE_AGENT_KEY:-}"
- if [ -z "$delegate_agent" ]; then
- case "$AGENT" in
- claude) delegate_agent="claude-code" ;;
- ...
- fi
+ delegate_agent="${MAM_DELEGATE_AGENT_KEY:-antigravity-cli}" # ← 1차에서 차단했던 바로 그 결함
결과:
baseline (수정 상태) : 12 passed in 0.46s
mutant (결함 복원) : 12 passed in 0.46s ← 아무도 눈치채지 못함
즉 지금 R1 수정을 되돌리고 커밋해도 전 스위트가 녹색입니다. 1차에서 차단했던 "claude 세션의 위임 잡이 antigravity-cli 로 기록되는" 결함이 그대로 재유입될 수 있습니다.
직전 라운드보다 나빠진 점이 하나 있습니다. 이전에는 이 테스트가 단순 중복 단언이라 "가드가 없다"는 사실이 코드만 봐도 드러났지만, 지금은 R1 을 명시적으로 언급하는 주석과 셸 실행이 붙어 가드가 있는 것처럼 읽힙니다. 후속 작업자가 이를 근거로 안심할 여지가 생겼습니다.
권고
create_session.sh 를 실제로 실행하되 브리지만 실패하게 만드는 형태로 교체하십시오. 예:
def test_delegate_agent_fallback_in_create_session(tmp_path):
# PATH 앞단에 실패하는 python 스텁을 놓아 facts 브리지만 죽인다
...
res = subprocess.run(["bash", "-c",
f'cd {ws} && bash {create_sh} --workspace {ws} --agent claude '
f'--role creator --submit-job "x" --dry-run'], ...)
assert "claude-code" in res.stdout # antigravity-cli 가 아님
--dry-run 경로가 위임 블록에 도달하지 않는다면, 최소한 스크립트 본문에서 해당 case 블록을 추출해 실행하는 형태(파일을 읽어 sed/awk 로 잘라내 bash -c)로라도 생산 파일이 입력에 포함되어야 합니다.
5. ⚪ 잔여 (비차단, 판정 무관)
N5 — _MAM_READY_TOKENS_CLAUDE 중복 존치
lib.sh 에 여전히 2회 등장합니다(:63 정의, :1735 handle_startup_dialogs 소비). ClaudeAgentAdapter.ready_tokens 와 동일 문자열을 두 곳이 각자 보유하는 상태로, M7 이 없애려던 이중 진실원입니다. 1·2차에 이어 3라운드 연속 비차단으로 남깁니다 — 값이 갈라지기 전까지는 무해하나, 갈라지면 조용히 어긋납니다.
R6 · R7 — 동작 변경 미기록
- R6: purge 경로 키가
workspace_key()의realpath기준으로 전환 (심볼릭 링크 하위 워크스페이스에서 삭제 대상 파일이 달라짐). - R7:
verify_session_uuid가 미지 에이전트에 대해True→False로 fail-closed 전환.
둘 다 방향은 옳으나 커밋 메시지·LOG.md 어디에도 서술이 없습니다. 차단하지 않되, P3-1 커밋을 최종 확정할 때 한 줄씩 남기기를 권고합니다.
문서 — 3라운드 지적 전부 해소 상태 유지 ✅
IMPROVEMENTS.md 의 §2/§4/§5 카운트와 머리말 일치, C-3b 의 자기모순 항목 제거, 로드맵 P3-1/P3-3 완료 표기, LOG.md 의 ## 📌 1. 헤딩 복원, 격리 잔재 문구 3곳 교정 — 모두 유지되고 있습니다.
6. 검증 결과
| 항목 | 결과 |
|---|---|
전체 회귀 pytest tests/ -q |
262 passed in 381.58s (0:06:21) — 독립 재실행 확인 |
test_a4_adapter_contract.py (env -u PYTHONPATH) |
12 passed — N1 해소 (직전: 1 failed) |
| 뮤테이션 M1 — R1 수정 되돌림 | 12 passed (탐지 실패) → N2 |
뮤테이션 M2 — __main__.py 서브커맨드 3종 제거 |
1 failed ✅ 가드 유효 |
뮤테이션 M3 — wait_for_tui_ready 빈 토큰 가드 제거 |
1 failed ✅ 가드 유효 |
| R1 재현 (브리지 실패 시 위임 키) | 4/4 정상 |
| R2 재현 (코드 주입 페이로드) | 무력 |
bash -n (셸 5종) |
5/5 OK |
| 죽은 import (이번 리팩터 귀속분) | 0건 |
resolve_home 제거 안전성 |
외부 소비자 0건 확인 |
_MAM_READY_TOKENS_CLAUDE |
2회 존치 (N5) |
M3 이 41초 걸린 점도 기록해 둡니다 — 가드를 제거하면 함수가 30회 sleep 루프로 빠지며, 이는 Rev.2 계획서가 예측했던 "크래시가 아니라 30초 오탐 타임아웃" 거동과 정확히 일치합니다.
7. 한계
- macOS(darwin 25.5.0) 단일 환경. N1 해소는
env -u PYTHONPATH로 확인했을 뿐 실제 CI 러너 실행은 아닙니다. - 뮤테이션은 격리 클론에서만 수행했고, 각 뮤테이션 후 원본을 복원해 서로 간섭하지 않게 했습니다. 저장소 작업 트리는 리뷰 전후 동일(12 M + 1 ??)합니다.
shellcheck·pyflakes미설치 — 셸은bash -n, Python 미사용 import 는 자체 AST 스캔(보수적).- hermes 미설치로 해당 어댑터의
auth_ok/discover는 계약 테스트로만 확인. - R2 주입 프로브는 stderr 출력만 하는 비파괴 페이로드입니다.
8. 결론
3라운드에 걸친 차단 사유가 모두 해소되었습니다.
- R1(위임 키 조용한 오값) — 수정, 재현 검증 완료
- R2(Python 소스 보간 → 조용한 폴백 + 코드 주입) — argv 서브커맨드로 교체, 페이로드 무력화 확인
- N1(회귀 가드가 주변
PYTHONPATH에 의존해 CI 적색) — 수정, 깨끗한 환경에서 12/12 확인
부수 지적 N3·N4 도 정리되었고, 신규 가드 3종 중 2종은 뮤테이션으로 실제 방어력이 있음을 증명했습니다. 문서 동기화도 유지되고 있습니다. 어댑터 계층 자체는 1차 리뷰 때부터 견고했고 그대로입니다.
남은 N2 는 이미 올바른 코드에 대한 회귀 가드가 비어 있는 문제이지 동작 결함이 아니며, 직전 리뷰에서도 비차단으로 분류한 항목입니다. 지금 와서 차단 사유로 승격하는 것은 기준을 뒤로 옮기는 일이므로 그렇게 하지 않습니다. 다만 "가드가 있는 것처럼 보이는 가드"는 없는 것보다 위험할 수 있으므로 다음 커밋 전 필수 후속으로 명시합니다.
설계 변경 요소는 없습니다.
필수 후속: N2 권고: N5, R6·R7 기록
[VERDICT: PASS]