Files
multi-agent-mux/bug_report.md
T
Godopu c3631e2aa1 fix(herdr): resolve shim routing defects, add workspace scoping, and bump to v3.0.1
- Resolve Herdr shim 5 routing & paste defects (ISSUE-1 ~ ISSUE-5):
  * paste-buffer: use pane send-text without auto-enter, propagate rc=3 to send_keys_safe
  * exact-match pane resolution: remove substring matching ('in tn') across all branches
  * workspace scoping: introduce HERDR_WORKSPACE_ID and .mam/herdr_workspace_id persistence
  * unified resolver: single _resolve_herdr_pane_id helper across shim commands
- Resolve dialog token false-positive on 'Yes, try it' tip and isolate fullscreen modal rejection
- Sync mock Herdr CLI contracts in tests/conftest.py
- Add contract tests H-15~H-23 and regression tests D-4~D-7 (412 tests, 100% PASS)
- Add multi-agent loop plans, review reports, and bug report
- Update framework and skill packages to v3.0.1
2026-08-27 21:42:37 +09:00

9.7 KiB

🐛 버그 리포트: Herdr 패인 라우팅 및 지침 주입 안전성 분석 리포트

  • 문서 버전: v1.0
  • 작성일자: 2026-08-27
  • 대상 모듈: .agents/skills/lib.sh (Multi-Agent Mux 코어 라이브러리)
  • 검토 참여 리뷰어: planner-reviewer-claude-01 (Claude), reviewer-creator-grok-01 (Grok), reviewer-cline-01 (Cline)

1. 개요 및 배경

Multi-Agent Mux(MAM) 오케스트레이션 루프(run_loop.sh) 구동 중, herdr agent로 자동 등록되지 않고 herdr pane 라벨(label: creator-agy-01, label: reviewer-cline-01) 형태로 동작하는 에이전트 세션에 대해 프롬프트 주입(inject_instructions / send_keys_safe)이 도달하지 못하고 세션이 무한 대기/타임아웃되는 현상이 발생했습니다.

이에 대한 임시 라우팅 패치 후 3개 독립 리뷰어 세션(claude, grok, cline)을 통해 교차 피어 리뷰를 수행한 결과, 추가적인 핵심 맹점(P0~P2)이 발굴되었습니다. 본 문서는 타 프로젝트 및 업스트림(multi-agent-mux) 반영을 위해 분석된 문제점과 권고 수정안을 상세히 정리합니다.


2. 핵심 결함 상세 분석 (Findings)

[ISSUE-1] P0: paste-buffer의 불필요한 Enter 전송으로 인한 이중 제출 (Double Submit)

  • 위치: .agents/skills/lib.sh paste-buffer (~880줄) 및 send_keys_safe (~1890줄)
  • 현상: paste-buffer 구현부에서 _real_herdr pane send-text 호출 직후 _real_herdr pane send-keys "$pane_id" Enter를 실행하도록 작성됨. 그러나 상위 send_keys_safe 함수는 자체적으로 텍스트 주입 확인 후 _sks_herdr send-keys -t "$sess" C-m을 1~3회 재시도하며 제출하는 2단계(Paste \rightarrow Submit) 프로토콜을 가짐.
  • 문제점:
    1. paste-buffer가 텍스트를 넣으면서 이미 1차 엔터를 쳐버림.
    2. send_keys_safe의 후속 C-m 루프가 빈 프롬프트(Empty Enter)를 전송하거나, CLI에 떠 있는 대화창/옵션을 의도치 않게 자동 수락하는 사이드 이펙트 유발.
  • 해결 방안: paste-buffer는 순수하게 텍스트를 입력 버퍼에 배치하는 작업만 수행해야 하며, 엔터 제출(Enter/C-m)은 send_keys_safe 단독으로 제어해야 함.

