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

27 KiB
Raw Blame History

멀티에이전트 추상화 계층 감사 및 정비 계획서 — Rev.2

  • job_id: 3aee63cf (Rev.1 = a4589a4b)
  • 역할: Planner
  • 반영한 이의제기: 94b33591 (agy, herdr:agy-creator-01) — [VERDICT: PASS WITH CHALLENGE]
  • 대상: lib.sh, resolve_session_id.sh, resume_session.sh, create_session.sh, stop_session.sh, delegate_job 레지스트리
  • 실측 하네스:
    • .mam/jobs/a4589a4b/claude-reports/proposed/probe_agent_abstraction.sh (Rev.1, 유효)
    • .mam/jobs/3aee63cf/claude-reports/proposed/probe_adoption_guard.sh (Rev.2 신규, 실행·검증 완료)
  • 저장소 변경: 없음 (Planner 는 코드를 수정하지 않음 — MULTI_AGENT_RULES.md §1)

0. Rev.1 대비 변경 요약

agy 의 이의제기는 Rev.1 의 W8 이 reconcile.sh 입양 루프를 다른 7개 호출부와 동일하게 취급한 것을 정확히 짚었다. 입양 루프는 "이 세션은 무슨 에이전트인가"를 묻는 자리가 아니라 "이 세션을 MAM 이 관리해야 하는가"를 묻는 자리이며, 두 질문은 다른 함수로 답해야 한다. 이 지적을 수용해 계획을 수정했다.

이의제기 항목 판정 Rev.2 반영
§1 입양 루프 오입양 (Primary) [ADJUDICATION: SUSTAINED] (메커니즘 서술 일부 정정) W8 을 W8a/W8b/W8c 로 분할, 전용 소유권 가드 도입
§1.3 입양 entry 의 role 키 누락 [ADJUDICATION: SUSTAINED] — 실측 확인 W8c 신설
§2.1 derive_session_name-creator- 하드코딩 [ADJUDICATION: SUSTAINED] — agy 가 말한 것보다 심각 W9b 신설 + role 권위 재정의
§2.2 shim 중복이 단일 소스를 훼손 [ADJUDICATION: SUSTAINED] W6 을 "생성 시 코드 주입"으로 변경
§3.1 권고: 접미사 일치 세션만 입양 [ADJUDICATION: OVERRULED] — 실측 반증 채택하지 않음, 대안 제시 (§1.2)

핵심 정정 1건: agy 가 권고한 remedy(§3.1 "Name Suffix 가 일치하는 세션만 입양")를 그대로 적용하면, 지금 이 순간 살아 있는 agy-creator-01 — 이의제기를 작성한 agy 자신의 세션 — 이 영구히 입양 대상에서 제외된다. 실측으로 확인했다(§1.2).


1. 이의제기 판정

1.1 Primary Challenge — 입양 오염: SUSTAINED (메커니즘 2곳 정정)

agy 의 우려는 성립한다. 근거를 실측했다.

성립 근거 ①: 후보 풀에 사용자 개인 서버가 포함된다. reconcile.sh:369unique_servers = {'default'} 로 시작한다. MAM 세션은 HERDR_SESSION_NAME=<workspace-slug> 서버를 쓰지만, default 서버는 무조건 함께 스캔된다. 즉 사용자가 개인적으로 띄운 herdr 세션이 후보 풀에 들어온다. 현재 default 서버의 실제 상태:

agy-creator-01
canary-projects-multi-agent-mux-creator-claude
canary-projects-multi-agent-mux-creator-cline

성립 근거 ②: 오늘 이것을 막는 유일한 장치가 바로 W8 이 교체하려던 이름 화이트리스트다. reconcile.sh:494-503else: continue 가 제거되고 mam_resolve_agent() 의 우선순위 ④(pane.cmd 일치)가 그 자리에 들어가면, 가드가 사라진 채 판정만 남는다. agy 의 지적이 정확한 지점이다.


