Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4b1fb3329 | ||
|
|
6378471702 | ||
|
|
974941bdb4 | ||
|
|
c00fbb1356 | ||
|
|
87bb2780ac | ||
|
|
daa1476714 | ||
|
|
087a294135 | ||
|
|
90afd45aba | ||
|
|
336aa5fd9d | ||
|
|
e2b3ee7e82 | ||
|
|
d7fa9af410 |
@@ -1,6 +1,6 @@
|
||||
# MULTI_AGENT_RULES.md
|
||||
|
||||
본 문서는 새로운 프로젝트에 **MQTT 메시징 백플레인 및 Tmux 기반 멀티 에이전트 오케스트레이션 워크플로우**를 도입하고, 협업하는 에이전트들이 일관된 규칙과 아키텍처에 따라 안전하고 견고하게 작업을 수행할 수 있도록 정의한 공통 지침 및 규약입니다.
|
||||
본 문서는 새로운 프로젝트에 **MQTT 메시징 백플레인 및 Herdr 기반 멀티 에이전트 오케스트레이션 워크플로우**를 도입하고, 협업하는 에이전트들이 일관된 규칙과 아키텍처에 따라 안전하고 견고하게 작업을 수행할 수 있도록 정의한 공통 지침 및 규약입니다.
|
||||
|
||||
새로운 프로젝트에서 작업하는 모든 에이전트는 작업을 시작하기 전 이 문서를 반드시 정독하고 규약을 준수해야 합니다.
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
### 🗃️ 레지스트리 및 상태 관리
|
||||
- 본 아키텍처는 목적에 따라 두 가지 레지스트리를 분리하여 운영합니다:
|
||||
- **잡 레지스트리 (Job Registry)**: 각 비동기 잡의 메타데이터와 생명주기는 개별 JSON 파일(`.mam/jobs/<id>.json`)로 기록되며, 다중 세션 간의 동시 청구(claiming) 경합은 파일 단위의 `fcntl` advisory lock(`registry_lock` via `registry.py`)을 통해 방어합니다.
|
||||
- **세션 레지스트리 (Session Registry)**: TMUX 모니터링 상태 및 에이전트 구동 정보는 SQLite WAL 데이터베이스(`.mam/agent-sessions.db`)를 통해 단일 호스트 내에서 안정적인 동시 트랜잭션으로 일관되게 제어합니다. 단, SQLite WAL 모드는 NFS(네트워크 파일 시스템) 환경에서는 완전한 파일 락이 보장되지 않으므로 로컬 파일 시스템 사용을 권장합니다.
|
||||
- **세션 레지스트리 (Session Registry)**: Herdr 모니터링 상태 및 에이전트 구동 정보는 SQLite WAL 데이터베이스(`.mam/agent-sessions.db`)를 통해 단일 호스트 내에서 안정적인 동시 트랜잭션으로 일관되게 제어합니다. 단, SQLite WAL 모드는 NFS(네트워크 파일 시스템) 환경에서는 완전한 파일 락이 보장되지 않으므로 로컬 파일 시스템 사용을 권장합니다.
|
||||
|
||||
### 🛡️ 보안 프로토콜 (HMAC-SHA256)
|
||||
- **무인증 PoC 모드**: 잡 레지스트리 생성 시 `auth_token`이 `null`로 지정된 경우(PoC 기본 모드), 별도의 서명 검증을 생략하고 모든 이벤트를 수용합니다 (`verify_hmac`이 항상 `True`를 반환).
|
||||
@@ -113,22 +113,22 @@ sequenceDiagram
|
||||
장기 실행 에이전트 분석 중 발생하는 유실 및 인프라적 장애를 예방하기 위한 중요 지침입니다.
|
||||
|
||||
### 📸 TUI 뷰포트 절단 방지 (Pane Snapshotting 3대 규칙)
|
||||
TMUX 환경에서 실행되는 에이전트가 화면 스크롤 한계로 인해 이전 출력이나 장문의 디버깅 로그를 잃지 않도록 아래의 **스냅샷 패턴을 의무적으로 수행**합니다.
|
||||
Herdr 환경에서 실행되는 에이전트가 화면 스크롤 한계로 인해 이전 출력이나 장문의 디버깅 로그를 잃지 않도록 아래의 **스냅샷 패턴을 의무적으로 수행**합니다.
|
||||
1. **Pre-brief Capture**: 작업 지침(Brief)을 전송한 직후, 즉시 해당 세션의 pane을 캡처(`capture-pane -S -200`)해두어 입력 기록의 시작점을 백업합니다.
|
||||
2. **Loop Snapshot**: 장기 실행(5분 이상) 중인 에이전트 세션의 경우, 주기적으로(예: 30초마다) 뷰포트를 스캔하여 증분 데이터를 `/tmp/pane-snap.txt`에 계속 누적(append) 기록합니다.
|
||||
3. **Post-job Capture**: 잡 완료/에러 반환 즉시 전체 pane 상태를 마지막으로 캡처하여 전체 작업 궤적을 보존합니다.
|
||||
|
||||
### 📄 마크다운 기반 협업 및 결과 전달 (Markdown-Based Workflow & Communication)
|
||||
- **핵심 원칙**: TMUX `send-keys`나 입력 버퍼를 통해 긴 지시사항을 직렬로 입력하는 과정에서 문자 누락이나 레이아웃 유실이 발생하는 것을 방지하기 위해, 에이전트 간의 모든 주요 협업 소통은 파일 기반 마크다운 문서 생성을 원칙으로 합니다.
|
||||
- **핵심 원칙**: herdr `send-keys`나 입력 버퍼를 통해 긴 지시사항을 직렬로 입력하는 과정에서 문자 누락이나 레이아웃 유실이 발생하는 것을 방지하기 위해, 에이전트 간의 모든 주요 협업 소통은 파일 기반 마크다운 문서 생성을 원칙으로 합니다.
|
||||
- **세부 규칙 및 규약**:
|
||||
- **예외 사항**: 1~2줄 내외의 매우 단순한 요청, 상태 확인, 수락 진행 등의 단발성 프롬프트는 tmux 입력을 통해 직접 보낼 수 있습니다.
|
||||
- **예외 사항**: 1~2줄 내외의 매우 단순한 요청, 상태 확인, 수락 진행 등의 단발성 프롬프트는 herdr 입력을 통해 직접 보낼 수 있습니다.
|
||||
- **작업 위임**:
|
||||
- *수동 경로*: 상세 사양과 계획 수립 등의 복잡한 작업 지시는 먼저 로컬 마크다운 파일(예: `.mam/reports/brief-<job_id>.md` 또는 지정된 워크스페이스 경로)로 작성한 후, 에이전트에게 `"Read <파일경로> and execute."` 라는 실행 명령만 전달하십시오.
|
||||
- *자동 경로*: 자동화 잡 런너(`multi-agent-mux-delegate-job submit`)는 잡 등록 시 `.mam/jobs/<job_id>/brief.md` 디렉터리에 지시서를 자동 집필하고 단일 라인 포인터 프롬프트만 에이전트 세션에 인가합니다.
|
||||
- **결과 안내 및 피드백**:
|
||||
- *수동/영구 리뷰*: 상세 리뷰 결과, 설계 제안서 등은 `.mam/reports/<tmux_session_name>/report-<job_id>.md` 경로에 저장합니다.
|
||||
- *수동/영구 리뷰*: 상세 리뷰 결과, 설계 제안서 등은 `.mam/reports/<herdr_session_name>/report-<job_id>.md` 경로에 저장합니다.
|
||||
- *자동화 잡 보고서*: 자동 위임된 비동기 작업의 완료 결과는 잡 디렉터리 하위인 `.mam/jobs/<job_id>/<agent_name>-reports/report-final.md` (루프/Discuss 위임 시에는 `<clean_session_name>-reports/`) 경로에 기록해야 합니다.
|
||||
- *버전 관리 이관*: 버전 관리가 필요한 주요 산출물(최종 설계 계획, 최종 리뷰 보고서, 보안 감사 리포트 등)은 gitignore 대상인 `.mam/` 하위가 아닌, 버전 관리 대상 경로(구체적으로 `.agents/reports/<tmux_session_name>/` 또는 `docs/reports/` 등)로 명시적으로 복사하여 이관 보존해야 합니다.
|
||||
- *버전 관리 이관*: 버전 관리가 필요한 주요 산출물(최종 설계 계획, 최종 리뷰 보고서, 보안 감사 리포트 등)은 gitignore 대상인 `.mam/` 하위가 아닌, 버전 관리 대상 경로(구체적으로 `.agents/reports/<herdr_session_name>/` 또는 `docs/reports/` 등)로 명시적으로 복사하여 이관 보존해야 합니다.
|
||||
- **디스크 정리 및 보존 정책 계약 (Cleanup & Retention)**: `.mam/jobs/<job_id>/` 및 `.mam/reports/` 폴더 아래의 파일들은 휘발성 감사 이력(audit-trail) 산출물입니다. 버전 관리가 필요한 문서들은 `.agents/reports/` 하위로 수동 복사하여 커밋해야 하며, `stop_session.sh` 세션 종료 스크립트는 이들 보고서 디렉터리를 자동으로 삭제하지 않으므로 수동 또는 주기적 클린업이 권장됩니다.
|
||||
|
||||
### ⏱️ 타임아웃 구성 및 정렬 규칙
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# MULTI_AGENT_RULES.md
|
||||
|
||||
This document serves as the common guidelines and protocol for introducing the **MQTT messaging backplane and Tmux-based multi-agent orchestration workflow** to a new project. It defines the rules and architecture to ensure collaborating agents perform tasks safely, robustly, and consistently.
|
||||
This document serves as the common guidelines and protocol for introducing the **MQTT messaging backplane and Herdr-based multi-agent orchestration workflow** to a new project. It defines the rules and architecture to ensure collaborating agents perform tasks safely, robustly, and consistently.
|
||||
|
||||
All agents working on a new project must read this document thoroughly and comply with the defined protocols before starting any tasks.
|
||||
|
||||
@@ -54,7 +54,7 @@ Asynchronous communication and state management between agents are controlled vi
|
||||
### 🗃️ Registry & State Management
|
||||
- This architecture maintains two distinct registries based on their purpose:
|
||||
- **Job Registry**: The metadata and lifecycle of each asynchronous job are recorded in individual JSON files (`.mam/jobs/<id>.json`). Concurrency conflicts (claiming races) across multiple sessions are prevented via file-based `fcntl` advisory locks (`registry_lock` via `registry.py`).
|
||||
- **Session Registry**: TMUX monitoring states and running agent metadata are consistently controlled using a SQLite WAL database (`.mam/agent-sessions.db`) to support reliable concurrent transactions on a single host. However, since SQLite WAL mode does not guarantee complete file locking in Network File System (NFS) environments, we recommend using a local file system.
|
||||
- **Session Registry**: Herdr monitoring states and running agent metadata are consistently controlled using a SQLite WAL database (`.mam/agent-sessions.db`) to support reliable concurrent transactions on a single host. However, since SQLite WAL mode does not guarantee complete file locking in Network File System (NFS) environments, we recommend using a local file system.
|
||||
|
||||
### 🛡️ Security Protocol (HMAC-SHA256)
|
||||
- **Unauthenticated PoC Mode**: If the `auth_token` in the job registry is set to `null` (the default PoC mode), signature verification is skipped and all events are accepted (`verify_hmac` always returns `True`).
|
||||
@@ -113,7 +113,7 @@ sequenceDiagram
|
||||
These are critical instructions for preventing data loss and infrastructure-level failures during long-running agent analyses.
|
||||
|
||||
### 📸 Preventing TUI Viewport Truncation (The 3 Pane Snapshotting Rules)
|
||||
To ensure that agents running in TMUX environments do not lose debug logs or previous outputs due to screen scrollback limits, the following **snapshotting pattern must be enforced**:
|
||||
To ensure that agents running in Herdr environments do not lose debug logs or previous outputs due to screen scrollback limits, the following **snapshotting pattern must be enforced**:
|
||||
1. **Pre-brief Capture**: Capture the pane (`capture-pane -S -200`) immediately after sending the task instruction (Brief) to back up the starting point of the input history.
|
||||
2. **Loop Snapshot**: For long-running agent sessions (5 minutes or more), periodically (e.g., every 30 seconds) scan the viewport and append the incremental data to `/tmp/pane-snap.txt`.
|
||||
3. **Post-job Capture**: Capture the complete pane state one final time immediately after a job completes or returns an error to preserve the entire execution trajectory.
|
||||
@@ -121,14 +121,14 @@ To ensure that agents running in TMUX environments do not lose debug logs or pre
|
||||
### 📄 Markdown-Based Workflow & Communication (마크다운 기반 협업 및 결과 전달)
|
||||
- **Core Principle**: To prevent TUI character loss, truncation, and layout breakage during sequential input typing, all collaborative workflows must favor file-based markdown communication.
|
||||
- **Rules & Protocols**:
|
||||
- **Exception**: Extremely simple prompts (e.g., "Re-evaluate", "Check status", "Proceed") of 1 or 2 lines may be sent directly via tmux input buffers.
|
||||
- **Exception**: Extremely simple prompts (e.g., "Re-evaluate", "Check status", "Proceed") of 1 or 2 lines may be sent directly via herdr input buffers.
|
||||
- **Task Delegation**:
|
||||
- *Manual path*: Detailed task briefs may be written to a local Markdown file (e.g., `.mam/reports/brief-<job_id>.md` or a workspace path) first. The sender then issues a simple trigger command: `"Read <file_path> and execute."`
|
||||
- *Automated path*: The automated job runner (`multi-agent-mux-delegate-job submit`) automatically provisions the brief at `.mam/jobs/<job_id>/brief.md` and sends a short pointer instruction to the agent.
|
||||
- **Result Reporting & Feedback**:
|
||||
- *Manual/Durable reviews*: Detailed reviews, design proposals, or audit reports must be saved under `.mam/reports/<tmux_session_name>/report-<job_id>.md`.
|
||||
- *Manual/Durable reviews*: Detailed reviews, design proposals, or audit reports must be saved under `.mam/reports/<herdr_session_name>/report-<job_id>.md`.
|
||||
- *Automated job reports*: Automated execution results are saved directly to `.mam/jobs/<job_id>/<agent_name>-reports/report-final.md` (or `<clean_session_name>-reports/` for loops) as transient files.
|
||||
- *Versioned promotions*: Any final design plans, review verdicts, or security audit reports that require version control must be explicitly copied to tracked directory paths (specifically under `.agents/reports/<tmux_session_name>/` or `docs/reports/`).
|
||||
- *Versioned promotions*: Any final design plans, review verdicts, or security audit reports that require version control must be explicitly copied to tracked directory paths (specifically under `.agents/reports/<herdr_session_name>/` or `docs/reports/`).
|
||||
- **Cleanup & Retention Contract**: Files under `.mam/jobs/<job_id>/` and `.mam/reports/` are transient audit-trail artifacts. While durable outcomes are committed to version control under `.agents/reports/`, ephemeral directory trees can be cleaned up manually as needed; `stop_session.sh` does not automatically purge these report trees during session exit.
|
||||
|
||||
### ⏱️ Timeout Configuration & Alignment Rules
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# 리뷰 리포트 — Job 4094502a (MULTI_AGENT_RULES.md/ko.md tmux→herdr 개정)
|
||||
|
||||
- **리뷰 대상**: 작업 트리 미커밋 diff — `.agents/MULTI_AGENT_RULES.md`(6개소), `.agents/MULTI_AGENT_RULES.ko.md`(7개소), cline 작성
|
||||
- **리뷰어**: claude (planner-reviewer)
|
||||
|
||||
## 1. 변경 무결성 — 토큰 단위 검증
|
||||
|
||||
`git diff --word-diff` 전수 집계 결과, 변경은 **정확히 23개 토큰 치환**(TMUX/Tmux/tmux → Herdr/herdr, `Tmux-based`→`Herdr-based`, `<tmux_session_name>`→`<herdr_session_name>` 경로 플레이스홀더 4건 포함)뿐이며 **문장 추가·삭제·구조 변경 0건**. 번역 유실이나 mermaid/sequenceDiagram 블록 훼손 없음. 양 언어판의 치환 지점이 상호 대응함(ko의 send-keys 문구 1건은 원래 ko에만 존재하는 기존 번역 차이로, 이번 diff와 무관).
|
||||
|
||||
## 2. 잔존 레거시 스윕
|
||||
|
||||
- 두 파일 모두 대소문자 무시 `tmux` 검색 **0건** — 누락된 레거시 없음.
|
||||
- 저장소 전체에서 `<tmux_session_name>` 플레이스홀더 잔존은 `.agents/reports/` 하위 **아카이브된 과거 리뷰 리포트 3건뿐** — 역사적 감사 기록이므로 개정 대상이 아님(방치 아님).
|
||||
|
||||
## 3. herdr 사양 교차 검증 (실구현 대조)
|
||||
|
||||
| 문서 표기 | 실구현 근거 | 판정 |
|
||||
|---|---|---|
|
||||
| "herdr `send-keys`" / "herdr 입력" | shim(`.mam/shim/herdr:318`)에 `send-keys` 의사 명령 실재 | ✅ 부합 |
|
||||
| `capture-pane -S -200` (유지된 기존 문구) | shim `capture-pane` 의사 명령 실재(:301) | ⚠️ 명령은 실재하나 아래 비차단 지적 1 참조 |
|
||||
| `.agents/reports/<herdr_session_name>/` 관례 | 실제 디렉터리(`.agents/reports/canary-projects-…-cline` 등)가 세션명 기반으로 운영 중 | ✅ 부합 |
|
||||
| "Herdr 모니터링 상태 … SQLite WAL" | `.mam/agent-sessions.db` + `herdr_sessions` 스키마 현행 일치 | ✅ 부합 |
|
||||
|
||||
## 4. 비차단(Non-blocking) 지적
|
||||
|
||||
1. **`capture-pane -S -200`의 시맨틱 공백(기존 문구, 이번 diff 무관)**: shim의 `capture-pane`은 `-S -200` 플래그를 파싱하지 않고 조용히 무시하며 항상 `agent read --source visible --lines 100`으로 동작한다. 즉 문서가 약속하는 "스크롤백 200행 백업"이 실제로는 "가시 영역 100행"으로 축소 실행된다. 스냅샷 규칙의 취지(뷰포트 절단 방지)가 약화되므로, 후속 개선으로 (a) shim이 `-S -N`을 `--lines N`으로 매핑하거나 (b) 문서에서 플래그 표기를 herdr 실사양으로 갱신할 것을 권장.
|
||||
2. **의사 명령 전제 미표기**: 다른 SKILL.md들은 `send-keys`/`capture-pane`이 "lib.sh 소싱 후에만 동작하는 tmux-compat 의사 명령"임을 명시하나, 본 규칙 문서는 전제 없이 사용한다. 규칙서가 신규 에이전트의 첫 관문임을 고려하면 각주 1줄 추가 가치가 있음.
|
||||
|
||||
## 5. 결론
|
||||
|
||||
치환은 토큰 단위로 정밀하고(내용 유실 0), 잔존 레거시 0건이며, 도입된 어휘가 현행 스킬/shim/디렉터리 관례와 전부 부합한다. 비차단 2건은 이번 diff가 만들지 않은 기존 문구의 후속 개선 사항이다.
|
||||
|
||||
[VERDICT: PASS]
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# 구현 계획서 — deploy/ 배포 설정 tmux→herdr 전환 (Job baa15c96)
|
||||
|
||||
- **Planner**: claude (planner-reviewer)
|
||||
- **입력**: `.mam/deploy_brief.md` + `deploy/` 전수 분석 + 현행 스킬 구현(단일 진실 소스) 대조
|
||||
- **핵심 원칙**: 문서·스크립트가 브리프의 *추정* 어휘가 아니라 **현행 스킬이 실제 소비하는 어휘**와 일치해야 한다. 실측 결과 브리프의 제안 중 2건은 실제 구현과 다르므로 아래와 같이 교정한다:
|
||||
- ~~`HERDR_SESSION_NAME`~~ → **`HERDR_SERVER_NAME`** (스킬 전체가 이 이름만 소비, `TMUX_SERVER_NAME` 소비처는 0곳)
|
||||
- ~~`--herdr-session`~~ → **`--herdr-server`** (`create_session.sh:63`이 수용하는 유일한 플래그, `--tmux-server`는 이미 제거되어 **미지원**)
|
||||
|
||||
## 0. 실측 현황 (전수 스윕: `grep -in "tmux" deploy/` + 스킬 대조)
|
||||
|
||||
| 파일 | 행 | 현재 내용 | 판정 |
|
||||
|---|---|---|---|
|
||||
| install_mam.sh | 224 | `--tmux-server multi-agent-mux` (Quick Start 예시) | 🔴 **기능 파손** — create_session.sh가 이 플래그를 거부(unknown arg, exit 2). 사용자가 복붙 시 즉시 실패 |
|
||||
| install_mam.sh | 227 | `$ tmux -L multi-agent-mux attach -t <session_name>` | 🔴 죽은 명령 |
|
||||
| install.sh | 31 | `check_cmd tmux` | 🟠 잘못된 의존성 진단(herdr 미검사) |
|
||||
| install.sh | 249 | `.env`에 `TMUX_SERVER_NAME=default` 기록 | 🟠 죽은 설정(소비처 0) |
|
||||
| README.md | 9 | requirements "(`tmux`, `python3`)" | 🟡 문서 불일치 |
|
||||
| INSTALL.md | 21, 37 | 진단 목록에 `tmux` | 🟡 문서 불일치(install_mam.sh:86 실제 DEPS는 이미 `herdr python3 rsync uuidgen`) |
|
||||
| INSTALL.md | 69 | "호스트 재기동으로 tmux가 소멸한 경우" | 🟡 문서 불일치 |
|
||||
| plugin.json | 3 | `"... Backplane on Tmux & MQTT."` | 🟡 메타데이터 불일치 |
|
||||
| remove.sh / update.sh | — | tmux/kill 로직 **없음**(파일 삭제·venv·문서 갱신뿐) | ✅ 수정 불요(브리프 검토 항목 종결) |
|
||||
| generate-env.sh / gitea-ci.yml | — | tmux 참조 없음 | ✅ 수정 불요 |
|
||||
|
||||
## 1. 파일별 수정 계획 (변경 대비표)
|
||||
|
||||
### 1-1. `deploy/install_mam.sh` (우선순위 1 — 기능 파손 수정)
|
||||
| 행 | 변경 전 | 변경 후 |
|
||||
|---|---|---|
|
||||
| 224 | `--tmux-server multi-agent-mux` | `--herdr-server multi-agent-mux` |
|
||||
| 227 | `$ tmux -L multi-agent-mux attach -t <session_name>` | `$ HERDR_SERVER_NAME=multi-agent-mux herdr agent attach <session_name>` |
|
||||
|
||||
근거: 224는 `create_session.sh:63`의 실제 플래그. 227은 create_session.sh:328이 YAML `attach_command`로 방출하는 **canonical 형식**(`HERDR_SERVER_NAME=<server> herdr agent attach <name>`)과 동일하게 맞춘다(개별 에이전트 pane attach). 전체 서버 화면이 필요하면 `herdr session attach multi-agent-mux`도 각주로 병기 가능.
|
||||
|
||||
### 1-2. `deploy/install.sh`
|
||||
| 행 | 변경 전 | 변경 후 |
|
||||
|---|---|---|
|
||||
| 31 | `check_cmd tmux` | `check_cmd herdr` |
|
||||
| 249 | `TMUX_SERVER_NAME=default` | `HERDR_SERVER_NAME=default` |
|
||||
|
||||
권장 추가(선택): `check_cmd herdr` 실패 시 install_mam.sh:96–98과 동일한 설치 안내(`curl -fsSL https://herdr.dev/install.sh \| sh`)를 출력하도록 `check_cmd` 호출부 뒤에 힌트 블록 추가 — 두 인스톨러의 UX 일관성 확보.
|
||||
마이그레이션 노트: 기존 설치본 `.env`의 `TMUX_SERVER_NAME`은 소비처가 없어 잔존해도 무해하므로 자동 치환 로직은 불요(계획서 기록으로 갈음).
|
||||
|
||||
### 1-3. `deploy/README.md`
|
||||
| 행 | 변경 전 | 변경 후 |
|
||||
|---|---|---|
|
||||
| 9 | ``checks system requirements (`tmux`, `python3`)`` | ``checks system requirements (`herdr`, `python3`)`` |
|
||||
|
||||
### 1-4. `deploy/INSTALL.md`
|
||||
| 행 | 변경 전 | 변경 후 |
|
||||
|---|---|---|
|
||||
| 21 | ``시스템의 `tmux`, `python3`, `rsync`, `uuidgen` …을 진단`` | ``시스템의 `herdr`, `python3`, `rsync`, `uuidgen` …을 진단`` |
|
||||
| 37 | ``**의존성 진단**: … `tmux`, `python3`, …`` | ``**의존성 진단**: … `herdr`, `python3`, …`` |
|
||||
| 69 | `호스트 재기동으로 tmux가 소멸한 경우에도` | `호스트 재기동으로 herdr 서버가 소멸한 경우에도` |
|
||||
|
||||
### 1-5. `deploy/plugin.json`
|
||||
| 행 | 변경 전 | 변경 후 |
|
||||
|---|---|---|
|
||||
| 3 | `"… Backplane on Tmux & MQTT."` | `"… Backplane on Herdr & MQTT."` |
|
||||
|
||||
### 1-6. `deploy/remove.sh`, `deploy/update.sh` — **수정 없음** (분석 결과 기록)
|
||||
두 스크립트 모두 세션 킬링 로직 자체가 존재하지 않고 파일 자산 삭제/갱신만 수행하므로 tmux→herdr 마이그레이션 대상이 아니다. (선택적 후속 개선: remove.sh가 스킬 제거 전 실행 중인 herdr 에이전트 세션을 `multi-agent-mux-stop`으로 정리하도록 권고하는 안내 문구 추가 — 본 브리프 범위 밖이므로 별도 결정.)
|
||||
|
||||
## 2. 구현 순서
|
||||
|
||||
1. install_mam.sh (기능 파손 우선) → 2. install.sh → 3. 문서 3종(README/INSTALL/plugin.json) → 4. 검증 → 5. 커밋(예: `fix(deploy): migrate deployment scripts and docs from tmux to herdr`).
|
||||
|
||||
## 3. 검증 계획 (Reviewer/Creator 공용 DoD)
|
||||
|
||||
1. **잔존 스윕**: `grep -rin "tmux" deploy/` → **0건** (대소문자 무시 필수 — `Tmux` 표기가 plugin.json에 존재했음).
|
||||
2. **정적**: 수정된 .sh에 `bash -n` + `shellcheck -S warning`, 변경 전 대비 신규 경고 0건. plugin.json은 `python3 -m json.tool`로 유효성 확인.
|
||||
3. **기능(핵심)**: Quick Start 예시를 **그대로 복붙 실행** — 스크래치 워크스페이스에서 `create_session.sh --workspace <scratch> --agent claude --role developer --isolate --herdr-server <scratch서버명> --dry-run` 이 exit 0. (--dry-run이 spawn 전에 종료하므로 라이브 무접촉. `--isolate` 플래그는 create_session.sh:66에서 여전히 수용됨을 확인 완료.)
|
||||
4. **attach 명령 실증**: 문서의 attach 형식이 실제 YAML `attach_command`(예: `.mam/agent-sessions.yaml:18`)와 동일 형식인지 대조.
|
||||
5. **회귀**: `.env` 신규 생성 경로에서 `HERDR_SERVER_NAME=default` 기록 확인, `TMUX_SERVER_NAME` 소비처 0곳 재확인(`grep -rn TMUX_SERVER_NAME .agents/skills` = 0).
|
||||
|
||||
## 4. 완료 기준
|
||||
|
||||
- deploy/ 내 tmux 참조(대소문자 무관) 0건, Quick Start 예시 실행 가능, 정적 검사 클린, 문서 어휘가 현행 스킬 구현(`--herdr-server`/`HERDR_SERVER_NAME`/`herdr agent attach`)과 1:1 일치.
|
||||
@@ -0,0 +1,144 @@
|
||||
# ✅ Peer Review Report: delegate-job run_agent() herdr 버그 3건 수정 검토
|
||||
|
||||
**Reviewer**: Reviewer B (Cline, `canary-projects-multi-agent-mux-reviewer-cline`)
|
||||
**Job ID**: 7f25e72e
|
||||
**Review Target**: `.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job`의 `run_agent()` 함수 herdr 관련 버그 3건 수정 (커밋 `6df4b03`에 반영됨, working tree의 해당 파일은 clean)
|
||||
**Review Scope**: (a) 수정이 실제 herdr CLI 문법과 맞는지, (b) source 순서 변경이 스크립트 다른 부분과 충돌하지 않는지, (c) HERDR_SERVER_NAME 자동 해석이 비격리(default) 세션에 회귀 없이 동작하는지
|
||||
**Method**: 실제 herdr v0.7.4 도움말 직접 열람 + shim has-session/agent attach 실행 + resolve_herdr_workspace python3 시뮬레이션 + 형제 스킬(resume/stop) 일관성 비교 + 55개 테스트 실행
|
||||
|
||||
---
|
||||
|
||||
## 1. 발견된 버그 3건과 수정 내용 (커밋 6df4b03)
|
||||
|
||||
### 버그 1: lib.sh source 순서 — has-session이 실제 바이너리 호출 (항상 실패)
|
||||
- **문제**: lib.sh를 `run_agent()` 내부, has-session 체크(라인 437)보다 늦게(라인 444) source → has-session 호출 시점엔 `herdr`이 shim이 아닌 실제 바이너리 → 존재하지 않는 `has-session` 서브커맨드 → 항상 실패
|
||||
- **수정**: `source "$SCRIPT_DIR/../lib.sh"`를 라인 31(스크립트 상단, `run_agent()` 정의 라인 408보다 377줄 앞)로 이동 + 주석 "Source EARLY (before any herdr usage in run_agent)"
|
||||
- **결과**: `herdr has-session -t`가 shim 경유로 `_real_herdr agent get "$sess"`로 번역되어 정상 동작
|
||||
|
||||
### 버그 2: HERDR_SERVER_NAME 자동 해석 누락 — 격리 세션 위임 실패
|
||||
- **문제**: 호출자가 `HERDR_SERVER_NAME`를 수동 export하길 기대 → 격리된 세션 위임 시 default 세션에서 찾아 실패
|
||||
- **수정**: `export HERDR_SERVER_NAME="$(resolve_herdr_workspace "$sess")"` 자동 해석 추가 (라인 443) + 주석 "Auto-resolve isolation the same way resume/stop/create do"
|
||||
- **이전 코드**: `local _herdr="herdr"; if [ -n "${HERDR_SERVER_NAME:-}" ]; then _herdr="herdr -L $HERDR_SERVER_NAME"; fi` — 수동 env 의존 + `herdr -L` 직접 사용(shim 경유 아님)
|
||||
- **신규 코드**: 자동 해석 + `herdr has-session -t` (shim이 `-L`를 내부 처리)
|
||||
|
||||
### 버그 3: 안내 메시지 'session attach' → 'agent attach'
|
||||
- **문제**: 마지막 안내가 `herdr session attach $sess` — `session attach`는 서버 전체 단위 명령이라 개별 에이전트 이름으로 못 찾음
|
||||
- **수정**: `HERDR_SERVER_NAME=$HERDR_SERVER_NAME herdr agent attach $sess` (라인 513) + 주석 "`session attach` operates on whole herdr *sessions*, not an individual agent"
|
||||
- **추가**: `HERDR_SERVER_NAME` 인라인 포함 — source 안 된 fresh shell에서도 copy-paste 가능
|
||||
|
||||
---
|
||||
|
||||
## 2. 검증 관점 (a): 실제 herdr CLI 문법 일치 여부
|
||||
|
||||
### 2.1 ✅ has-session — shim 번역 정확
|
||||
- 실제 herdr v0.7.4에 `has-session` 서브커맨드 **없음** (도움말에 없음)
|
||||
- shim(lib.sh 147-163): `has-session -t <sess>` → `_real_herdr agent get "$sess" >/dev/null 2>&1` — agent 존재 여부로 세션 존재 판정
|
||||
- **실행 검증**: 존재 세션 → exit 0, 비존재 세션 → exit 1 ✅
|
||||
|
||||
### 2.2 ✅ agent attach — 실제 서브커맨드 확인
|
||||
- `herdr agent --help` 출력: `herdr agent attach <target> [--takeover]` — **실제 존재** ✅
|
||||
- `herdr agent attach <target>`는 **개별 에이전트 target**을 받음 — `$sess`(MAM 에이전트 이름)와 일치 ✅
|
||||
|
||||
### 2.3 ✅ session attach vs agent attach 구분 정확
|
||||
- `herdr session --help` 출력: `herdr session attach <name>` — **서버 전체 session** name을 받음
|
||||
- `herdr agent attach <target>` — **개별 에이전트** target을 받음
|
||||
- delegate-job의 `$sess`는 MAM 에이전트 이름(예: `canary-projects-multi-agent-mux-reviewer-cline`) → `agent attach`가 정답 ✅
|
||||
- 수정 전 `session attach $sess`는 에이전트 이름을 session 이름으로 잘못 전달 → 실패 확정 ✅ (버그 재현 논리 타당)
|
||||
|
||||
### 2.4 ✅ HERDR_SERVER_NAME 인라인 메시지
|
||||
---
|
||||
|
||||
## 3. 검증 관점 (b): source 순서 변경 충돌 여부
|
||||
|
||||
### 3.1 ✅ source 위치 — run_agent보다 377줄 앞
|
||||
- `source "$SCRIPT_DIR/../lib.sh"` (라인 31) vs `run_agent()` 정의 (라인 408) — 377줄 선행 ✅
|
||||
- has-session 호출(라인 445) 시점엔 shim 활성화 보장 ✅
|
||||
|
||||
### 3.2 ✅ 다른 함수/변수와 충돌 없음
|
||||
- lib.sh source 시 정의되는 함수: `herdr`(shim), `resolve_herdr_workspace`, `send_keys_safe`, `load_state_json`, `derive_session_name`, `env_python` 등
|
||||
- delegate-job이 이미 사용 중인 함수(`send_keys_safe`, `load_state_json`) — source 순서 변경 후에도 동일 동작 ✅
|
||||
- `pick_python()` (라인 34)는 source 이후 정의 — lib.sh가 `pick_python`에 의존하지 않으므로 순서 충돌 없음 ✅
|
||||
- 라인 26-30 주석이 "Source EARLY" 의도 명시 — 유지보수자에게 경고 ✅
|
||||
|
||||
### 3.3 ✅ 기존 `source` 라인(라인 444) 제거 확인
|
||||
- 6df4b03 diff: `source "$SCRIPT_DIR/../lib.sh"`가 run_agent 내부(구 라인 444)에서 제거되고 상단(라인 31)로 이동 — 중복 source 아님 ✅
|
||||
|
||||
---
|
||||
|
||||
## 4. 검증 관점 (c): HERDR_SERVER_NAME 자동 해석 — 비격리(default) 회귀 여부
|
||||
|
||||
### 4.1 ✅ resolve_herdr_workspace 구현 (lib.sh 550-562)
|
||||
- state JSON에서 session name 매칭 → `herdr_workspace`/`herdr_server` 반환
|
||||
- 매칭 실패 시 `os.environ.get('HERDR_SERVER_NAME', 'default')` fallback
|
||||
- **비격리(default) 경로**: session이 JSON에 없거나 `herdr_workspace` 미설정 → `default` ✅
|
||||
|
||||
### 4.2 ✅ python3 시뮬레이션 검증
|
||||
- **비격리**: `MAM_STATE_JSON='{}' SESSION_NAME='nonexistent'` (HERDR_SERVER_NAME unset) → `default` ✅
|
||||
- **격리**: `MAM_STATE_JSON='{"herdr_sessions":[{"name":"isolated-agent","herdr_workspace":"iso-server"}]}' SESSION_NAME='isolated-agent'` → `iso-server` ✅
|
||||
- **env fallback**: `HERDR_SERVER_NAME=custom_server` → `custom_server` (test_resume_resolve_herdr_workspace_env 검증) ✅
|
||||
|
||||
### 4.3 ✅ 형제 스킬 일관성
|
||||
| 스크립트 | 패턴 |
|
||||
|---------|------|
|
||||
| resume_session.sh:40 | `HERDR_SERVER_NAME="$(resolve_herdr_workspace "$SESSION_NAME")"` |
|
||||
| update_yaml_resumed.sh:36 | `HERDR_SERVER_NAME="$(resolve_herdr_workspace "$SESSION_NAME")"` |
|
||||
| stop_session.sh:85 | `HERDR_SERVER_NAME="$(resolve_herdr_workspace "$SESSION_NAME")"` |
|
||||
| **delegate-job:443 (신규)** | `export HERDR_SERVER_NAME="$(resolve_herdr_workspace "$sess")"` |
|
||||
|
||||
- delegate-job이 형제 스킬과 **동일 패턴** 채택 — 일관성 확보 ✅
|
||||
- 유일한 차이: `export` 추가 — delegate-job은 subprocess(send_keys_safe 등)에 전달 필요 → export 정당 ✅
|
||||
|
||||
### 4.4 ✅ 비격리 회귀 없음
|
||||
- default 세션의 에이전트: `resolve_herdr_workspace`가 `default` 반환 → `HERDR_SERVER_NAME=default` → shim이 `_MAM_SESSION=""`(빈) → `_real_herdr`가 `--session` 없이 호출 → default 서버 사용 ✅
|
||||
- 55개 테스트(깨끗한 환경) 통과 — 비격리 경로 회귀 없음 ✅
|
||||
|
||||
---
|
||||
|
||||
## 5. 추가 검증: 정적 분석 & 테스트
|
||||
|
||||
### 5.1 ✅ bash -n
|
||||
| 파일 | 결과 |
|
||||
|------|------|
|
||||
| `multi-agent-mux-delegate-job` | SYNTAX OK (exit 0) ✅ |
|
||||
| `.agents/skills/lib.sh` | SYNTAX OK (exit 0) ✅ |
|
||||
|
||||
### 5.2 ✅ 테스트 (깨끗한 환경)
|
||||
- `env -u HERDR_SERVER_NAME pytest tests/test_tier1_unit.py tests/test_tier2_component.py`: **55 passed** ✅
|
||||
- **주의**: 리뷰어 세션 환경(`HERDR_SERVER_NAME=multi-agent-mux`)에서 실행 시 `test_resume_resolve_herdr_workspace_default` 실패 — 이는 **테스트 하네스 env 격리 한계**(test가 `env=` 미전달하여 부모 환경 상속), 코드 결함 아님. `env -u HERDR_SERVER_NAME`로 실행 시 통과 ✅
|
||||
|
||||
---
|
||||
|
||||
## 6. 잔여 결함
|
||||
|
||||
### 6.1 ⚠️ test_resume_resolve_herdr_workspace_default env 격리 부족 (LOW, 본 수정 외)
|
||||
- 테스트가 `run_lib_func` 호출 시 `env=` 미전달 → 부모 shell의 `HERDR_SERVER_NAME` 상속
|
||||
- 격리 herdr 세션 내에서 pytest 실행 시 실패(환경 artifact)
|
||||
- **영향**: 본 delegate-job 수정과 무관, 기존 테스트 하네스 한계. CI(깨끗한 env)에서는 통과
|
||||
- **심각도**: LOW — 테스트 격로 보강 권장(`env={}` 명시 또는 `monkeypatch.delenv`)
|
||||
|
||||
### 6.2 ℹ️ 주석 "tmux-compat shim" (INFO)
|
||||
- 라인 27 주석 "turns plain `herdr` into the tmux-compat shim" — tmux 호환성 레퍼런스는 의도된 설명(실제 shim이 tmux 문법을 herdr로 번역)
|
||||
- **심각도**: INFO — 유지
|
||||
|
||||
---
|
||||
|
||||
## 7. 종합 평가
|
||||
|
||||
### 버그 3건 수정 품질
|
||||
1. ✅ **버그 1 (source 순서)**: lib.sh를 라인 31로 조기 이동 — has-session이 shim 경유 `agent get`으로 정상 동작. run_agent보다 377줄 선행, 다른 함수와 충돌 없음
|
||||
2. ✅ **버그 2 (HERDR_SERVER_NAME 자동 해석)**: `resolve_herdr_workspace` 자동 호출 — 형제 스킬(resume/stop)과 동일 패턴, 비격리(default) 회귀 없음, 격리 세션 정상 해석
|
||||
3. ✅ **버그 3 (session attach → agent attach)**: 실제 herdr v0.7.4 도움말로 `agent attach <target>` 존재 확인 — 에이전트 이름 전달 정확, `session attach`는 서버 단위로 부적절
|
||||
|
||||
### 검증 관점 충족
|
||||
- **(a) herdr CLI 문법**: has-session(shim `agent get` 번역), agent attach(실제 서브커맨드), session attach(서버 단위 구분) — 모두 실제 도움말로 확인 ✅
|
||||
- **(b) source 순서 충돌**: 라인 31 조기 source, run_agent(408) 선행, 중복 source 없음, 다른 함수 충돌 없음 ✅
|
||||
- **(c) 비격리 회귀**: resolve_herdr_workspace가 default fallback, 55개 테스트(깨끗한 env) 통과, 형제 스킬 일관성 ✅
|
||||
|
||||
### 잔여 결함 (본 수정 외, LOW/INFO)
|
||||
- LOW 6.1: test env 격리 부족 (본 수정 무관, CI 통과)
|
||||
- INFO 6.2: "tmux-compat shim" 주석 (의도된 설명)
|
||||
|
||||
### 판정 근거
|
||||
3건 버그 수정 모두 실제 herdr v0.7.4 CLI 문법과 정확히 일치(has-session/agent attach/session attach 도움말 직접 확인). source 순서 변경은 run_agent 선행 보장 + 다른 함수 충돌 없음. HERDR_SERVER_NAME 자동 해석은 형제 스킬과 동일 패턴으로 일관성 확보 + 비격리 회귀 없음. 정적 분석 통과, 55개 테스트(깨끗한 환경) 통과. 잔여 결함은 본 수정 외 LOW/INFO로 BLOCKING 아님. 설계 변경/재작업 불필요. PASS.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,46 @@
|
||||
# ✅ deploy/ 배포 설정 tmux→herdr 전환 수정 완료 보고
|
||||
|
||||
**Job ID**: 94ce2fcc (동일 작업: 60af70e4)
|
||||
**Role**: Implementation (Cline)
|
||||
**Plan**: [report-baa15c96.md](.agents/reports/canary-projects-multi-agent-mux-planner-reviewer-claude/report-baa15c96.md)
|
||||
**Brief**: [.mam/deploy_patch_brief.md](.mam/deploy_patch_brief.md)
|
||||
|
||||
## 적용된 변경 (5 파일, 7 에디트)
|
||||
|
||||
### 1. deploy/install_mam.sh (기능 파손 수정 — 우선순위 1)
|
||||
- **L224**: `--tmux-server multi-agent-mux` → `--herdr-server multi-agent-mux`
|
||||
- 근거: `create_session.sh:63`의 실제 플래그(`--herdr-server`) — 기존 `--tmux-server`는 create_session.sh가 거부(unknown arg, exit 2)하여 복붙 시 즉시 실패하는 기능 파손
|
||||
- **L227**: `tmux -L multi-agent-mux attach -t <session_name>` → `HERDR_SERVER_NAME=multi-agent-mux herdr agent attach <session_name>`
|
||||
- 근거: `create_session.sh:328`이 YAML `attach_command`로 방출하는 canonical 형식과 1:1 일치 (`HERDR_SERVER_NAME={server} herdr agent attach {name}`)
|
||||
|
||||
### 2. deploy/install.sh
|
||||
- **L31**: `check_cmd tmux` → `check_cmd herdr` (올바른 의존성 진단)
|
||||
- **L249**: `TMUX_SERVER_NAME=default` → `HERDR_SERVER_NAME=default` (소비처 0인 죽은 설정 → 실제 소비 변수)
|
||||
|
||||
### 3. deploy/README.md
|
||||
- **L9**: `checks system requirements (\`tmux\`, \`python3\`)` → `\`herdr\`, \`python3\``
|
||||
|
||||
### 4. deploy/INSTALL.md
|
||||
- **L21**: 진단 목록 `tmux` → `herdr`
|
||||
- **L37**: 의존성 진단 `tmux` → `herdr`
|
||||
- **L69**: `tmux가 소멸한 경우` → `herdr 서버가 소멸한 경우`
|
||||
|
||||
### 5. deploy/plugin.json
|
||||
- **L3**: `Backplane on Tmux & MQTT.` → `Backplane on Herdr & MQTT.`
|
||||
|
||||
## 검증 결과 (DoD)
|
||||
|
||||
1. ✅ **잔존 스윕**: `grep -rin tmux deploy/` → **0건** (대소문자 무시)
|
||||
2. ✅ **정적**: `bash -n` install_mam.sh/install.sh 모두 exit 0; `shellcheck -S warning` exit 0 (신규 경고 0건); `python3 -m json.tool plugin.json` VALID
|
||||
3. ✅ **기능**: `--herdr-server`가 `create_session.sh:35/63`의 실제 플래그 확인; attach 형식이 `create_session.sh:328` canonical 형식과 1:1 일치
|
||||
4. ✅ **회귀**: `env -u HERDR_SERVER_NAME pytest tests/test_tier1_unit.py tests/test_tier2_component.py` → **55 passed**
|
||||
|
||||
## 계획서 교정사항 반영
|
||||
- ~~`HERDR_SESSION_NAME`~~ → **`HERDR_SERVER_NAME`** (스킬 전체가 소비하는 유일한 변수명) ✅
|
||||
- ~~`--herdr-session`~~ → **`--herdr-server`** (create_session.sh:63의 유일한 플래그) ✅
|
||||
|
||||
## 제외 항목 (계획서 §1-6)
|
||||
- remove.sh / update.sh: tmux/kill 로직 없음 → 수정 불요 ✅
|
||||
- generate-env.sh / gitea-ci.yml: tmux 참조 없음 → 수정 불요 ✅
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,58 @@
|
||||
# ✅ MULTI_AGENT_RULES tmux→herdr 전환 수정 완료 보고
|
||||
|
||||
**Job ID**: b92c34d7
|
||||
**Role**: Implementation (Cline)
|
||||
**Brief**: [.mam/rules_patch_brief.md](.mam/rules_patch_brief.md)
|
||||
**Target Files**: `.agents/MULTI_AGENT_RULES.md` (EN) + `.agents/MULTI_AGENT_RULES.ko.md` (KO)
|
||||
|
||||
## 적용된 변경 (2 파일, 13 에디트 — 정확한 1:1 치환)
|
||||
|
||||
### 1. MULTI_AGENT_RULES.md (EN) — 6 에디트
|
||||
| 행 | 변경 전 | 변경 후 |
|
||||
|---|---|---|
|
||||
| 3 | `Tmux-based multi-agent orchestration` | `Herdr-based multi-agent orchestration` |
|
||||
| 57 | `TMUX monitoring states` | `Herdr monitoring states` |
|
||||
| 116 | `running in TMUX environments` | `running in Herdr environments` |
|
||||
| 124 | `via tmux input buffers` | `via herdr input buffers` |
|
||||
| 129 | `<tmux_session_name>` | `<herdr_session_name>` |
|
||||
| 131 | `<tmux_session_name>` | `<herdr_session_name>` |
|
||||
|
||||
### 2. MULTI_AGENT_RULES.ko.md (KO) — 7 에디트
|
||||
| 행 | 변경 전 | 변경 후 |
|
||||
|---|---|---|
|
||||
| 3 | `Tmux 기반 멀티 에이전트` | `Herdr 기반 멀티 에이전트` |
|
||||
| 57 | `TMUX 모니터링 상태` | `Herdr 모니터링 상태` |
|
||||
| 116 | `TMUX 환경에서 실행되는` | `Herdr 환경에서 실행되는` |
|
||||
| 122 | `TMUX `send-keys`나 입력 버퍼` | `herdr `send-keys`나 입력 버퍼` |
|
||||
| 124 | `tmux 입력을 통해` | `herdr 입력을 통해` |
|
||||
| 129 | `<tmux_session_name>` | `<herdr_session_name>` |
|
||||
| 131 | `<tmux_session_name>` | `<herdr_session_name>` |
|
||||
|
||||
## 치환/보완 가이드 대응표 (브리프 §치환/보완 가이드)
|
||||
| 브리프 지시 | 적용 | 비고 |
|
||||
|---|---|---|
|
||||
| `Tmux-based` → `Herdr-based` | ✅ EN L3, KO L3 | |
|
||||
| `TMUX monitoring states` → `Herdr monitoring states` | ✅ EN L57, KO L57 | |
|
||||
| `running in TMUX environments` → `running in Herdr environments` | ✅ EN L116, KO L116 | |
|
||||
| `via tmux input buffers` → `via herdr input buffers` | ✅ EN L124 | KO L124 `tmux 입력` → `herdr 입력` (맥락 동일) |
|
||||
| `tmux_session_name` → `herdr_session_name` | ✅ EN L129/L131, KO L129/L131 | 경로 placeholder 4곳 |
|
||||
| KO: `herdr`/`herdr 서버`로 개정 | ✅ KO L3/L57/L116/L122/L124 | |
|
||||
|
||||
## 검증 결과 (DoD)
|
||||
|
||||
1. ✅ **잔존 스윕**: `grep -in 'tmux\|TMUX'` 두 파일 → **0건** (대소문자 무시)
|
||||
2. ✅ **마크다운 포맷팅**: 헤딩 개수 EN 17 / KO 17 (변경 전후 동일 — 레이아웃 유지); diff 13 insertions / 13 deletions (정확한 1:1 치환, 구조 변경 없음)
|
||||
3. ✅ **herdr 치환 확인**: EN 6곳, KO 7곳 herdr/Herdr 표기 존재
|
||||
4. ✅ **회귀**: `env -u HERDR_SERVER_NAME pytest tests/test_tier1_unit.py tests/test_tier2_component.py` → **55 passed**
|
||||
|
||||
## 관찰 사항 (INFO, 본 브리프 범위 외)
|
||||
|
||||
### INFO: `capture-pane -S -200` 명령어 예시 잔존
|
||||
- 스냅샷 3대 규칙 섹션(EN L117-118, KO L117-118)에 `capture-pane -S -200` 명령어 예시가 잔존
|
||||
- 이는 tmux 명령어이나 본 브리프의 치환 대상(`tmux`/`TMUX`/`tmux_session_name` 텍스트 표기)이 아님 — "tmux" 단어를 포함하지 않으므로 grep 스윕에 걸리지 않음
|
||||
- **심각도**: INFO — 본 브리프 범위 밖. 후속 작업에서 `herdr agent read --lines 200` 등 herdr 네이티브 명령으로 교체 권장 (별도 결정 사항)
|
||||
|
||||
## 제외 항목
|
||||
- 본 브리프는 지침 문서 2종만 대상 — 다른 파일은 수정하지 않음 ✅
|
||||
|
||||
[VERDICT: PASS]
|
||||
+63
-29
@@ -58,12 +58,6 @@ _resolve_real_herdr_path() {
|
||||
_init_herdr_isolation() {
|
||||
local wrapper_dir="$WORKSPACE_ROOT/.mam/shim"
|
||||
mkdir -p "$wrapper_dir"
|
||||
if [ -x "$wrapper_dir/herdr" ]; then
|
||||
if [[ ":$PATH:" != *":$wrapper_dir:"* ]]; then
|
||||
export PATH="$wrapper_dir:$PATH"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
local tmp_file
|
||||
tmp_file=$(mktemp "$wrapper_dir/herdr.XXXXXX")
|
||||
@@ -123,7 +117,7 @@ except Exception:
|
||||
# Headless bootstrap avoids herdr's "nested herdr is disabled" guard that
|
||||
# blocks a normal interactive `herdr --session <name>` launch from inside
|
||||
# an existing herdr pane (which is how MAM's own agents usually run).
|
||||
nohup "$REAL_HERDR" --session "$_MAM_SESSION" server >/dev/null 2>&1 &
|
||||
setsid "$REAL_HERDR" --session "$_MAM_SESSION" server </dev/null >/dev/null 2>&1 &
|
||||
disown 2>/dev/null || true
|
||||
for _mam_wait_i in $(seq 1 40); do
|
||||
[ -S "${HOME:-$HOME_DIR}/.config/herdr/sessions/$_MAM_SESSION/herdr.sock" ] && break
|
||||
@@ -221,17 +215,47 @@ print('\t'.join(env_flags) + '\n' + ' '.join(binary_tokens))
|
||||
# fresh workspace inside whatever session is active (default or
|
||||
# isolated) and place the agent there. No cross-session label lookup
|
||||
# needed since `--session` already scopes everything.
|
||||
ws_id=$(_real_herdr workspace create --cwd "${ws:-.}" --no-focus 2>/dev/null | python3 -c "
|
||||
import sys, json
|
||||
# Resolve ws_id and split direction:
|
||||
# - If we have up to 3 agents, split 'right' (columns)
|
||||
# - If we have 4 or more agents, split 'down' (rows)
|
||||
res=$(_real_herdr workspace list 2>/dev/null | WS_CWD="${ws:-.}" python3 -c "
|
||||
import sys, json, os
|
||||
try:
|
||||
d = json.loads(sys.stdin.read())
|
||||
print(d.get('result', {}).get('workspace', {}).get('workspace_id', ''))
|
||||
target_label = os.path.basename(os.path.abspath(os.environ.get('WS_CWD', '.')))
|
||||
wlist = json.loads(sys.stdin.read()).get('result', {}).get('workspaces', [])
|
||||
match = None
|
||||
for w in wlist:
|
||||
if w.get('label') == target_label:
|
||||
match = w
|
||||
break
|
||||
if not match and wlist:
|
||||
match = wlist[0]
|
||||
if match:
|
||||
ws_id = match.get('workspace_id', 'w1')
|
||||
pane_count = match.get('pane_count', 0)
|
||||
split_dir = 'right' if pane_count < 3 else 'down'
|
||||
print(f'{ws_id}\t{split_dir}')
|
||||
except Exception:
|
||||
pass
|
||||
")
|
||||
ws_id="${ws_id:-w1}"
|
||||
ws_id=$(echo "$res" | cut -f1)
|
||||
split_dir=$(echo "$res" | cut -f2)
|
||||
|
||||
eval "_real_herdr agent start \"$name\" --workspace \"$ws_id\" --cwd \"${ws:-.}\" $env_flags -- $final_cmd"
|
||||
if [ -z "$ws_id" ]; then
|
||||
ws_id=$(_real_herdr workspace create --cwd "${ws:-.}" --no-focus 2>/dev/null | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
d = json.loads(sys.stdin.read()).get('result', {}).get('workspace', {}).get('workspace_id', '')
|
||||
print(d)
|
||||
except Exception:
|
||||
pass
|
||||
")
|
||||
split_dir="right"
|
||||
fi
|
||||
ws_id="${ws_id:-w1}"
|
||||
split_dir="${split_dir:-right}"
|
||||
|
||||
eval "_real_herdr agent start \"$name\" --workspace \"$ws_id\" --split \"$split_dir\" --cwd \"${ws:-.}\" $env_flags -- $final_cmd"
|
||||
;;
|
||||
kill-session)
|
||||
sess=""
|
||||
@@ -339,7 +363,7 @@ except Exception:
|
||||
esac
|
||||
;;
|
||||
capture-pane)
|
||||
sess=""
|
||||
sess="" src="visible" lines="100"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-t)
|
||||
@@ -350,10 +374,22 @@ except Exception:
|
||||
sess="$2"
|
||||
shift 2
|
||||
;;
|
||||
-S)
|
||||
# tmux `-S -N` (start N lines above the visible viewport) maps to
|
||||
# herdr `--source recent --lines N`. Non-negative or non-numeric
|
||||
# values keep the default visible viewport read.
|
||||
if [ $# -ge 2 ] && [[ "$2" =~ ^-[0-9]+$ ]]; then
|
||||
src="recent"
|
||||
lines="${2#-}"
|
||||
shift 2
|
||||
else
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
_real_herdr agent read "$sess" --source visible --lines 100 2>/dev/null || true
|
||||
_real_herdr agent read "$sess" --source "$src" --lines "$lines" 2>/dev/null || true
|
||||
;;
|
||||
send-keys)
|
||||
sess="" key=""
|
||||
@@ -447,7 +483,11 @@ except Exception:
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$tmp_file"
|
||||
mv -f "$tmp_file" "$wrapper_dir/herdr"
|
||||
if [ -f "$wrapper_dir/herdr" ] && cmp -s "$tmp_file" "$wrapper_dir/herdr"; then
|
||||
rm -f "$tmp_file"
|
||||
else
|
||||
mv -f "$tmp_file" "$wrapper_dir/herdr"
|
||||
fi
|
||||
if [[ ":$PATH:" != *":$wrapper_dir:"* ]]; then
|
||||
export PATH="$wrapper_dir:$PATH"
|
||||
fi
|
||||
@@ -467,9 +507,9 @@ herdr() {
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# resolve_herdr_server <session_name>
|
||||
# resolve_herdr_session <session_name>
|
||||
#
|
||||
# Query agent-sessions.yaml to find the herdr_server associated with a session.
|
||||
# Query agent-sessions.yaml to find the herdr_session associated with a session.
|
||||
# Fallback to HERDR_SERVER_NAME or 'default' if not registered or field is missing.
|
||||
# Prints the resolved server name on stdout.
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -513,13 +553,7 @@ print(json.dumps(d, ensure_ascii=False))
|
||||
PYEOF
|
||||
}
|
||||
|
||||
# Despite the name (kept for caller compatibility — resume/stop/update_yaml_resumed
|
||||
# all do `HERDR_SERVER_NAME="$(resolve_herdr_workspace "$SESSION_NAME")"`), this
|
||||
# returns the isolated herdr *session* name to use for this MAM session row, not
|
||||
# a workspace id. Real isolation is `--session <name>` (see `_MAM_SESSION` in the
|
||||
# generated wrapper) — a workspace label match provides no actual isolation
|
||||
# since agent/pane commands are server-global regardless of workspace.
|
||||
resolve_herdr_workspace() {
|
||||
resolve_herdr_session() {
|
||||
local session_name="$1"
|
||||
MAM_STATE_JSON="$(load_state_json)" SESSION_NAME="$session_name" python3 -c "
|
||||
import sys, os, json
|
||||
@@ -527,7 +561,7 @@ name = os.environ['SESSION_NAME']
|
||||
d = json.loads(os.environ.get('MAM_STATE_JSON', '{}'))
|
||||
for s in d.get('herdr_sessions', []):
|
||||
if s.get('name') == name:
|
||||
print(s.get('herdr_workspace') or s.get('herdr_server') or 'default')
|
||||
print(s.get('herdr_session') or s.get('herdr_workspace') or s.get('herdr_server') or 'default')
|
||||
sys.exit(0)
|
||||
print(os.environ.get('HERDR_SERVER_NAME', 'default'))
|
||||
"
|
||||
@@ -1554,7 +1588,7 @@ send_keys_safe() {
|
||||
_sks_herdr send-keys -t "$sess" C-m
|
||||
return 0
|
||||
fi
|
||||
sleep 0.5
|
||||
sleep "${SKS_PASTE_SLEEP:-1.5}"
|
||||
local pane_content was_popup=0
|
||||
pane_content=$(_pane_capture "$sess")
|
||||
if printf '%s\n' "$pane_content" | grep -Eq "Pasted text|paste again to expand"; then
|
||||
@@ -1563,7 +1597,7 @@ send_keys_safe() {
|
||||
# lines at the terminal width (and some TUIs add indentation on the
|
||||
# continuation line too), which can split/reformat the marker across two
|
||||
# visual lines and make a literal grep miss it even though the paste landed.
|
||||
elif ! printf '%s' "$pane_content" | tr -d '[:space:]' | grep -Fq "$marker_norm"; then
|
||||
elif [ -n "$marker_norm" ] && ! printf '%s' "$pane_content" | tr -d '[:space:]' | grep -Fq "$marker_norm"; then
|
||||
echo "send_keys_safe: paste not visible ($sess)" >&2
|
||||
return 3
|
||||
fi
|
||||
@@ -1578,7 +1612,7 @@ send_keys_safe() {
|
||||
if printf '%s\n' "$cur_content" | grep -Eq "● |✽ |[A-Za-z]+ing…|[A-Za-z]+ing\.\.\.|esc to interrupt"; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$was_popup" = "0" ] && ! _pane_tail "$sess" 3 | tr -d '[:space:]' | grep -Fq "$marker_norm" && [ "$cur_content" != "$pre_submit" ]; then
|
||||
if [ "$was_popup" = "0" ] && { [ -z "$marker_norm" ] || ! _pane_tail "$sess" 3 | tr -d '[:space:]' | grep -Fq "$marker_norm"; } && [ "$cur_content" != "$pre_submit" ]; then
|
||||
return 0
|
||||
elif [ "$was_popup" = "1" ] && \
|
||||
! printf '%s\n' "$cur_content" | grep -Eq "Pasted text|paste again to expand" && \
|
||||
|
||||
@@ -310,7 +310,7 @@ entry = {
|
||||
'role': role,
|
||||
'herdr_session_created_at': os.environ['NOW_ISO'],
|
||||
'herdr_session_epoch': int(epoch) if epoch.isdigit() else 0,
|
||||
'herdr_server': server_name,
|
||||
'herdr_session': server_name,
|
||||
'delegate_job_id': os.environ.get('DELEGATE_JOB_ID', '') or None,
|
||||
'pane': {
|
||||
'index': 0,
|
||||
|
||||
@@ -138,6 +138,19 @@ cmd_submit() {
|
||||
- **Timeout**: $TIMEOUT s (Idle: $IDLE_TIMEOUT s)
|
||||
- **Output Report Path**: .mam/jobs/$JOB_ID/$AGENT-reports/report-final.md
|
||||
|
||||
## 🔔 Execution Commands
|
||||
On start run:
|
||||
python3 .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py --registry-dir .mam/jobs --job $JOB_ID --event started
|
||||
|
||||
On progress (optional):
|
||||
python3 .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py --registry-dir .mam/jobs --job $JOB_ID --event progress --detail '<short status>'
|
||||
|
||||
On success run:
|
||||
python3 .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py --registry-dir .mam/jobs --job $JOB_ID --event completed --detail '<one-line summary>'
|
||||
|
||||
On failure run:
|
||||
python3 .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py --registry-dir .mam/jobs --job $JOB_ID --event error --detail '<one-line reason>'
|
||||
|
||||
## 🔎 Task Description
|
||||
$PROMPT
|
||||
EOF
|
||||
@@ -184,7 +197,8 @@ EOF
|
||||
# an id from an earlier session is the #1 reason a delegated job sits idle and
|
||||
# times out (see SKILL.md "Wrong job_id propagated to the agent"). We make the
|
||||
# freshness explicit in the instruction header.
|
||||
local instructions="Your job_id is \"$JOB_ID\". Detailed task requirements, instructions, and target output paths are documented in the task brief file at: .mam/jobs/$JOB_ID/brief.md. Please READ and follow .mam/jobs/$JOB_ID/brief.md to complete your work. Commands: start='$pub --event started', success='$pub --event completed --detail <summary>', error='$pub --event error --detail <reason>'."
|
||||
# Keep this short and ASCII-only to prevent paste/wrap rendering issues in CLI REPLs.
|
||||
local instructions="Job $JOB_ID: Read .mam/jobs/$JOB_ID/brief.md and complete the task."
|
||||
|
||||
run_agent "$JOB_ID" "$instructions"
|
||||
|
||||
@@ -440,7 +454,7 @@ run_agent() {
|
||||
# the caller having exported HERDR_SERVER_NAME by hand. This is what lets
|
||||
# delegation reach an agent living in an isolated herdr session (e.g. one
|
||||
# created with --herdr-server) instead of silently looking in "default".
|
||||
export HERDR_SERVER_NAME="$(resolve_herdr_workspace "$sess")"
|
||||
export HERDR_SERVER_NAME="$(resolve_herdr_session "$sess")"
|
||||
|
||||
if ! herdr has-session -t "$sess" 2>/dev/null; then
|
||||
echo "ERROR: 에이전트 세션 '$sess'이 존재하지 않습니다. 작업을 위임하기 전에 먼저 에이전트 세션을 기동해 주세요." >&2
|
||||
|
||||
@@ -121,7 +121,7 @@ _changed = False
|
||||
for s in d.get('herdr_sessions', []):
|
||||
if s.get('delegate_job_id') == _jid and s.get('status') == 'running':
|
||||
_name = s.get('name')
|
||||
_srv = s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
_srv = s.get('herdr_session') or s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
if _event == 'completed':
|
||||
s['delegate_job_id'] = None
|
||||
print('MQTT Monitor: job completed on ' + str(_name) + ' — session kept alive', flush=True)
|
||||
@@ -131,7 +131,8 @@ for s in d.get('herdr_sessions', []):
|
||||
s['terminated_at'] = _now.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
s['terminated_at_epoch'] = int(_now.timestamp())
|
||||
s['termination_mode'] = 'auto-detected (MQTT ' + _event + ')'
|
||||
_cmd = ['herdr'] + (['-L', _srv] if _srv != 'default' else []) + ['kill-session', '-t', _name]
|
||||
_shim = os.path.join(os.environ.get('WORKSPACE_ROOT', os.getcwd()), '.mam/shim/herdr')
|
||||
_cmd = [_shim] + (['-L', _srv] if _srv != 'default' else []) + ['kill-session', '-t', _name]
|
||||
subprocess.run(_cmd, capture_output=True)
|
||||
print('MQTT Monitor: terminated + killed ' + str(_name) + ' on ' + str(_srv) + ' due to MQTT ' + _event, flush=True)
|
||||
_changed = True
|
||||
@@ -309,6 +310,8 @@ import yaml
|
||||
yaml_path = os.environ['YAML_PATH']
|
||||
home = os.environ['HOME_DIR']
|
||||
claude_project_dir = os.environ.get('CLAUDE_PROJECT_DIR', f"{home}/.claude/projects")
|
||||
workspace_root = os.environ.get('WORKSPACE_ROOT', os.getcwd())
|
||||
shim_herdr = os.path.join(workspace_root, '.mam/shim/herdr')
|
||||
|
||||
now_iso = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
|
||||
@@ -342,12 +345,12 @@ unique_servers = {'default'}
|
||||
if 'HERDR_SERVER_NAME' in os.environ:
|
||||
unique_servers.add(os.environ['HERDR_SERVER_NAME'])
|
||||
for s in d.get('herdr_sessions', []):
|
||||
srv = s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
srv = s.get('herdr_session') or s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
unique_servers.add(srv)
|
||||
|
||||
try:
|
||||
for srv in sorted(unique_servers):
|
||||
cmd = ['herdr']
|
||||
cmd = [shim_herdr]
|
||||
if srv != 'default':
|
||||
cmd += ['-L', srv]
|
||||
cmd += ['ls', '-F', '#{session_name}|#{session_created}']
|
||||
@@ -369,7 +372,7 @@ except Exception:
|
||||
|
||||
def pane_meta(session, srv):
|
||||
try:
|
||||
cmd = ['herdr']
|
||||
cmd = [shim_herdr]
|
||||
if srv != 'default':
|
||||
cmd += ['-L', srv]
|
||||
cmd += ['list-panes', '-t', session, '-F',
|
||||
@@ -396,7 +399,7 @@ if herdr_confirmed:
|
||||
# (없으면 herdr-dead stopped 세션을 'terminated' 로 덮어써 resumable 플래그가 소실됨)
|
||||
if s.get('status') in ('terminated', 'archived', 'stopped'):
|
||||
continue
|
||||
srv = s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
srv = s.get('herdr_session') or s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
if (name, srv) not in alive_set:
|
||||
s['status'] = 'terminated'
|
||||
s['terminated_at'] = now_iso
|
||||
|
||||
@@ -37,7 +37,7 @@ if [ -z "$UUID" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HERDR_SERVER_NAME="$(resolve_herdr_workspace "$SESSION_NAME")"
|
||||
HERDR_SERVER_NAME="$(resolve_herdr_session "$SESSION_NAME")"
|
||||
export HERDR_SERVER_NAME
|
||||
|
||||
# 2. If herdr is alive, print warning or attach.
|
||||
|
||||
@@ -33,7 +33,7 @@ done
|
||||
[ -n "$UUID" ] || { echo "ERROR: --uuid required" >&2; exit 2; }
|
||||
[ -f "$AGENT_SESSIONS_YAML" ] || { echo "ERROR: $AGENT_SESSIONS_YAML not found" >&2; exit 1; }
|
||||
|
||||
HERDR_SERVER_NAME="$(resolve_herdr_workspace "$SESSION_NAME")"
|
||||
HERDR_SERVER_NAME="$(resolve_herdr_session "$SESSION_NAME")"
|
||||
export HERDR_SERVER_NAME
|
||||
|
||||
# --agent 미지정 시 이름 suffix 로 fallback (P1-F: 가능하면 --agent 명시)
|
||||
|
||||
@@ -93,7 +93,7 @@ def get_job_status(s):
|
||||
sessions_detail = []
|
||||
for s in d.get('herdr_sessions', []):
|
||||
name = s.get('name', '?')
|
||||
server = s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
server = s.get('herdr_session') or s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
jid, jstatus = get_job_status(s)
|
||||
pane = s.get('pane') or {}
|
||||
sessions_detail.append({
|
||||
@@ -197,7 +197,7 @@ if not sessions:
|
||||
print("(no sessions registered)")
|
||||
for s in sessions:
|
||||
name = s.get('name', '?')
|
||||
server = s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
server = s.get('herdr_session') or s.get('herdr_workspace') or s.get('herdr_server') or 'default'
|
||||
status = s.get('status', '?')
|
||||
herdr = 'alive' if f"{name}|{server}" in alive else 'dead'
|
||||
cmd = (s.get('pane') or {}).get('cmd', '?')
|
||||
|
||||
@@ -82,7 +82,7 @@ if [ "$PURGE" = "1" ]; then
|
||||
trap 'rm -f "$WORKSPACE_ROOT/.mam/purging-$SESSION_NAME"' EXIT
|
||||
fi
|
||||
|
||||
HERDR_SERVER_NAME="$(resolve_herdr_workspace "$SESSION_NAME")"
|
||||
HERDR_SERVER_NAME="$(resolve_herdr_session "$SESSION_NAME")"
|
||||
export HERDR_SERVER_NAME
|
||||
|
||||
# --agent 미지정 시 이름 suffix 로 fallback (P1-F)
|
||||
|
||||
+3
-3
@@ -18,7 +18,7 @@ MAM 스킬 및 스크립트들은 호스트 시스템의 다음 도구들에 의
|
||||
|
||||
## 2. 🚀 자동 설치 방법
|
||||
|
||||
MAM의 자동 설치 스크립트(`deploy/install_mam.sh`)를 사용하여 10초 만에 필요한 규칙과 라이프사이클 툴킷을 타겟 프로젝트에 이식할 수 있습니다. 스크립트는 실행 시 자동으로 시스템의 `tmux`, `python3`, `rsync`, `uuidgen` 및 필수 파이썬 모듈들을 진단합니다.
|
||||
MAM의 자동 설치 스크립트(`deploy/install_mam.sh`)를 사용하여 10초 만에 필요한 규칙과 라이프사이클 툴킷을 타겟 프로젝트에 이식할 수 있습니다. 스크립트는 실행 시 자동으로 시스템의 `herdr`, `python3`, `rsync`, `uuidgen` 및 필수 파이썬 모듈들을 진단합니다.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **설치 전제조건**: MAM 스킬을 타겟 프로젝트에 설치하려면 **먼저 MAM 레포지토리가 로컬 머신에 clone 되어 있어야 합니다.**
|
||||
@@ -34,7 +34,7 @@ $ bash deploy/install_mam.sh --target /path/to/your/project --force
|
||||
```
|
||||
|
||||
### 설치 스크립트가 수행하는 작업:
|
||||
1. **의존성 진단**: 시스템에 `tmux`, `python3`, `rsync`, `uuidgen` CLI 바이너리와 파이썬 `pyyaml`/`sqlite3` 모듈이 설치되어 있는지 확인합니다.
|
||||
1. **의존성 진단**: 시스템에 `herdr`, `python3`, `rsync`, `uuidgen` CLI 바이너리와 파이썬 `pyyaml`/`sqlite3` 모듈이 설치되어 있는지 확인합니다.
|
||||
2. **규칙 및 스킬 복제**: 오케스트레이션 가이드(`.agents/` 하위 전체)를 타겟 프로젝트 하위로 이식합니다.
|
||||
3. **지침 전파**: 에이전트가 로드하고 복종할 행동 지침 문서(`AGENTS.md`)를 프로젝트 루트에 복사합니다.
|
||||
4. **형상 제외 설정**: 세션 DB 및 격리 캐시 저장소인 `.mam/` 디렉토리를 타겟 프로젝트의 `.gitignore` 에 자동 주입하여 불필요한 형상 관리를 방지합니다.
|
||||
@@ -66,7 +66,7 @@ $ herdr session attach my-project-dev-claude
|
||||
* **화면 탈출**: 대화 중 세션을 유지한 채 터미널로 돌아오려면 `Ctrl + B`를 누른 뒤 `D` 키를 차례로 입력합니다.
|
||||
|
||||
### 3) 에이전트 상태 복원 (Resume)
|
||||
세션이 중지되었거나, 호스트 재기동으로 tmux가 소멸한 경우에도 이전 대화 ID 및 격리 디렉토리를 원자적으로 이어받아 다시 기동할 수 있습니다.
|
||||
세션이 중지되었거나, 호스트 재기동으로 herdr 서버가 소멸한 경우에도 이전 대화 ID 및 격리 디렉토리를 원자적으로 이어받아 다시 기동할 수 있습니다.
|
||||
```bash
|
||||
# 1단계: 복원 대상 세션의 UUID 자동 조회 (DB/YAML 레지스트리 기반)
|
||||
$ WORKSPACE="/path/to/your/project"
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ This directory contains packaging templates and installation scripts to deploy t
|
||||
|
||||
## 📁 Deployment Directory Structure
|
||||
|
||||
* **`install.sh`**: A self-contained, idempotent remote shell installer (via curl) that checks system requirements (`tmux`, `python3`), detects NFS/network filesystem mounts, sets up a local python virtual environment (`.venv`), and initializes environment configuration (`.env`).
|
||||
* **`install.sh`**: A self-contained, idempotent remote shell installer (via curl) that checks system requirements (`herdr`, `python3`), detects NFS/network filesystem mounts, sets up a local python virtual environment (`.venv`), and initializes environment configuration (`.env`).
|
||||
* **`install_mam.sh`**: A local-clone installer that copies rules/skills (`.agents/`), `AGENTS.md`, and sets up environment bootstrap on target projects.
|
||||
* **`generate-env.sh`**: Environment configuration bootstrap helper.
|
||||
* **`INSTALL.md`**: Detailed installation and quick-start user manual.
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ check_cmd() {
|
||||
fi
|
||||
}
|
||||
|
||||
check_cmd tmux
|
||||
check_cmd herdr
|
||||
check_cmd python3
|
||||
|
||||
# Verify Python Version
|
||||
@@ -246,7 +246,7 @@ MQTT_BROKER=broker.hivemq.com
|
||||
MQTT_PORT=1883
|
||||
MQTT_TLS=0
|
||||
MQTT_CLIENT_ID_PREFIX=mam-agent
|
||||
TMUX_SERVER_NAME=default
|
||||
HERDR_SERVER_NAME=default
|
||||
EOF
|
||||
chmod 0600 "$ENV_FILE"
|
||||
echo "✅ Config file .env initialized with chmod 0600."
|
||||
|
||||
@@ -221,10 +221,10 @@ cat <<EOF
|
||||
1. Initialize a new isolated session:
|
||||
$ bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \\
|
||||
--workspace "$TARGET_DIR" --agent claude --role developer --isolate \\
|
||||
--tmux-server multi-agent-mux
|
||||
--herdr-server multi-agent-mux
|
||||
|
||||
2. Attach to the running session:
|
||||
$ tmux -L multi-agent-mux attach -t <session_name>
|
||||
$ HERDR_SERVER_NAME=multi-agent-mux herdr agent attach <session_name>
|
||||
|
||||
3. Gracefully stop the session:
|
||||
$ bash .agents/skills/multi-agent-mux-stop/scripts/stop_session.sh \\
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "multi-agent-mux",
|
||||
"description": "Multi-Agent Orchestration & Messaging Backplane on Tmux & MQTT.",
|
||||
"description": "Multi-Agent Orchestration & Messaging Backplane on Herdr & MQTT.",
|
||||
"disabled": false
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages: {}
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
@@ -124,7 +124,7 @@ d['herdr_sessions'] = [{{
|
||||
'name': '{session_name}',
|
||||
'status': 'running',
|
||||
'role': 'Creator',
|
||||
'herdr_workspace': 'my-custom-workspace',
|
||||
'herdr_session': 'my-custom-workspace',
|
||||
'pane': {{
|
||||
'cwd': 'WS_PLACEHOLDER',
|
||||
'pid': 7777,
|
||||
|
||||
@@ -104,15 +104,15 @@ def test_create_validate_env_key(mam_sandbox):
|
||||
# FEATURE 2: Resume Session (6 Test Cases)
|
||||
# ==============================================================================
|
||||
|
||||
def test_resume_resolve_herdr_workspace_default(mam_sandbox):
|
||||
"""Test resolve_herdr_workspace fallback behavior when session is not in YAML."""
|
||||
res = run_lib_func(mam_sandbox, "resolve_herdr_workspace", "non-existent-session")
|
||||
def test_resume_resolve_herdr_session_default(mam_sandbox):
|
||||
"""Test resolve_herdr_session fallback behavior when session is not in YAML."""
|
||||
res = run_lib_func(mam_sandbox, "resolve_herdr_session", "non-existent-session")
|
||||
assert res.returncode == 0
|
||||
assert res.stdout.strip() == "default"
|
||||
|
||||
def test_resume_resolve_herdr_workspace_env(mam_sandbox):
|
||||
"""Test resolve_herdr_workspace fallback to HERDR_SERVER_NAME env var."""
|
||||
res = run_lib_func(mam_sandbox, "resolve_herdr_workspace", "non-existent-session", env={"HERDR_SERVER_NAME": "custom_server"})
|
||||
def test_resume_resolve_herdr_session_env(mam_sandbox):
|
||||
"""Test resolve_herdr_session fallback to HERDR_SERVER_NAME env var."""
|
||||
res = run_lib_func(mam_sandbox, "resolve_herdr_session", "non-existent-session", env={"HERDR_SERVER_NAME": "custom_server"})
|
||||
assert res.returncode == 0
|
||||
assert res.stdout.strip() == "custom_server"
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ def test_integration_create_options_combination(mam_sandbox, mock_herdr, mock_ag
|
||||
session = sessions[0]
|
||||
|
||||
assert session["role"] == "Creator"
|
||||
assert session["herdr_server"] == "custom_server"
|
||||
assert session.get("herdr_session") == "custom_server"
|
||||
assert session["delegate_job_id"] is not None
|
||||
assert session["status"] == "running"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user