[ISSUE-2] P0: agent in tn 부분 매칭으로 인한 오라우팅 (Cross-Pane Mis-Routing)

  • 위치: .agents/skills/lib.sh capture-pane, send-keys, paste-buffer 내부 파이썬 스크랩 코드
  • 현상:
    if p.get("label") == tn or (p.get("agent") and p.get("agent") in tn):
        print(p.get("pane_id", ""))
        sys.exit(0)
    
  • 문제점: 동일한 CLI를 사용하는 다중 에이전트(예: planner-reviewer-claude-01creator-claude-02, 또는 reviewer-creator-grok-01worker-grok-02)가 동일 서버에 공존할 때, p.get("agent") == "grok"tn == "reviewer-creator-grok-01"의 부분 문자열("grok" in tn)로 참(True)이 되어, 목록에서 먼저 발견된 다른 에이전트의 패인으로 키와 텍스트가 잘못 전송됨.
  • 해결 방안: 부분 매칭을 전면 제거하고, p.get("label") == tn (라벨 완전 일치) \rightarrow p.get("agent") == tn or p.get("name") == tn (이름 완전 일치) 순서로 엄격한 일치만 허용.

[ISSUE-3] P1: pane list 조회 시 워크스페이스 미격리로 인한 패인 충돌 (Cross-Workspace Collision)

  • 위치: has-session, kill-session, capture-pane, send-keys, paste-buffer
  • 현상: herdr pane list의 결과는 서버 전체 전역(Server-Global) 목록임.
  • 문제점: MULTI_AGENT_RULES.md의 표준 명명 규칙에 따라 여러 프로젝트가 creator-agy-01, reviewer-cline-01 등 동일한 기본 라벨을 사용할 경우, 다른 워크스페이스의 패인을 조회하여 텍스트를 탈취하거나 명령을 잘못 주입할 위험이 있음.
  • 해결 방안: 현재 세션의 workspace_id(예: resolve_herdr_workspace 또는 p.get("workspace_id") == target_ws)를 조건에 포함하여 동일 워크스페이스 내의 패인만 식별하도록 스코프 제한.

[ISSUE-4] P2: agent start 성공 판정 정규식의 타임아웃 오인

  • 위치: .agents/skills/lib.sh new-session (~560줄)
  • 현상:
    if echo "$res" | grep -qE "agent_started|agent_not_ready|timed out waiting for agent startup"; then
      success=1
    
  • 문제점: timed out waiting for agent startupagent_not_ready는 실제로는 기동 실패 또는 지연 상태임에도 success=1로 처리되어 즉시 재시도 루프를 빠져나감. 이로 인해 TUI가 아직 뜨지 않은 빈 셸 상태에서 프롬프트 주입이 시도되어 실패를 초래함.
  • 해결 방안: 성공 판정은 agent_started (및 이미 실행 중인 확인 응답)만 인정하고, agent_not_ready/타임아웃 시에는 백오프 재시도를 수행하도록 엄격화.

[ISSUE-5] P2: 중복된 파이썬 원라이너 파서 및 데드 파이프라인

  • 위치: 5개 이상의 명령 분기(has-session, kill-session, capture-pane, send-keys, paste-buffer)
  • 현상: 각 분기마다 독립적으로 파이썬 스크립트가 복사-붙여넣기되어 구현이 제각각으로 분기(Drift)되었고, python3 -c "... except: pass"가 항상 exit code 0을 반환하여 bash의 || 체인이 2차 폴백으로 넘어가지 않는 데드 코드가 존재함.
  • 해결 방안: 단일 공용 헬퍼 함수(_resolve_herdr_pane_id)를 선언하여 모든 분기가 일관된 단일 로직을 호출하도록 리팩터링.

3.1 단일 공용 헬퍼 함수 선언 (_resolve_herdr_pane_id)