정정 ①: agy 의 메커니즘 서술은 cwd 봉쇄 가드를 누락했다.

이름 화이트리스트 다음(reconcile.sh:508-510)에 이미 이런 가드가 있다:

pane_cwd_abs = os.path.realpath(pm['cwd'])
ws_root_abs  = os.path.realpath(workspace_root)
if not pane_cwd_abs or not (pane_cwd_abs == ws_root_abs
                            or pane_cwd_abs.startswith(ws_root_abs + os.sep)):
    continue

따라서 agy 가 예로 든 my-dev-session, test-pane, build-worker워크스페이스 밖에서 돌고 있다면 이름 가드를 없애도 여전히 입양되지 않는다. 실제 노출 범위는 "무관한 외부 herdr 세션 일반"이 아니라 "MAM 워크스페이스 디렉터리 안에서 도는 사용자 임의 세션" 으로 한정된다.

다만 이 정정이 우려를 약화시키지는 않는다 — 오히려 가장 흔한 경우가 정확히 그것이다. 개발자가 자기 프로젝트 디렉터리(=MAM 워크스페이스)에서 개인용 claude 를 herdr 로 하나 띄우는 것은 지극히 자연스럽다. 그래서 이 우려는 유효하다.

정정 ②: status: terminated 오염 서술. agy 는 "입양된 외부 세션이 종료 시 status: terminated 로 오염 데이터로 남는다"고 했다. 이는 drift A(reconcile.sh:463-480)의 정상 동작이며 입양된 모든 세션에 동일하게 적용된다. 오염의 본질은 terminated 상태 자체가 아니라 애초에 입양되지 말았어야 할 행이 YAML 에 생긴다는 것이다. 결론은 같지만 원인 귀속을 바로잡아 둔다.

1.2 agy 의 권고(§3.1) — 접미사 전용 입양: OVERRULED

agy 는 "Priority ④ 를 오버라이드하여 Name Suffix 가 일치하는 세션만 입양"할 것을 권고했다. 이 remedy 는 채택할 수 없다. 실측 반증:

LIVE SESSION                                     SUFFIX   PANE_CMD   PANE_CWD
agy-creator-01                                   REJECT   agy        …/canary_projects/multi-agent-mux
canary-projects-multi-agent-mux-creator-claude   MATCH    claude     …/canary_projects/multi-agent-mux
canary-projects-multi-agent-mux-creator-cline    MATCH    cline      …/canary_projects/multi-agent-mux

agy-creator-01-{creator,planner,reviewer}-{claude,agy,hermes,cline} 접미사 규칙에 일치하지 않는다 — 접미사가 -creator-01 이고 01 은 에이전트가 아니다. 그런데 이 세션은:

  • agy 를 워크스페이스 루트에서 실행 중인
  • .mam/agent-sessions.yamlrole: creator, status: running 으로 정식 등록된
  • 이 이의제기를 작성한 바로 그 MAM 세션이다

agy 의 remedy 를 적용하면, 이 행이 어떤 이유로든 YAML 에서 빠졌을 때(수동 편집, 손상 복구, .bak 롤백) reconcile 이 영원히 되찾지 못한다. 오탐(false positive)을 막으려다 오탈락(false negative)을 만드는 교환이며, 후자가 더 위험하다 — 오탐은 YAML 에 행 하나가 더 생기는 것이고, 오탈락은 살아 있는 에이전트가 오케스트레이션에서 사라지는 것이다.

근본 문제는 "어느 이름 규칙을 쓰느냐"가 아니라 "이름을 소유권 신호로 쓰는 것" 자체다. 이름은 사용자가 --session 으로 임의 지정할 수 있고(그래서 agy-creator-01 이 존재한다), 반대로 사용자의 개인 세션이 우연히 MAM 규칙과 같은 이름을 가질 수도 있다. 이름은 소유권의 증거가 아니다.

