chore(docs): clean up temporary FIX.md and bug_report.md
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
### 수정 내역 (lib.sh)
|
||||
|
||||
1. 에이전트 시작 판별 조건 완화 (new-session / `agent start`)
|
||||
|
||||
- 치명적 CLI 오류(`^usage:` / `^error:` 등)를 **성공 분류보다 먼저** 판별한다.
|
||||
- `agent_started`뿐 아니라 herdr 문서의 `agent_not_ready`(프로세스는 살아 있고 다이얼로그에 막힘)도
|
||||
롤백하지 않고 `wait_for_tui_ready`로 넘긴다.
|
||||
- `timed out waiting for agent startup`은 **성공으로 승격하지 않는다.** herdr는 `/bin/false`처럼
|
||||
이미 죽은 프로세스에도 같은 메시지를 반환하므로, 이 토큰은 실패와 지연을 구분하지 못한다.
|
||||
|
||||
2. Claude 풀스크린 렌더러 업셀 모달
|
||||
|
||||
- 차단형 모달 고유 시그니처는 `Yes, try it`이다. `_MAM_DIALOG_TOKENS`과
|
||||
`handle_startup_dialogs`는 이 문자열만 쓴다.
|
||||
- 거절은 **Escape**다. Enter는 기본 선택지 `Yes, try it`을 수락하고 세션을 permission flag 없이
|
||||
재시작한다.
|
||||
- idle 팁 `Try the new fullscreen renderer — … · /tui fullscreen`은 입력을 막지 않으므로
|
||||
다이얼로그로 취급하지 않는다. 광의 토큰 `fullscreen renderer|Try the new fullscreen`는
|
||||
팁과 모달을 동시에 매칭해 `wait_for_tui_ready`를 데드락 시킨다.
|
||||
|
||||
3. 검증: `tests/test_b19_headless_reconcile_fixes.py`에서 `_pane_capture`를 stub 한 행위 테스트
|
||||
(모달 vs 팁).
|
||||
-215
@@ -1,215 +0,0 @@
|
||||
# 🐛 버그 리포트: 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` 내부 파이썬 스크랩 코드
|
||||
- **현상**:
|
||||
```python
|
||||
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-01`과 `creator-claude-02`, 또는 `reviewer-creator-grok-01`과 `worker-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줄)
|
||||
- **현상**:
|
||||
```bash
|
||||
if echo "$res" | grep -qE "agent_started|agent_not_ready|timed out waiting for agent startup"; then
|
||||
success=1
|
||||
```
|
||||
- **문제점**:
|
||||
`timed out waiting for agent startup` 및 `agent_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. 권고 아키텍처 및 개선 코드 (Recommended Implementation)
|
||||
|
||||
### 3.1 단일 공용 헬퍼 함수 선언 (`_resolve_herdr_pane_id`)
|
||||
|
||||
```bash
|
||||
# _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 간소화 및 정합성 보장
|
||||
|
||||
```bash
|
||||
# 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` 리팩터링 패치를 공식 배포할 것을 강력히 권고합니다.
|
||||
Reference in New Issue
Block a user