# _resolve_herdr_pane_id <session_name> [workspace_id]
# Resolves the concrete pane_id ("wN:pN") for a given session name or label.
# Strict resolution order:
#   1. herdr agent get <sanitized_name>
#   2. herdr agent get <raw_name>
#   3. herdr pane list (scoped by workspace_id, exact match on label, then exact match on agent/name)
_resolve_herdr_pane_id() {
  local target="$1"
  local target_ws="${2:-${HERDR_WORKSPACE_ID:-}}"
  local sat
  sat=$(_sanitize_herdr_agent_name "$target")

  # 1 & 2. Try native agent get
  local pid=""
  pid=$(_real_herdr agent get "$sat" 2>/dev/null | python3 -c "
import sys, json
try:
    d = json.load(sys.stdin)
    print(d.get('result', {}).get('agent', {}).get('pane_id') or '')
except Exception:
    pass
" 2>/dev/null || echo "")

  if [ -z "$pid" ]; then
    pid=$(_real_herdr agent get "$target" 2>/dev/null | python3 -c "
import sys, json
try:
    d = json.load(sys.stdin)
    print(d.get('result', {}).get('agent', {}).get('pane_id') or '')
except Exception:
    pass
" 2>/dev/null || echo "")
  fi

  # 3. Fallback to pane list with exact matching and optional workspace scoping
  if [ -z "$pid" ]; then
    pid=$(_real_herdr pane list 2>/dev/null | TARGET_NAME="$target" TARGET_WS="$target_ws" python3 -c "
import sys, json, os, re
tn = os.environ.get('TARGET_NAME', '')
tws = os.environ.get('TARGET_WS', '')
try:
    data = json.load(sys.stdin)
    panes = data.get('result', {}).get('panes', [])
    # Filter by workspace if provided
    if tws:
        panes = [p for p in panes if p.get('workspace_id') == tws]
    # Priority 1: Exact label match
    for p in panes:
        if p.get('label') == tn:
            pid = p.get('pane_id', '')
            if pid:
                print(pid)
                sys.exit(0)
    # Priority 2: Exact agent name match (NO substring matching)
    for p in panes:
        if p.get('agent') == tn or p.get('name') == tn:
            pid = p.get('pane_id', '')
            if pid:
                print(pid)
                sys.exit(0)
except Exception:
    pass
sys.exit(1)
" 2>/dev/null || echo "")
  fi

  # Validate pane_id format (e.g. w1:p2)
  if [[ "$pid" =~ ^w[0-9]+:p[0-9]+$ ]]; then
    echo "$pid"
    return 0
  fi
  return 1
}

3.2 분기별 Shim 간소화 및 정합성 보장

# capture-pane
capture-pane)
  sess=""
  # ... parse -t ...
  pane_id=$(_resolve_herdr_pane_id "$sess")
  if [ -n "$pane_id" ]; then
    _real_herdr pane read "$pane_id" --source visible --lines 100 2>/dev/null || true
  else
    _real_herdr agent read "$sess" --source visible --lines 100 2>/dev/null || true
  fi
  ;;

# send-keys
send-keys)
  sess="" key=""
  # ... parse -t and key ...
  [ "$key" = "C-m" ] && key="Enter"
  pane_id=$(_resolve_herdr_pane_id "$sess")
  if [ -n "$pane_id" ]; then
    _real_herdr pane send-keys "$pane_id" "$key" >/dev/null 2>&1 || true
  else
    _real_herdr pane send-keys "$sess" "$key" >/dev/null 2>&1 || true
  fi
  ;;

# paste-buffer (NO Enter, insert-only)
paste-buffer)
  # ... parse -b and -t ...
  pane_id=$(_resolve_herdr_pane_id "$sess")
  if [ -f "$buffer_dir/$buf" ]; then
    if [ -n "$pane_id" ]; then
      # Insert text only. Enter submission is strictly delegated to send_keys_safe.
      _real_herdr pane send-text "$pane_id" "$(cat "$buffer_dir/$buf")" >/dev/null 2>&1 || true
    else
      _real_herdr agent send "$sess" "$(cat "$buffer_dir/$buf")" >/dev/null 2>&1 || true
    fi
  fi
  ;;

4. 결론 및 향후 조치

  • 본 버그 리포트에 명시된 5대 핵심 결함(P0: 이중 제출, P0: substring 오라우팅, P1: 워크스페이스 미격리, P2: 타임아웃 성공 오인, P2: 중복 파서)은 독립적인 3인 리뷰어의 정밀 교차 검증을 통해 그 위험성과 재현 경로가 명백히 입증되었습니다.
  • 본 리포트의 내용을 기반으로 multi-agent-mux 업스트림 저장소 및 라이브러리 유지보수 프로젝트에서 lib.sh 리팩터링 패치를 공식 배포할 것을 강력히 권고합니다.