대안 (W8a): 적극적 소유권 마커. herdr 은 이미 페인 프로세스에 env 를 주입하고 있고, 그것을 되읽을 수 있다. 실측:

$ ps eww -p 7643 | tr ' ' '\n' | grep -E '^(HERDR|MAM)'
HERDR_ENV=1
HERDR_PANE_ID=wP:p2
HERDR_SESSION=multi-agent-mux
HERDR_SESSION_NAME=multi-agent-mux
HERDR_SOCKET_PATH=/Users/godopu16/.config/herdr/sessions/multi-agent-mux/herdr.sock
HERDR_TAB_ID=wP:t1
HERDR_WORKSPACE_ID=wP

그리고 herdr agent start--env KEY=VALUE 를 지원한다(job f3b10c00 에서 CLI 계약 실측). 따라서 생성 시점에 MAM 이 자기 소유를 명시적으로 각인하고, 입양 시 그것을 되읽으면 된다:

생성: herdr agent start … --env MAM_MANAGED=<workspace_root_realpath> …
입양: ps eww -p <pane_pid>  (Linux: /proc/<pid>/environ) 에서 MAM_MANAGED 확인

이 신호는 이름과 무관하므로 agy-creator-01 도 정상 입양되고, 사용자의 개인 세션은 마커가 없으므로 입양되지 않는다. 오탐과 오탈락을 동시에 없앤다.

단, 마커는 도입 이후 생성된 세션에만 존재한다. 따라서 기존 세션을 위한 3단 판정으로 설계한다:

단계 조건 판정
1 MAM_MANAGED == 이 워크스페이스 입양 (권위)
2 마커 없음 + 이름 접미사 일치 + cwd 봉쇄 통과 입양 (레거시 호환)
3 그 외 입양 안 함 (Fail-Closed)

2단계가 오늘의 동작과 정확히 같으므로 회귀 위험이 없고, 1단계가 agy-creator-01 같은 비규격 이름을 구제한다. pane.cmd 일치(우선순위 ④)는 입양 경로에서 완전히 배제한다 — 여기서는 agy 의 판단이 옳다.

1.3 role 키 누락: SUSTAINED

실측 확인. 입양 entry 의 키 목록:

name, status, herdr_session_created_at, herdr_session_epoch, herdr_session,
pane{index,pid,cmd,cmd_full,cwd}, start_command, attach_command, kill_command,
last_visible_status, last_visible_note
→ role 키 존재: NO

agy 의 지적대로다. 다만 해결 방법은 agy 가 제안한 "세션 이름에서 role 을 추출"이 아니다 — 그 이유는 §1.4 에서.

1.4 derive_session_name-creator- 하드코딩: SUSTAINED, 그리고 agy 가 말한 것보다 심각

lib.sh:992printf '%s-creator-%s' 로 하드코딩한다는 지적은 사실이다. 그런데 이 문제는 "앞으로 planner 세션 이름이 부정확해진다"에 그치지 않는다. 이미 깨져 있다. 이 워크스페이스의 현재 레지스트리:

canary-projects-multi-agent-mux-creator-claude   name_role=creator   row_role=planner    <== 충돌
canary-projects-multi-agent-mux-creator-cline    name_role=creator   row_role=reviewer   <== 충돌
agy-creator-01                                   name_role=(없음)     row_role=creator    <== 이름에 role 없음

이름/row 충돌 : 2
이름에 role 없음: 1

3개 세션 전부가 이름으로 role 을 알아낼 수 없는 상태다. 이름이 -creator- 라고 말하는 두 세션의 실제 role 은 plannerreviewer 다. 즉 지금 이 감사를 수행 중인 세션(planner)과 리뷰를 수행 중인 세션(reviewer) 둘 다 이름이 거짓말을 하고 있다.

그래서 결론은 agy 의 방향과 부분적으로 다르다:

  • 수용: derive_session_name 에 role 인자를 추가한다 (W9b). 앞으로 만들어질 이름은 정확해진다.
  • 반대: 그것을 role 판정의 근거로 삼아서는 안 된다. 위 3행이 보여주듯 기존 이름은 role 을 담고 있지 않으며, 이름을 고쳐도 이미 존재하는 세션의 이름은 바뀌지 않는다(herdr 세션명·~/.local/bin/<session> 래퍼·디스크 아티팩트가 모두 이름에 묶여 있어 개명이 불가능하다).

따라서 role 의 권위는 레지스트리 row (s['role']) 로 고정한다. 이름 접미사는 row 가 없을 때의 최후 폴백일 뿐이다. 입양 시 role 을 채우는 방법도 이 원칙을 따른다 — 이름에서 뽑는 것이 아니라, 마커/폴백 판정 결과에 따라 명시적으로 부여하고 알 수 없으면 'unknown' 을 기록한다(§W8c).

1.5 shim 중복: SUSTAINED — 단, 해법은 "중복 제거"가 아니라 "중복 생성"

agy 의 지적은 타당하다. 그러나 Rev.1 의 C2 제약(shim 은 PYTHONPATH 없이 동작해야 하므로 lib_py 를 import 할 수 없다)은 job 7132d954 에서 실측한 사실이라 철회할 수 없다.

해법은 shim 이 손으로 유지되는 사본이 아니라 생성물이 되게 하는 것이다. 실측한 사실:

  • _init_herdr_isolation() 은 lib.sh 를 source 할 때마다 무조건 shim 을 재생성한다 (존재 여부 가드 if [ -f ] 가 0개, mv -f "$tmp_file" "$wrapper_dir/herdr" 로 원자 교체)
  • shim 본문은 cat <<'EOF'인용된 heredoc (lib.sh:118 ~ 795)

따라서 에이전트 표를 생성 시점에 주입하면 단일 소스가 유지된다. 다만 heredoc 의 인용을 풀어서는 안 된다 — 678줄 안의 모든 $ 가 전개되어 shim 이 파괴된다. 주입은 (a) 인용 heredoc 뒤에 두 번째 비인용 heredoc 을 append 하거나, (b) 플레이스홀더 한 줄을 생성 후 치환하는 방식이어야 한다. (a) 를 권장한다 — 치환은 실패해도 조용하다.

그리고 표가 어긋나면 실패하는 테스트를 붙인다(W6b): lib.sh 를 source 해 shim 을 재생성한 뒤, shim 이 아는 에이전트 집합과 단일 소스의 집합을 비교해 불일치 시 실패. 이것이 agy 가 우려한 "신규 에이전트 추가 시 shim 동시 갱신 누락"을 기계적으로 잡는다.


2. 감사 결과 (Rev.1 에서 변경 없음 — 요약)

Rev.1 의 §1–§4 실측 결과는 이의제기의 영향을 받지 않았으므로 그대로 유효하다. 전문은 .mam/jobs/a4589a4b/claude-reports/report-final.md 를 참조하고, 여기서는 계획 수립에 필요한 결론만 옮긴다.

8개 유도 구현의 불일치 (probe_agent_abstraction.sh 로 재현 가능):

SESSION NAME                     TRUTH   | A:shim  B:stop  C:upd   D:loop  E:stat  G:adopt H:rowag
proj-planner-claude              claude  | claude  ERR     claude  claude  ?       SKIP    claude   <== MISMATCH
proj-reviewer-cline              cline   | cline   ERR     cline   cline   ?       SKIP    cline    <== MISMATCH
hermes-sdk-creator-cline         cline   | hermes  cline   cline   cline   ?       cline   cline    <== MISMATCH
my-cline-fork-creator-hermes     hermes  | hermes  hermes  hermes  cline   ?       hermes  hermes   <== MISMATCH

결함 목록 (F1F9, 상세는 Rev.1 §4):

ID 결함 등급
F1 cline/hermes verify_session_uuid 에 워크스페이스 스코핑 없음 → P0-C 위반 P0
F2 kind 오탐이 duplicate-path strip 무력화 → b0c2c08 회귀 P0
F3 역할 커버리지 불일치 (stop=거절 / reconcile=무시 / tier-1=우회) P1
F4 status.sh 가 hermes/cline resume 판정 불가 P1
F5 wrapper 경로에서 실행되지 않은 커맨드를 레지스트리에 기록 P1
F6 orc_onboard 의 hermes argv 파싱 누락 P1
F7 send_keys_safe 제출 경로가 세션 이름 부분문자열로 결정됨 P2
F8 TUI ready 토큰 변별력 부족 P2
F9 create_session.sh--agent 검증 시점이 늦음 P2

이의제기가 추가한 결함:

ID 결함 등급
F10 reconcile.sh 입양 판정이 이름에만 의존 — 소유권의 적극적 증거가 없음 P1
F11 입양 entryrole 키 누락 → 스키마 불완전 행 유입 P1
F12 derive_session_name 이 role 을 반영하지 않아 현재 3개 세션 전부 이름/role 불일치 P1

3. 구현 계획 (Rev.2)

설계 원칙 (Rev.1 에서 1개 추가):

  1. 에이전트별 지식을 단일 레지스트리로 모으고, 8개 유도 구현을 하나의 함수로 대체한다.
  2. 기존 소비자 계약을 깨지 않도록 파사드를 유지한다.
  3. (신규) "이 세션은 무슨 에이전트인가"와 "이 세션을 MAM 이 관리하는가"는 서로 다른 질문이므로 서로 다른 함수로 답한다. — 이의제기 §1 수용

Phase 0 — 선행 조건 (해소됨)

W0. Rev.1 이 블로킹으로 지목했던 미커밋 워킹트리는 커밋 657a749 로 확정되었다. git status --porcelaingit diff HEAD --stat 모두 비어 있음을 확인했고, 이 계획의 모든 라인 참조는 git show HEAD: 로 대조 검증했다. 블로킹 선행 조건 없음.

Phase 1 — P0 수정 (독립 실행 가능, 지금 착수 가능)

Phase 1 은 lib_py 에도 이의제기 반영분에도 의존하지 않는다.

W1. cline verify_session_uuid 에 cwd 검사 추가 (lib.sh:1517-1531) 이미 읽고 있는 sdata 에서 cwd(없으면 workspace_root)를 꺼내 workspace_key() 로 비교, 불일치 시 False. claude 분기(lib.sh:1468)와 동일 형태.

W2. hermes verify_session_uuid 에 cwd 검사 추가 (lib.sh:1502-1516) SELECT 1 FROM sessions WHERE id=?SELECT cwd FROM sessions WHERE id=?workspace_key 비교. 선행 확인 필요(§5.3).

W3. W1/W2 회귀 테스트 — 먼저 작성해 실패를 확인할 것 test_workspace_scope.py 에 4개 에이전트 전부에 대해 "다른 워크스페이스의 id 는 반환되지 않는다"를 추가한다. 현재 이 파일의 비-claude 참조는 0 이며, 그것이 F1 이 발견되지 않은 이유다.

W4. kind 판정을 접미사 우선으로 교정 (lib.sh:267-280) -{creator,planner,reviewer}-<agent> 접미사를 먼저 확인, 실패 시에만 현행 부분문자열 캐스케이드. 기본값 cline 제거 — 판정 실패는 "" 로 두고 strip 을 건너뛴다(오탐 strip 보다 안전).

W5. W4 회귀 테스트test_herdr_shim_contract.pyhermes-sdk-creator-cline 계열 이름 추가.

Phase 2 — 단일 소스 도입 (이의제기 §2.2 반영)

W6a. 에이전트 능력 레지스트리 정의 — 본체는 lib_py/agents.py (job 7132d954 의 패키지 위에).

W6b. shim 표를 생성물로 만든다이의제기 §2.2 반영, Rev.1 에서 변경 Rev.1 은 "shim 이 쓰는 최소 부분만 lib.sh 에 중복"이라 했으나, 손으로 유지되는 중복은 agy 의 지적대로 갱신 누락에 취약하다. _init_herdr_isolation() 이 shim 을 매번 무조건 재생성한다는 실측에 근거해, 표를 생성 시점에 주입한다:

  • 인용 heredoc(lib.sh:118-795)은 그대로 둔다 — 인용을 풀면 678줄의 모든 $ 가 전개되어 shim 이 파괴된다
  • 표는 인용 heredoc 뒤에 두 번째 비인용 heredoc 으로 append
  • 표 불일치 시 실패하는 테스트를 함께 추가 (shim 재생성 → shim 이 아는 에이전트 집합 == 단일 소스 집합)

W7. mam_resolve_agent() — "무슨 에이전트인가"에만 답한다 우선순위: ① 명시 --agent → ② 레지스트리 row 의 agent → ③ 세션 이름 접미사 -{role}-<agent> → ④ pane.cmd 정확 일치 → ⑤ 실패는 실패로 반환(암묵 기본값 없음). 이 함수는 소유권을 판정하지 않는다. 우선순위 ④ 는 "이미 MAM 이 관리한다고 확정된 세션"에 대해서만 유효하다. 입양 경로는 W8a 를 쓴다.

W8. 호출부 교체 — 입양 경로를 분리이의제기 §1 반영, Rev.1 W8 에서 분할

  • W8a. reconcile.sh 입양 루프 전용 판정 (reconcile.sh:494-510) mam_resolve_agent() 를 쓰지 않는다. 대신 §1.2 의 3단 판정:

    1. MAM_MANAGED == realpath(workspace_root) (pane pid 의 env 에서 읽음) → 입양
    2. 마커 없음 + 이름 접미사 일치 + 기존 cwd 봉쇄 통과 → 입양 (레거시 호환, 오늘의 동작과 동일)
    3. 그 외 → 입양 안 함 (Fail-Closed)

    pane.cmd 일치는 이 경로에서 완전히 배제한다. env 읽기는 macOS ps eww -p <pid> / Linux /proc/<pid>/environ 두 경로를 모두 구현하고, 어느 쪽도 불가하면 2단계로 강등한다(마커 없음과 동일 취급).

  • W8b. 생성 시 소유권 마커 각인 create_session.sh 의 herdr 기동 경로에 --env MAM_MANAGED=<workspace_root_realpath> 를 추가한다. 실측으로 herdr agent start --env KEY=VALUE 지원과 ps eww 회수 가능성을 모두 확인했다.

  • W8c. 입양 시 role 부여이의제기 §1.3 반영 entry['role'] 을 추가한다. 값의 출처는 §1.4 의 원칙을 따른다: 이름 접미사에서 뽑지 않고, 1단계 마커 입양이면 마커에 함께 실은 role 을, 2단계 레거시 입양이면 이름 접미사의 role 을, 알 수 없으면 'unknown' 을 기록한다. role 을 절대 추측해 'creator' 로 채우지 않는다 — 현재 레지스트리에서 이름이 creator 라고 말하는 두 세션의 실제 role 이 planner/reviewer 이기 때문이다.

  • W8d. 나머지 6개 호출부를 mam_resolve_agent() 로 교체 A(lib.sh:267), B(stop_session.sh:94), C(update_yaml_resumed.sh:46), D(run_loop.sh:254), E(status.sh:55,151), H(reconcile.sh:568). F(send_keys_safe)는 W12.

Phase 3 — P1 수정

W9a. 역할 커버리지 통일 — F3. -{creator,planner,reviewer}- 3개 역할을 전 경로에서 인정. find_workspace_uuid tier-1 의 endswith('-creator-…') 게이트(lib.sh:1707-1721)도 함께 푼다.

W9b. derive_session_name 에 role 인자 추가 — F12, 이의제기 §2.1 반영, 신규 derive_session_name <workspace> <agent> [role=creator]printf '%s-%s-%s' "$slug" "$role" "$agent". create_session.sh--role 을 넘기도록 연결한다. W9a 와 반드시 같이 착수한다 — 순서가 어긋나면 새 이름(<slug>-planner-claude)을 이해하지 못하는 경로가 남는다. 기존 세션은 개명하지 않는다. 이름/role 불일치 3건은 그대로 두고, role 권위를 row 로 고정(W9c)해 해소한다.

W9c. role 판정의 권위를 레지스트리 row 로 고정 — F12, 신규 s['role'] 이 있으면 그것이 답이다. 이름 접미사는 row 가 없을 때의 폴백일 뿐임을 코드와 주석에 명시한다. run_loop.sh:resolve_planner_session 이 이미 s.get('role') 를 쓰고 있으므로 이것이 기준 구현이다.

W10. status.sh:resume_on_disk 에 hermes/cline 분기 추가 — F4. W11. create_session.sh wrapper 경로에서 CMD_FULL 재계산 — F5. W12. send_keys_safeagent 기반으로 전환 — F7. hermes 만 strict-verify 를 타는 것이 의도인지 확인 후 정책을 명시 기록. W13. orc_onboard 에 hermes argv 분기 추가 — F6. --resume[[:space:]=]+<id>.

Phase 4 — P2 및 정리

W14. create_session.sh--agent 검증을 파싱 직후로 이동 — F9. W15. TUI ready 토큰 4개를 전부 오버라이드 가능한 변수로 — F8. W16. 신규 에이전트 추가 절차를 AGENTS.md 에 기록 — W6a 레지스트리 + W6b 생성 표에 항목 하나만 추가하면 되도록 만든 뒤.


4. 검증 전략

두 하네스를 회귀 게이트로 쓴다.

probe_agent_abstraction.sh (Rev.1) — 판정 기준이 스크립트에 내장되어 있다:

A 의 불일치가 0, C 가 4개 에이전트 모두 scoped=YES, D 의 비-claude 참조가 0 이 아님.

probe_adoption_guard.sh (Rev.2 신규) — 이의제기 대응분의 게이트:

C3 에 오탈락(SUFFIX=REJECT + 지원 에이전트 + 워크스페이스 내부) 행이 없을 것, C4 의 role 키 존재 = YES, C5 의 "이름에 role 없음"이 새로 늘지 않을 것.

W8a 전용 추가 검증 (이의제기가 요구하는 것): 격리된 herdr 세션에 (i) 마커 있는 세션, (ii) 마커 없는 규격 이름 세션, (iii) 마커 없는 임의 이름 + 지원 에이전트 + 워크스페이스 내부 세션 셋을 띄우고 reconcile 을 --once 로 돌려, (i)(ii) 만 입양되고 (iii) 은 입양되지 않는지 확인한다. (iii) 이 agy 가 지적한 오염 케이스이고, (ii) 가 내가 지적한 오탈락 방지 케이스다.

W8d 차등 테스트: 교체 전후 동일 입력 집합에 대해 출력을 바이트 비교. 의도한 차이(오분류 4행)만 남고 나머지는 전부 동일해야 한다.

기존 스위트는 이 역할을 못 한다 — tier3/tier4 는 HEAD 에서 10분을 넘겨 job 7132d954 에서도 측정에 실패했다.


5. 리스크 및 미측정 항목

5.1 선행 블로커 없음. 커밋 657a749 로 해소(§Phase 0).

5.2 7132d954 와의 순서 의존. W6a 는 lib_py/ 패키지를 전제한다. 권장 순서: Phase 1(W1W5) 단독 실행 → 7132d954 lib_py 이행 → Phase 2 이후. Phase 1 은 어디에도 의존하지 않는다.

5.3 hermes 는 미설치 — 실측 불가. 이 머신에 hermes 바이너리가 없다(claude/agy/cline 은 있음). hermes 관련 판단은 전부 소스 독해이며 실행으로 확인하지 않았다:

  • ~/.hermes/state.dbsessions 테이블에 cwd 컬럼이 실재하는지 (tier-2/3 의 WHERE cwd=? 로부터의 추론)
  • hermes 세션 id 가 UUID 형식인지 (is_valid_id 가 그렇게 가정하나 미검증)
  • hermes --resume <uuid> 플래그의 실재 W2/W13 착수 전 hermes 설치 환경에서 확인할 것.

5.4 소유권 마커(W8a/W8b)의 미측정 부분. ps eww 로 herdr 주입 env 를 읽는 것은 이 머신에서 확인했으나, 다음은 미확인이다:

  • herdr agent start --env MAM_MANAGED=… 로 넣은 값이 페인 루트 프로세스의 env 에 실제로 나타나는지 (herdr 자체 주입 변수는 확인했으나 --env 경유 값은 별도 확인 필요)
  • Linux /proc/<pid>/environ 경로 (이 머신은 darwin)
  • 에이전트가 자식 프로세스를 새로 exec 하며 env 를 갈아끼우는 경우 W8b 착수 전 이 3가지를 먼저 실측할 것. 실패하면 대체 마커(예: .mam/owned/<session> 마커 파일)로 전환하되, 3단 판정 구조 자체는 유지한다.

5.5 default 서버 스캔은 이번 범위에서 바꾸지 않는다. unique_servers 가 항상 'default' 를 포함하는 것이 오염 노출의 근인이지만, 이를 제거하면 default 서버에 있는 현재 살아 있는 3개 세션 전부가 관측 대상에서 사라진다. 소유권 마커가 자리잡은 뒤 별도 job 으로 다룰 문제다.

5.6 cline id 는 UUID 가 아니다. 실제 형식은 1782614591159_mrkxj. is_valid_id^[0-9]{10,}_[0-9A-Za-z]+$ 로 올바르게 허용함은 확인했으나, "UUID" 라는 용어가 전반에 쓰여 오해를 부른다. 개명은 호출부 21곳(테스트 핀 고정)을 건드리므로 이번 범위 밖.

5.7 Rev.1 §2.3 의 노출 측정(cline 26개/8 cwd)은 이 머신 한정이다. 다만 스코핑 부재라는 사실 자체는 소스에서 확인된 것이므로 환경과 무관하다.

5.8 reconcile.sh 840줄 전체를 감사하지 않았다. 브리프가 지정한 6개 인터페이스와 이의제기가 지목한 입양 루프·row_agent 에 집중했다.


6. 판정

이의제기 3건을 모두 검토해 2건을 전면 수용, 1건(권고 remedy)을 실측 반증으로 대체하고, 신규 결함 3건(F10–F12)을 계획에 반영했다.

[ADJUDICATION: SUSTAINED]94b33591 §1 (입양 오염), §1.3 (role 누락), §2.1 (derive_session_name), §2.2 (shim 중복) [ADJUDICATION: OVERRULED]94b33591 §3.1 (접미사 일치 세션만 입양) — agy-creator-01 오탈락 실측

[VERDICT: PASS]

(이 토큰은 이 계획서 산출물의 완성도를 뜻한다. 감사 대상 코드에 대한 판정이 아니다 — 감사 결과는 P0 2건 + P1 6건 미해결이며, 특히 F1 은 resolve_session_id.sh 가 헤더에 명시한 P0-C 계약을 cline 경로에서 지키지 못하고 있는 상태다.)