docs(readme): review fixes (38a64423 / c5a8813c / 7d29a620) and README.ko.md in step
- install.sh: what it actually checks; it writes a starter .mam.env with MQTT_BROKER=localhost (broker.hivemq.com is only the unset fallback); install_mam.sh is a separate installer. - Security: tokens exist only when given or when the broker has TLS or a username (PoC: no signature check); the remote dispatch carries the token. - Server isolation: create defaults to a workspace-named server; the shim bootstraps only a named server and never when MAM_NO_AUTOSTART=1 (status/monitor); restore-off is the normal path, with a warning fallback. - Loop: the selected reviewers must all pass; no reviewer option means a creator self-review. - Interpreter order with the PATH python3 fallback; exit 5 also covers target_not_found; two layers with their own registries; test isolation stated as sandboxes plus after-run guards; quick start uses named sessions. - README.ko.md now mirrors README.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
1 parent
20b1014717
commit
423abc7844
2 files changed
+215
-190
No files matched your search
+191
-168
@@ -1,6 +1,6 @@
|
|||||||
# herdr-agent-orchestration (다중 에이전트 Herdr 오케스트레이션 및 메시징 백플레인)
|
# herdr-agent-orchestration (다중 에이전트 Herdr 오케스트레이션 및 메시징 백플레인)
|
||||||
|
|
||||||
Herdr와 MQTT 브로커를 기반으로 구축된 고신뢰성 **다중 에이전트 오케스트레이션 및 메시징 백플레인** 프레임워크입니다. `claude`, `agy`, `hermes`, `grok`, `opencode`, `codex` 등 다중 LLM 백엔드 에이전트 전반에 걸쳐 장시간 수행되는 작업(코드 생성, 리팩토링, 보안 검토 등)을 조정, 격리 및 감사할 수 있도록 설계되었습니다.
|
[herdr](https://herdr.dev)와 MQTT 위에 만든 **다중 에이전트 오케스트레이션·메시징 백플레인**입니다. 장시간 실행되는 코딩 에이전트(`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`)를 격리된 herdr pane 에서 돌리고, 작업을 추적되는 잡으로 위임하며, 모든 리뷰어가 통과시킬 때까지 planner / creator / reviewer 루프를 돌립니다.
|
||||||
|
|
||||||
[](VERSIONS.md)
|
[](VERSIONS.md)
|
||||||
|
|
||||||
@@ -8,212 +8,235 @@ Herdr와 MQTT 브로커를 기반으로 구축된 고신뢰성 **다중 에이
|
|||||||
|
|
||||||
## 🚀 개요
|
## 🚀 개요
|
||||||
|
|
||||||
최근의 에이전트 워크플로우는 세션 타임아웃, 프로세스 격리 부재, 터미널 뷰포트 잘림(스크롤백 한계로 인한 디버그 로그 유실), 복잡한 동시성 경쟁 등의 문제를 자주 겪습니다.
|
긴 에이전트 작업은 정해진 방식으로 깨집니다. 터미널과 함께 세션이 죽고, 한 pane 에 잡 두 개가 들어가고, 오케스트레이터는 바쁜 에이전트와 죽은 에이전트를 구분하지 못하고, 리뷰는 읽은 뒤에 바뀐 코드를 승인합니다. Multi-Agent Mux(MAM)는 이를 다음으로 막습니다.
|
||||||
|
|
||||||
**herdr-agent-orchestration**은 다음과 같은 솔루션을 통해 이를 해결합니다:
|
1. **격리되고 재개 가능한 에이전트 세션.** 에이전트마다 자기 herdr pane 에서 돌고, 보통 워크스페이스 전용 herdr 서버를 씁니다. 세션은 대화 ID 와 함께 `.mam/agent-sessions.yaml`(SQLite 백업)에 기록되므로, 멈춘 에이전트를 나중에 같은 대화로 재개할 수 있습니다.
|
||||||
1. **Herdr 기반 프로세스 격리:** 개별적으로 격리된 herdr 환경 내부에 에이전트 CLI 세션을 띄워, 백그라운드에서 끊김 없이 장시간 작업이 영속되도록 보장합니다. 백그라운드 데몬 서버는 독립된 OS 세션/프로세스 그룹(`start_new_session=True`)으로 분리되어 부모 셸의 시그널 브로드캐스트로부터 안전하게 격리됩니다.
|
2. **화면이 아니라 이벤트로 판정하는 잡.** 위임 잡은 레지스트리 레코드와 MQTT 이벤트 흐름(`started`, `progress`, `completed`, `error`)입니다. 에이전트는 정해진 시간 안에 프롬프트 수신을 알리고, 일하는 동안 heartbeat 를 보내야 합니다. 잡을 닫는 것은 프로토콜 이벤트나 시간 초과뿐이며, 터미널을 읽어 판정하지 않습니다.
|
||||||
2. **비동기 이벤트 기반 아키텍처:** MQTT 브로커를 메시징 백플레인으로 활용하여, 에이전트 간 상태 전이 단계(`started`, `progress`, `completed`, `error`)를 긴밀하게 제어 및 조정합니다.
|
3. **세션당 한 번에 잡 하나.** 프롬프트를 넣기 전에 디스패처가 레지스트리로 그 세션의 앞선 잡(온보딩 포함)이 끝나기를 기다립니다. pane 배치는 herdr 서버마다 직렬화되어, 동시에 실행한 `create` 가 같은 pane 을 차지하지 않습니다.
|
||||||
3. **Multi-Agent Mux (MAM):** 파일 기반의 어드바이저리 락(`fcntl`) 및 SQLite WAL 데이터베이스(`.mam/agent-sessions.db`)를 결합하여, 동시성 작업 선점 경쟁을 방지하고 에이전트 세션의 라이프사이클을 드리프트 없이 관리합니다.
|
4. **근거를 고정한 리뷰 루프.** `multi-agent-mux-loop` 는 변경을 선택한 리뷰어에게 보내고, 그 전원의 `PASS` 를 요구합니다. 리뷰어는 `--reviewer` 로 지정하거나 `--all-reviewer` 로 실행 중인 리뷰어 전원을 고르며, 둘 다 없으면 creator 가 스스로 리뷰합니다. `--worktree`/`--base` 를 쓰면 리뷰 라운드마다 소스 스냅샷에 묶이며, 라운드 중 소스가 바뀌면 그 승인은 버리고 다시 리뷰합니다.
|
||||||
4. **리뷰어 기반 고신뢰 검증 루프:** Worker 에이전트가 구현한 코드 변경 사항에 대해 상이한 강점을 지닌 전문 검증 에이전트(Claude, Hermes, Grok, OpenCode, Codex 등)들로부터 최종 `PASS` 판정을 획득한 뒤 머지하도록 교차 검증 루프를 자동화합니다.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ 핵심 스킬 및 구조
|
## 📦 설치 및 설정
|
||||||
|
|
||||||
모든 오케스트레이션 스킬들은 `.agents/skills/` 디렉터리 하위에 정의되어 있습니다:
|
아무 워크스페이스 디렉터리에서 MAM 을 설치합니다:
|
||||||
|
|
||||||
* **`multi-agent-mux-create`**: 격리된 herdr 세션을 생성하고 특정 에이전트 CLI를 백그라운드에서 구동합니다. 프로세스 PID 캡처, 메타데이터 레지스트리 업데이트 및 에이전트 인증 검증을 처리합니다.
|
```bash
|
||||||
* **`multi-agent-mux-stop`**: 에이전트 CLI 세션을 정상 종료 키 입력(`/exit` 또는 `Exit`)을 통해 안전하게 닫고, 격리된 대화 히스토리 및 데이터베이스 로그를 삭제(purge)하는 클린업 작업을 수행합니다.
|
curl -fsSL https://git.godopu.com/tmpl/multi-agent-mux/raw/branch/main/deploy/install.sh | bash
|
||||||
* **`multi-agent-mux-resume`**: 디스크 또는 캐시에서 특정 워크스페이스의 세션 UUID를 조회하여 기존 대화 상태(`claude -r <uuid>`, `agy --conversation <uuid>`, `hermes --resume <uuid>`, `grok --resume <uuid>`, `opencode --session <uuid>`, `codex resume <uuid>`) 그대로 세션을 복구하고 재개합니다.
|
|
||||||
* **`multi-agent-mux-status`**: 활성화된 모든 세션의 실시간 작동 상태를 쿼리하여 PID 정합성, 실행 명령 포맷, herdr 실제 상태와 데이터베이스 간의 동기화 드리프트를 감지합니다.
|
|
||||||
* **`multi-agent-mux-monitor`**: 백그라운드 Reconcile 프로세스로 실행되어, 실시간 herdr 세션 변화를 모니터링하고 `.mam/agent-sessions.yaml` 메타데이터 파일에 상태를 동기화합니다.
|
|
||||||
* **`multi-agent-mux-delegate-job`**: 태스크를 비동기식 독립 잡으로 위임 및 관리하는 핵심 모듈입니다 (본체 구현은 `.agents/skills/lib_py/delegation/`에 위치하며, `scripts/*.py`는 하위 호환 shim):
|
|
||||||
* `registry.py`: 파일 락(`fcntl`)을 활용해 경쟁 조건 없이 잡을 원자적으로 등록 및 점유(claim)합니다.
|
|
||||||
* `job_subscriber.py`: MQTT 백플레인 채널을 구독하여 실시간 상태 이벤트를 수집하고 이를 감사 로그(audit trail)에 기록합니다.
|
|
||||||
* `publish_event.py`: 실행 상태 전환 및 세부 에러 내용을 워크스페이스 스크립트에서 백플레인으로 발행합니다.
|
|
||||||
* `mqtt_common.py`: 브로커 접속 규칙, 크레덴셜 인증 및 HMAC 서명 기능을 공통 관리합니다.
|
|
||||||
* **`multi-agent-mux-loop`**: Planner, Creator, Reviewer 3자 간의 자율적인 계획-실행-교차검증 반복 순환 루프를 구동합니다 (Python 루프 런타임 `lib_py.loop`, eager-import 불변 런타임, strict verdict parser 기반).
|
|
||||||
* **`multi-agent-mux-orc-onboard`**: 오케스트레이터 UUID를 레지스트리에 격리 등록하여 서브 에이전트 자동 탐색 오염을 방지합니다.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📐 전체 아키텍처 구성 (Big-Picture Architecture)
|
|
||||||
|
|
||||||
이 시스템은 크게 두 가지 계층(Layer)을 통해 다중 워크스페이스에서 작동하는 LLM 에이전트들을 조율합니다:
|
|
||||||
|
|
||||||
1. **Layer A — Herdr 오케스트레이션 (lib.sh + status/resume/stop/create)**: 워크스페이스별 에이전트 세션을 독립된 herdr 인스턴스로 분리 실행하고, `.mam/agent-sessions.yaml` 및 SQLite 데이터베이스(`.mam/agent-sessions.db`)를 통해 에이전트 세션 메타데이터의 단일 참조 지점(Single Source of Truth)을 유지합니다.
|
|
||||||
2. **Layer B — 비동기 잡 위임 (delegate-job)**: 에이전트에 특정 태스크를 전송하고 비동기 이벤트 채널(MQTT)을 통해 진행 상황과 완료 여부를 모니터링합니다.
|
|
||||||
|
|
||||||
두 레이어는 파일 I/O 처리를 위한 하나의 핵심 관문인 `lib.sh::atomic_dump_yaml`을 공유합니다. 모든 YAML/DB 쓰기 작업은 SQLite 데이터베이스 트랜잭션 락과 데이터 스키마 유효성 검증을 거칩니다.
|
|
||||||
|
|
||||||
### 데이터 흐름 개요 (Data Flow)
|
|
||||||
|
|
||||||
```text
|
|
||||||
+-----------+ register_job +-------------------+
|
|
||||||
| delegator | ---------------> | .mam/jobs/<id>.json| <-- 실시간 잡 정보
|
|
||||||
+-----------+ +---------+---------+
|
|
||||||
|
|
|
||||||
| atomic rename + fsync
|
|
||||||
v
|
|
||||||
+-----------------+
|
|
||||||
| audit log | <-- 추가 전용
|
|
||||||
| .mam/delegate_ | events.ndjson
|
|
||||||
| job_logs/<id>/ |
|
|
||||||
+--------+--------+
|
|
||||||
^
|
|
||||||
| (최선 노력 미러링)
|
|
||||||
|
|
|
||||||
+-----------+ publish_event +-----+-----+ +---------+
|
|
||||||
| agent | ---------------> | MQTT broker | <--- | monitor |
|
|
||||||
| (claude) | +-------------+ +----+----+
|
|
||||||
+-----------+ |
|
|
||||||
^ v
|
|
||||||
| 구독자(subscriber) atomic_dump_yaml
|
|
||||||
| (job_subscriber.py) (.mam/agent-sessions.yaml)
|
|
||||||
| ^
|
|
||||||
+-------- 위임 대기 영역 -----------------+ |
|
|
||||||
+---+---+
|
|
||||||
| reconcil|
|
|
||||||
| e.sh |
|
|
||||||
+--------+
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🔒 Herdr 서버 격리 (Herdr Server Isolation)
|
로컬 클론에서는:
|
||||||
|
|
||||||
에이전트 세션 간의 충돌 및 시스템 전역 herdr 프로세스와의 혼선을 막기 위해 독립된 서버 소켓 환경을 보장합니다:
|
```bash
|
||||||
* **워크스페이스별 심(Shim):** `_init_herdr_isolation` 및 `_resolve_real_herdr_path` 함수가 `/tmp/multi-agent-herdr-shim/<HERDR_SERVER_NAME>/herdr` 경로에 독립된 심 디렉터리를 구성하고, 일반 herdr 명령 실행 시 자동으로 `herdr -L <server>` 형태의 독립 소켓 서버를 사용하게 만듭니다.
|
bash deploy/install.sh
|
||||||
* **PATH 환경변수 변조:** 자식 프로세스를 생성할 때 `PATH` 변수 맨 앞에 심 디렉터리 경로를 삽입합니다. 이로 인해 에이전트의 내부 셸에서 수행되는 모든 `herdr` 명령어는 해당 격리 서버 소켓으로 강제 제약됩니다.
|
```
|
||||||
* **환경 복구:** `HERDR_SERVER_NAME`을 `default`로 설정하는 경우 PATH 오버라이드가 정리되고 기본 전역 herdr 서버를 사용하게 됩니다.
|
|
||||||
|
|
||||||
### 🛡️ 동시성 설계 및 쓰기 직렬화
|
`install.sh` 는 여러 번 실행해도 안전합니다. 하는 일은 다음과 같습니다.
|
||||||
|
- herdr 와 Python 3.9+ 가 설치돼 있는지, 시스템 `python3` 에 PyYAML 이 있는지 확인합니다.
|
||||||
|
- `.agents/` 와 `.mam.env.example` 을 워크스페이스에 복사합니다.
|
||||||
|
- `.venv` 를 만들고 `paho-mqtt`·PyYAML 을 설치합니다.
|
||||||
|
- `MQTT_BROKER=localhost` 로 된 초기 `.mam.env` 를 씁니다. 이미 있는 `.mam.env` 는 그대로 두고, 예전 `.env` 는 `.mam.env` 로 이름을 바꿉니다.
|
||||||
|
- `.venv`, `.mam/`, `.mam.env` 를 `.gitignore` 에 추가합니다.
|
||||||
|
|
||||||
여러 에이전트가 동시에 실행될 때의 레이스 컨디션을 방지하기 위해 락 기반의 실행 패턴을 고수합니다:
|
`.mam.env` 의 `MQTT_BROKER` 를 직접 운영하는 브로커로 바꾸십시오(사설 브로커는 [MESSAGING.md](MESSAGING.md) 참고). `MQTT_BROKER` 가 아예 설정되지 않으면 코드는 공개 브로커 `broker.hivemq.com` 을 씁니다. 시험용으로는 괜찮지만 실제 작업용은 아닙니다. `deploy/generate-env.sh` 는 `.mam.env.example` 로 `.mam.env` 를 다시 만듭니다. 업데이트와 제거는 `deploy/update.sh`, `deploy/remove.sh` 입니다. `deploy/install_mam.sh` 는 로컬 클론에서 다른 디렉터리로 복사해 설치하는 별도 설치기입니다.
|
||||||
* **SQLite 데이터베이스 락 (`BEGIN IMMEDIATE`):** `agent-sessions.yaml` 또는 SQLite 레지스트리에 쓰기 연산을 진행할 때, 반드시 `lib.sh` 내부의 `atomic_dump_yaml` 함수를 거쳐 SQLite 데이터베이스 `.mam/agent-sessions.db`에 `BEGIN IMMEDIATE` 트랜잭션 락을 획득하도록 직렬화합니다.
|
|
||||||
* **이중 인터프리터 분리 구조:** 라이브러리 간 의존성 충돌과 실행 도구의 안정성을 보장하기 위해 환경을 이원화했습니다. MQTT 및 비동기 작업 통신에는 가상환경 `.venv` (paho-mqtt 필요)의 Python을 사용하고, YAML 직렬화 쓰기 및 유효성 검증을 담당하는 `atomic_dump_yaml`은 시스템 전역 `python3` (시스템 PyYAML 필요)을 호출합니다.
|
|
||||||
* **NFS 및 네트워크 파일시스템 대응:** 네트워크 디바이스(NFS, CIFS, SSHFS)에서는 파일 락(`flock`) 및 SQLite WAL 기능이 오작동할 수 있습니다. `lib.sh`는 쓰기 대상 파일시스템 경로의 마운트 타입을 체크하여, 네트워크 파일시스템 감지 시 경고 로그를 출력하고 SQLite의 저널 모드를 `WAL`에서 `DELETE`로 자동 전환해 동시성 안전을 강화합니다.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📐 아키텍처 및 조정 루프 (Review Loop)
|
## 🛠️ 핵심 스킬
|
||||||
|
|
||||||
Project Manager(PM), Worker, Reviewer 역할 간의 협업 구조는 엄격한 교차 검증 루프를 따릅니다:
|
모든 스킬은 `.agents/skills/` 아래에 있고, 각 `SKILL.md` 가 옵션과 종료 코드를 설명합니다.
|
||||||
|
|
||||||
|
| 스킬 | 하는 일 |
|
||||||
|
|---|---|
|
||||||
|
| **`multi-agent-mux-create`** | 에이전트를 herdr pane 에서 시작하고 세션을 등록합니다. `--onboard` 는 온보딩 잡이 끝날 때까지 기다립니다(실패하면 종료 코드 44, 시간 안에 끝나지 않으면 45; 세션은 남김). `scripts/worktree_prepare.sh` 는 creator 용 이슈 워크트리를 준비합니다. |
|
||||||
|
| **`multi-agent-mux-stop`** | 세션을 정상 종료합니다. 대화 ID 를 행에 저장합니다(`status: stopped`, `resumable: true`). `--purge-conversation --yes` 는 대신 대화를 지웁니다. `scripts/worktree_remove.sh` 는 서브모듈까지 깨끗한지 확인한 뒤에만 이슈 워크트리를 지웁니다. |
|
||||||
|
| **`multi-agent-mux-resume`** | 멈춘 세션을 저장된 대화로 되살립니다(`claude -r`, `agy --conversation`, `hermes --resume`, `grok --resume`, `opencode --session`, `codex resume`). 세션이 원래 돌던 디렉터리에서만 재개합니다. |
|
||||||
|
| **`multi-agent-mux-status`** | 모든 세션의 읽기 전용 스냅샷: 레지스트리 상태, herdr 생존, pane 명령과 cwd, 재개 ID, 불일치(drift). |
|
||||||
|
| **`multi-agent-mux-monitor`** | 장기 실행 조정자(`reconcile.sh --subscribe`). 잡 이벤트에 반응하고, 브로커가 죽으면 폴링으로 돌아가며, 레지스트리 불일치를 고칩니다. 예를 들어 죽은 세션을 `terminated` 로 표시하고, 레지스트리가 모르는 살아 있는 세션을 등록합니다. |
|
||||||
|
| **`multi-agent-mux-delegate-job`** | 세션(`herdr:<name>`)이나 다른 호스트의 러너(`remote:<name>`)에 잡을 위임하고 결과를 기다립니다. [위임 계약](#-위임-계약)을 보십시오. |
|
||||||
|
| **`multi-agent-mux-loop`** | 계획·구현·리뷰: 선택적인 planner 와 creator, 그다음 선택한 리뷰어(`--reviewer A,B`, 또는 `--all-reviewer` 로 creator 를 뺀 실행 중인 리뷰어 전원)가 모두 `PASS` 를 낼 때까지(또는 `--max-loop` 까지). 둘 다 없으면 creator 가 스스로 리뷰합니다. 루프 밖에서 끝난 작업은 `--review-only` 로 리뷰합니다. 통과한 리포트는 `.agents/reports/<session>/` 으로 옮겨 보관합니다. |
|
||||||
|
| **`multi-agent-mux-orc-onboard`** | 오케스트레이터 자신의 세션 ID 를 등록해, 세션 탐색이 그것을 하위 에이전트로 착각하지 않게 합니다. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📐 아키텍처
|
||||||
|
|
||||||
|
MAM 은 각자 레지스트리를 가진 두 계층으로 이루어집니다.
|
||||||
|
|
||||||
|
1. **세션 (herdr)**: `lib.sh` 와 create/stop/resume/status/monitor 스크립트가 에이전트를 돌리고, `.mam/agent-sessions.yaml`(+ `.mam/agent-sessions.db`)을 기준 기록으로 유지합니다.
|
||||||
|
2. **잡 (위임)**: `lib_py/delegation/` 이 `.mam/jobs/<id>.json` 에 잡을 등록하고, 브리프를 주입하고, MQTT 로 잡의 이벤트를 따라갑니다. 모든 이벤트는 잡의 감사 로그 `.mam/delegate_job_logs/<id>/` 에도 덧붙여집니다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
+--------------+ register / pick +----------------------+
|
||||||
|
| orchestrator | -----------------> | .mam/jobs/<id>.json | live record
|
||||||
|
| (wrapper) | +----------+-----------+
|
||||||
|
+------+-------+ | every transition
|
||||||
|
| inject brief v
|
||||||
|
v +----------------------+
|
||||||
|
+--------------+ publish_event | .mam/delegate_job_ | append-only
|
||||||
|
| agent pane | --------+ | logs/<id>/ | audit log
|
||||||
|
+--------------+ | +----------------------+
|
||||||
|
v
|
||||||
|
+-------------+ events + retained state
|
||||||
|
| MQTT broker | -------------------------> job_subscriber (orchestrator)
|
||||||
|
+-------------+ -------------------------> monitor (reconcile.sh)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
.mam/agent-sessions.yaml / .db
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔒 Herdr 서버 격리
|
||||||
|
|
||||||
|
- **워크스페이스 전용 서버.** `create` 는 기본으로 워크스페이스 이름(slug)을 딴 herdr 서버에서 에이전트를 돌립니다. 이 서버는 기본 herdr 와 분리된 자기 소켓을 씁니다. `--herdr-session <name>` 또는 `HERDR_SESSION_NAME` 으로 다른 이름을 고를 수 있습니다. 서버 이름은 세션 행에 기록되고, 이후 모든 명령이 그 서버로 갑니다.
|
||||||
|
- **shim.** `lib.sh` 를 source 하면 `<workspace>/.mam/shim/herdr` 를 쓰고 `PATH` 맨 앞에 둡니다. shim 은 스크립트가 쓰는 tmux 식 명령(`new-session`, `has-session` …)을 herdr 의 에이전트 API 위에 제공합니다. 이름 있는 서버면 호출마다 `--session <server>` 를 붙이고, 그 서버가 돌고 있지 않으면 헤드리스로 시작합니다. 단 `MAM_NO_AUTOSTART=1` 이면 시작하지 않습니다. `status` 와 `monitor` 가 이 값을 설정하므로, 조회만으로 서버가 뜨는 일은 없습니다. `default` 서버 호출은 바꾸지 않고 사용자의 herdr 로 그대로 갑니다.
|
||||||
|
- **예상치 못한 재실행 없음.** MAM 이 시작한 서버는 보통 사용자의 herdr 설정에 `resume_agents_on_restore = false` 를 더해 씁니다. 그래서 herdr 가 재시작돼도 저장된 에이전트를 권한 플래그 없이 다시 띄우지 않습니다. 이 설정을 만들 수 없으면 부트스트랩이 경고를 남기고, 서버는 restore 가 켜진 채 시작합니다.
|
||||||
|
- **pane 배치는 한 번에 하나.** pane 배치는 서버별 잠금(`lib_py/pane_lock.py`, macOS·Linux 에서 `flock`)을 잡고 합니다. 한 폴더에서 동시에 실행한 `create` 도 각자 자기 pane 을 받습니다.
|
||||||
|
|
||||||
|
### 🛡️ 레지스트리 쓰기와 동시성
|
||||||
|
|
||||||
|
- **직렬화된 쓰기.** `agent-sessions.yaml` 의 모든 변경은 `lib.sh::atomic_dump_yaml` 을 거칩니다. `.mam/agent-sessions.db` 의 배타적 SQLite 트랜잭션(`BEGIN IMMEDIATE`) 안에서 실행되고, 검증한 뒤에 YAML 을 원자적으로 다시 씁니다. 잡 레코드는 별도의 레지스트리 잠금을 씁니다.
|
||||||
|
- **Python 인터프리터.** 레지스트리 쓰기와 위임 헬퍼는 이 순서로 Python 을 고릅니다: `AGENT_PYTHON_BIN`, 활성화된 가상환경, 스킬 위쪽에서 가장 가까운 `.venv`, 마지막으로 `PATH` 의 `python3`. shim 과 `create` 의 일부 같은 셸 경로는 `PATH` 의 `python3` 를 직접 부릅니다.
|
||||||
|
- **네트워크 파일시스템.** `.mam/` 이 NFS/CIFS/SSHFS 에 있으면 `lib.sh` 가 경고하고, SQLite 저널 모드를 `WAL` 에서 `DELETE` 로 바꿉니다.
|
||||||
|
- **체크아웃당 루프 하나.** 루프 잠금이 같은 제어 평면에서 두 번째 `run_loop.sh` 를 거부합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📨 위임 계약
|
||||||
|
|
||||||
|
전체 명세는 [MESSAGING.md §2.5](MESSAGING.md)에 있습니다.
|
||||||
|
|
||||||
|
- **전달**: 프롬프트를 넘긴 뒤 **T** 초(기본 180) 안에 에이전트가 `started` 를 발행해야 합니다. 그러지 않으면 잡은 실패로 닫히고(`agent_blocked`, 종료 코드 1), 프롬프트를 두 번 넣지 않습니다.
|
||||||
|
- **생존**: `started --heartbeat N` 이 자동 heartbeat 를 시작합니다(기본 30 초마다). **K×N** 초(기본 120 초) 동안 아무 이벤트가 없으면 무응답으로 봅니다. herdr 상태와 pane 텍스트는 진단용으로만 로그에 남깁니다.
|
||||||
|
- **예산**: 잡마다 벽시계 예산이 있습니다(`--timeout` / `MAM_JOB_TIMEOUT_S`, 기본 3600 초). 예산을 다 쓴 잡은 `wall_clock_timeout` 으로 닫힙니다.
|
||||||
|
- **바쁜 세션**: 래퍼는 그 세션의 앞선 열린 잡이 끝나기를 `MAM_DISPATCH_WAIT_S` 만큼 기다립니다. 끝나지 않으면 `cancelled`(`session_busy`, 종료 코드 5)로 끝나고, 프롬프트는 전달되지 않습니다.
|
||||||
|
- **원격 에이전트**: `remote:<name>` 은 브리프를 서명된 retained 브로커 메시지로 다른 호스트의 `remote.py run` 러너에 보냅니다. 사설 브로커에서만 쓰십시오.
|
||||||
|
|
||||||
|
| 종료 코드 | 의미 |
|
||||||
|
|:---:|---|
|
||||||
|
| `0` | `completed` |
|
||||||
|
| `1` | 실패, 또는 T 안에 `started` 없음 |
|
||||||
|
| `2` | K×N 동안 이벤트 없음(`deadline_timeout`) 또는 예산 소진(`wall_clock_timeout`) |
|
||||||
|
| `3` | 브로커를 쓸 수 없고, 디스크에도 종결 상태가 없음 |
|
||||||
|
| `5` | 전달 안 됨(`cancelled`): 세션이 바쁨, 대상을 찾지 못함, 또는 기다리는 동안 레코드를 다른 쪽이 가져감 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔁 리뷰 루프
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
autonumber
|
autonumber
|
||||||
actor User as User
|
actor User
|
||||||
participant PM as Project Manager
|
participant L as run_loop.sh
|
||||||
participant W as Worker
|
participant P as Planner (optional)
|
||||||
|
participant C as Creator
|
||||||
participant R as Reviewers
|
participant R as Reviewers
|
||||||
participant M as MQTT Backplane
|
|
||||||
|
|
||||||
User->>PM: 요구사항 전달
|
User->>L: --task, --creator, --reviewer / --all-reviewer
|
||||||
Note over PM: 태스크 수립 및 Job 등록
|
opt --plan
|
||||||
PM->>M: Job 등록 및 Subscriber 시작
|
L->>P: write the plan (Creator challenges it for --plan-talk turns)
|
||||||
PM->>W: 작업 위임 (Job ID 및 가이드 제공)
|
|
||||||
W->>M: 'started' 이벤트 발행
|
|
||||||
Note over W: 코드 변경 및 자체 검증 수행
|
|
||||||
W->>M: 'completed' (또는 'error') 발행
|
|
||||||
PM->>R: 병렬 리뷰 요청 (Diff 제공)
|
|
||||||
Note over R: 에이전트 교차 검증 (Claude, Hermes, Grok, OpenCode, Codex)
|
|
||||||
alt 리뷰 피드백 (NOT PASS)
|
|
||||||
R->>PM: NOT PASS (대안 코드 블록과 함께 피드백 전달)
|
|
||||||
Note over PM: PM 직접 수정 또는 다시 위임 결정
|
|
||||||
PM->>W: 리뷰 피드백 반영 재요청
|
|
||||||
else 검증 통과 (PASS)
|
|
||||||
R->>PM: PASS
|
|
||||||
end
|
end
|
||||||
PM->>User: 최종 머지 완료 보고 및 변경사항 커밋
|
L->>C: implement and commit (skipped with --review-only)
|
||||||
|
loop up to --max-loop
|
||||||
|
L->>R: review the snapshot diff (base..head)
|
||||||
|
alt every reviewer PASS and the source unchanged
|
||||||
|
R-->>L: [VERDICT: PASS]
|
||||||
|
else NOT PASS
|
||||||
|
R-->>L: [VERDICT: NOT PASS] + findings
|
||||||
|
L->>C: fix (or [REBUT:] up to --max-rebut)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
L->>User: reports promoted to .agents/reports/<session>/
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
- **엄격한 판정**: 판정 토큰은 리포트 마지막 줄에 단독으로 있어야 합니다. 판정이 없으면 `NOT PASS` 로 봅니다.
|
||||||
|
- **리뷰 identity** (`--worktree` / `--base`): 라운드마다 커밋하지 않은 파일까지 담은 스냅샷을 리뷰합니다. 라운드 중 소스가 바뀌면 그 승인은 버립니다. 보관되는 리포트 옆에는 base·head·tree·리뷰어를 적은 `report-<job>.identity.json` 이 붙습니다.
|
||||||
## 🔒 보안 프로토콜 및 Replay 공격 방어
|
- **하위 잡**: creator 가 작업 일부를 위임하면(`--parent-job`), 루프는 리뷰 전에 그 잡들을 기다리고 상태를 리뷰어에게 보여 줍니다.
|
||||||
|
|
||||||
공용 MQTT 브로커 환경에서도 통신의 무결성을 보장하기 위해 **HMAC-SHA256 암호화 서명** 체계를 갖추고 있습니다:
|
|
||||||
* **PoC 모드 (무인증):** 기본 설정 모드로 `auth_token`이 `null`로 세팅되어 간단한 로컬 환경 검증 시 암호화 시그니처 체크를 건너뜁니다.
|
|
||||||
* **프로덕션 모드 (인증 필수):** 각 잡마다 무작위 암호화 토큰이 발급되며, 백플레인을 오가는 모든 페이로드에 토큰 기반으로 생성된 `hmac_sig` 서명을 탑재해야 수신측에서 수용합니다.
|
|
||||||
* **Replay 공격 방어:** 각 이벤트는 단조 증가하는 정수형 시퀀스 번호(`seq`)를 포함합니다. 구독자(`job_subscriber.py`)는 특정 잡에 대해 이미 수용한 최대 시퀀스 번호보다 크지 않은 시퀀스 번호를 가진 페이로드를 즉시 폐기합니다. 페이로드 바디에 대한 HMAC 서명과 결합하여, 이 메커니즘은 클록 동기화에 의존하지 않고 재전송(re-injected) 및 순서가 바뀐 패킷을 차단합니다. 메시지 내 타임스탬프 필드는 참고용 메타데이터이며, 백플레인은 시간 오차(clock-skew) 윈도우를 별도로 검증하지 않습니다.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📁 리포지토리 구성
|
## 🔐 보안
|
||||||
|
|
||||||
|
- **서명된 이벤트**: 토큰을 직접 넘기거나 브로커가 TLS 또는 사용자 이름을 쓰면, 잡은 고유 토큰(`auth_token`)을 받습니다. 그때 모든 이벤트와 retained 상태 메시지에 그 토큰으로 만든 HMAC-SHA256 서명이 붙고, 구독자는 검증에 실패한 메시지를 버립니다. 이벤트와 상태 메시지에는 토큰이 아니라 서명만 실립니다. TLS 도 사용자 이름도 없는 브로커(PoC 구성)에서는 잡에 토큰이 없어 서명을 검사하지 않습니다.
|
||||||
|
- **원격 디스패치에는 토큰이 실린다**: `remote:<name>` 디스패치 메시지에는 잡의 토큰이 들어갑니다(`MAM_DISPATCH_SECRET` 으로 서명될 뿐 암호화되지는 않음). 접근이 통제된 사설 브로커에서만 쓰십시오.
|
||||||
|
- **재전송과 순서**: 이벤트마다 증가하는 `seq` 가 붙습니다. 구독자는 지금까지 본 것보다 큰 번호만 받고, 빈 번호는 `seq_gap` 으로 기록합니다. 타임스탬프는 참고용입니다.
|
||||||
|
- **자격 증명**: 브로커 자격 증명은 `.mam.env` 에만 있고, 잡 레코드에는 없습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 테스트
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.venv/bin/python -m pytest -q -p no:cacheprovider tests
|
||||||
|
```
|
||||||
|
|
||||||
|
- 테스트는 약 1,900 개이고, 전체 실행에 한 시간 넘게 걸립니다.
|
||||||
|
- 프로세스 안에서 자체 MQTT 브로커를 띄우므로 `.mam.env` 가 필요 없습니다. 테스트용으로 `.mam.env` 를 export 하지 마십시오.
|
||||||
|
- 테스트는 임시 HOME·레지스트리·감사 로그 디렉터리와 mock herdr 를 쓰는 샌드박스에서 돕니다.
|
||||||
|
- 실제 herdr 를 다루는 테스트는 일회용 herdr 서버를 직접 띄웁니다.
|
||||||
|
- 실행이 끝나면 세션 단위 가드가 실제 제어 평면과 사용자의 에이전트 홈(`~/.claude/projects`, `~/.codex`, `~/.gemini` …)을 확인하고, 테스트가 남긴 항목이 있으면 실행을 실패시킵니다.
|
||||||
|
- 일부 테스트는 플랫폼에 따라 다릅니다. Python 3.11+(`tomllib`), Linux(`/proc`), `nats-docker` 서브모듈이 필요한 테스트는 그 밖의 환경에서 건너뜁니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 저장소 구조
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.
|
.
|
||||||
├── .agents/
|
├── .agents/
|
||||||
│ ├── MULTI_AGENT_RULES.md # 에이전트 역할 행동 강령 및 뷰포트 스냅샷 규칙
|
│ ├── MULTI_AGENT_RULES.md / .ko.md # 역할, 워크플로우, 협업 규칙
|
||||||
│ ├── MULTI_AGENT_RULES.ko.md # 에이전트 역할 행동 강령 (한국어 백업)
|
│ ├── reports/ # 보관된 리뷰 리포트 (+ identity 사이드카)
|
||||||
│ └── skills/ # 코어 오케스트레이션 셸 스크립트 및 라이브러리
|
│ └── skills/
|
||||||
│ ├── lib.sh # 공통 오케스트레이션 셸 함수 라이브러리
|
│ ├── lib.sh # 공용 셸 라이브러리 (herdr shim, 레지스트리 쓰기)
|
||||||
│ ├── lib_py/ # 파이썬 코어 오케스트레이션 엔진
|
│ ├── lib_py/ # Python 코어
|
||||||
│ │ ├── agents/ # 멀티 에이전트 CLI 수명 주기 어댑터
|
│ │ ├── agents/ # 에이전트별 CLI 어댑터 (spawn/resume/discover)
|
||||||
│ │ ├── delegation/ # 비동기 잡 위임 및 CAS 상태 전이 엔진
|
│ │ ├── delegation/ # 잡 레지스트리, 래퍼, MQTT 발행/구독, 원격 러너
|
||||||
│ │ └── loop/ # 자율 계획-실행-교차검증 루프 런타임
|
│ │ ├── loop/ # 리뷰 루프 런타임
|
||||||
│ ├── multi-agent-mux-create/
|
│ │ └── *.py # pane_lock, stall_watch, worktree, herdr_config, ...
|
||||||
│ ├── multi-agent-mux-stop/
|
│ └── multi-agent-mux-*/ # 스킬마다 디렉터리 하나 (SKILL.md + scripts/)
|
||||||
│ ├── multi-agent-mux-resume/
|
├── .mam/ # 런타임 상태 (git 제외): 세션, 잡, 감사 로그, shim
|
||||||
│ ├── multi-agent-mux-status/
|
├── deploy/ # install.sh, install_mam.sh, update.sh, remove.sh, generate-env.sh
|
||||||
│ ├── multi-agent-mux-monitor/
|
├── docs/ # contracts/, design/, 에이전트 통합·브리지 가이드
|
||||||
│ ├── multi-agent-mux-delegate-job/
|
├── nats-docker/ # 사설 NATS 브로커 배포 (서브모듈)
|
||||||
│ │ ├── requirements.txt # 파이썬 의존성 패키지 선언
|
├── tests/ # pytest 스위트
|
||||||
│ │ └── scripts/ # 하위 호환 shim 스크립트 (Python)
|
├── AGENTS.md # 에이전트 행동 지침
|
||||||
│ ├── multi-agent-mux-loop/
|
├── BOOTSTRAP.md / BOOTSTRAP.ko.md # 단계별 설치·검증 가이드
|
||||||
│ └── multi-agent-mux-orc-onboard/
|
├── IMPROVEMENTS.md # 백로그
|
||||||
├── .mam/ # Multi-Agent Mux 메타데이터 (git-ignored)
|
├── MESSAGING.md # MQTT 와이어 프로토콜과 위임 계약
|
||||||
│ ├── agent-sessions.db # SQLite WAL 세션 데이터베이스
|
├── README.md # 영문 README
|
||||||
│ ├── agent-sessions.yaml # 텍스트 형식의 세션 레지스트리 스냅샷
|
└── VERSIONS.md # 릴리스 노트 (락스텝 버전 원장)
|
||||||
│ └── jobs/ # 비동기 잡 메타데이터 JSON 파일들
|
|
||||||
├── deploy/ # 배포 및 설치 도구 패키지 폴더
|
|
||||||
│ ├── INSTALL.md # 설치 가이드 및 퀵스타트 매뉴얼
|
|
||||||
│ ├── install_mam.sh # 로컬/클론 인스톨러 스크립트
|
|
||||||
│ ├── generate-env.sh # 환경 파일(.mam.env) 템플릿 복사 스크립트
|
|
||||||
│ ├── install.sh # 원격/네트워크 인스톨러 스크립트
|
|
||||||
│ ├── update.sh # 업데이트 헬퍼 스크립트
|
|
||||||
│ └── remove.sh # 삭제/언인스톨 헬퍼 스크립트
|
|
||||||
├── docs/ # 기술 규격서, 가이드 및 아키텍처 계약 문서
|
|
||||||
│ ├── contracts/ # 이슈/백플레인 계약 및 마이그레이션 로드맵
|
|
||||||
│ ├── MAM_WEB_PTY_BRIDGE_PLAN.md # Web PTY 터미널 브리지 설계 계획서
|
|
||||||
│ ├── NEW_AGENT_INTEGRATION_GUIDE.md # 신규 AI 코딩 에이전트 연동 가이드
|
|
||||||
│ └── OPENCODE_OLLAMA_GUIDE.md # OpenCode 인터프리터 + Ollama 연동 가이드
|
|
||||||
├── nats-docker/ # 사설 NATS 브로커 배포 서브모듈
|
|
||||||
├── tests/ # 종합 회귀 테스트 스위트 (32개 모듈)
|
|
||||||
├── AGENTS.md # 에이전트 행동 지침 및 오케스트레이터 스코프 가드
|
|
||||||
├── BOOTSTRAP.ko.md # 프로젝트 초기 설치 가이드 (한국어 백업)
|
|
||||||
├── BOOTSTRAP.md # 프로젝트 초기 설치 및 검증 상세 가이드
|
|
||||||
├── IMPROVEMENTS.md # 이슈 백로그 및 완료된 아키텍처 과제 추적 원장
|
|
||||||
├── MESSAGING.md # MQTT 메시징 프로토콜 와이어 규격서
|
|
||||||
├── README.ko.md # 프로젝트 대표 소개 파일 (한국어)
|
|
||||||
├── README.md # 프로젝트 대표 소개 파일 (영문 원본)
|
|
||||||
└── VERSIONS.md # 릴리스 노트 및 4-way 락스텝 버전 원장
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚦 빠른 시작
|
## 🚦 빠른 시작
|
||||||
|
|
||||||
자세한 빌드 절차는 **[BOOTSTRAP.md](./BOOTSTRAP.md)** 문서를 참조하십시오. 아래는 간략한 요약입니다:
|
전체 과정은 [BOOTSTRAP.ko.md](BOOTSTRAP.ko.md)를 보십시오. 요약하면:
|
||||||
|
|
||||||
1. **환경 설정 파일(.mam.env) 생성:**
|
```bash
|
||||||
```bash
|
# 1. 워크스페이스에 설치한 뒤 .mam.env 에 브로커를 설정
|
||||||
./deploy/generate-env.sh
|
bash deploy/install.sh
|
||||||
```
|
|
||||||
2. **가상환경 생성 및 의존성 패키지 설치:**
|
# 2. 전용 herdr 서버에서 creator 와 reviewer 시작
|
||||||
```bash
|
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
||||||
python3 -m venv .venv
|
--workspace "$PWD" --agent claude --role developer --session creator-claude-01 \
|
||||||
source .venv/bin/activate
|
--herdr-session my-project --onboard
|
||||||
pip install -r .agents/skills/multi-agent-mux-delegate-job/requirements.txt
|
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
||||||
```
|
--workspace "$PWD" --agent codex --role reviewer --session reviewer-codex-01 \
|
||||||
3. **레지스트리 및 환경 작동 자가 검증:**
|
--herdr-session my-project --onboard
|
||||||
```bash
|
|
||||||
.venv/bin/python3 .agents/skills/multi-agent-mux-delegate-job/scripts/registry.py list
|
# 3. 세션 확인
|
||||||
```
|
bash .agents/skills/multi-agent-mux-status/scripts/status.sh
|
||||||
|
|
||||||
|
# 4. 루프로 작업 실행
|
||||||
|
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||||
|
--creator creator-claude-01 --all-reviewer --task "Fix the typo in docs/guide.md"
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📝 협업 에이전트 준수 사항
|
## 📝 프로젝트에 합류하는 에이전트에게
|
||||||
|
|
||||||
이 프로젝트에 새로 합류한 에이전트는 다음 규칙을 준수해야 합니다:
|
1. 역할과 규칙은 [AGENTS.md](AGENTS.md)와 [MULTI_AGENT_RULES.ko.md](.agents/MULTI_AGENT_RULES.ko.md)를 읽으십시오.
|
||||||
1. **[MULTI_AGENT_RULES.md](.agents/MULTI_AGENT_RULES.md)** 문서를 정독하여 프로젝트 매니저(PM), 작업자(Worker), 리뷰어(Reviewer) 간의 역할 및 개발 제약조건을 인지하십시오.
|
2. 긴 출력이 pane 스크롤백 밖으로 사라지지 않도록 `MULTI_AGENT_RULES.ko.md` §4 의 스냅샷 규칙을 따르십시오.
|
||||||
2. 장시간 명령을 실행하는 경우 터미널 스크롤백 로그 유실을 방지하기 위해 `MULTI_AGENT_RULES.md` (제4장)에 기재된 **뷰포트 스냅샷 규칙(Pane Snapshotting Rules)**을 반드시 적용하십시오.
|
3. 브리프가 지시하는 대로 잡 이벤트를 발행하십시오(`started --heartbeat`, 그다음 `completed` 또는 `error`). 리뷰받은 diff 없이 핵심 로직을 바꾸지 마십시오.
|
||||||
3. 리뷰어 세션에 diff 검증을 요청하기 전에는 어떠한 코어 파일의 임의 수정도 프로덕션 브랜치에 승인 없이 머지할 수 없습니다.
|
|
||||||
@@ -13,7 +13,7 @@ Long agent runs break in predictable ways: a session dies with its terminal, two
|
|||||||
1. **Isolated, resumable agent sessions.** Each agent runs in its own herdr pane, usually on a herdr server dedicated to the workspace. Sessions are recorded in `.mam/agent-sessions.yaml` (backed by SQLite) with their conversation ids, so a stopped agent resumes the same conversation later.
|
1. **Isolated, resumable agent sessions.** Each agent runs in its own herdr pane, usually on a herdr server dedicated to the workspace. Sessions are recorded in `.mam/agent-sessions.yaml` (backed by SQLite) with their conversation ids, so a stopped agent resumes the same conversation later.
|
||||||
2. **Jobs decided by events, not by the screen.** A delegated job is a registry record plus an MQTT event stream (`started`, `progress`, `completed`, `error`). The agent must acknowledge the prompt within a deadline and heartbeat while it works. A job is closed only by a protocol event or a timeout, never by reading the terminal.
|
2. **Jobs decided by events, not by the screen.** A delegated job is a registry record plus an MQTT event stream (`started`, `progress`, `completed`, `error`). The agent must acknowledge the prompt within a deadline and heartbeat while it works. A job is closed only by a protocol event or a timeout, never by reading the terminal.
|
||||||
3. **One job per session at a time.** Before injecting a prompt, the dispatcher waits by the registry for the session's earlier jobs (onboarding included) to end. Pane placement is serialised per herdr server, so concurrent `create` calls do not take the same pane.
|
3. **One job per session at a time.** Before injecting a prompt, the dispatcher waits by the registry for the session's earlier jobs (onboarding included) to end. Pane placement is serialised per herdr server, so concurrent `create` calls do not take the same pane.
|
||||||
4. **Reviewed loops with pinned evidence.** `multi-agent-mux-loop` sends a change to every registered reviewer and requires a unanimous `PASS`. With `--worktree`/`--base`, each review round is bound to a snapshot of the source. If the source changes during the round, its approvals are discarded and the round is reviewed again.
|
4. **Reviewed loops with pinned evidence.** `multi-agent-mux-loop` sends a change to the reviewers you select and requires every one of them to `PASS`. You can name reviewers with `--reviewer` or take every running reviewer with `--all-reviewer`; without either, the creator reviews its own work. With `--worktree`/`--base`, each review round is bound to a snapshot of the source. If the source changes during the round, its approvals are discarded and the round is reviewed again.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -31,14 +31,14 @@ or, from a local clone:
|
|||||||
bash deploy/install.sh
|
bash deploy/install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The installer is idempotent. It does the following:
|
`install.sh` is idempotent. It does the following:
|
||||||
- checks the dependencies (herdr, `python3` with PyYAML and sqlite3, rsync, uuidgen);
|
- checks that herdr and Python 3.9+ are installed, and that the system `python3` has PyYAML;
|
||||||
- copies `.agents/` and the templates;
|
- copies `.agents/` and `.mam.env.example` into the workspace;
|
||||||
- creates `.venv` and installs `paho-mqtt` and PyYAML into it;
|
- creates `.venv` and installs `paho-mqtt` and PyYAML into it;
|
||||||
- writes `.mam.env` from `.mam.env.example`;
|
- writes a starter `.mam.env` with `MQTT_BROKER=localhost`. An existing `.mam.env` is kept, and a legacy `.env` is renamed to `.mam.env`;
|
||||||
- adds `.venv`, `.mam/` and `.mam.env` to `.gitignore`.
|
- adds `.venv`, `.mam/` and `.mam.env` to `.gitignore`.
|
||||||
|
|
||||||
Configure the broker in `.mam.env`. The default is the public `broker.hivemq.com`, which is fine for a trial but not for real work. See [MESSAGING.md](MESSAGING.md) for a private broker. `deploy/update.sh` and `deploy/remove.sh` update and uninstall.
|
Point `MQTT_BROKER` in `.mam.env` at a broker you run (see [MESSAGING.md](MESSAGING.md) for a private broker). If `MQTT_BROKER` is not set at all, the code falls back to the public `broker.hivemq.com`. That is fine for a trial, but not for real work. `deploy/generate-env.sh` recreates `.mam.env` from `.mam.env.example`. `deploy/update.sh` and `deploy/remove.sh` update and uninstall. `deploy/install_mam.sh` is a separate installer that copies from a local clone into another directory.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -54,14 +54,14 @@ Every skill lives under `.agents/skills/` with a `SKILL.md` that documents its o
|
|||||||
| **`multi-agent-mux-status`** | Read-only snapshot of every session: registry status, herdr liveness, pane command and cwd, resume id, drift. |
|
| **`multi-agent-mux-status`** | Read-only snapshot of every session: registry status, herdr liveness, pane command and cwd, resume id, drift. |
|
||||||
| **`multi-agent-mux-monitor`** | Long-running reconciler (`reconcile.sh --subscribe`). It reacts to job events, falls back to polling when the broker is down, and fixes registry drift. For example, it marks dead sessions `terminated` and registers live sessions the registry does not know. |
|
| **`multi-agent-mux-monitor`** | Long-running reconciler (`reconcile.sh --subscribe`). It reacts to job events, falls back to polling when the broker is down, and fixes registry drift. For example, it marks dead sessions `terminated` and registers live sessions the registry does not know. |
|
||||||
| **`multi-agent-mux-delegate-job`** | Delegates a job to a session (`herdr:<name>`) or to a runner on another host (`remote:<name>`) and waits for its result. See [Delegation contract](#-delegation-contract). |
|
| **`multi-agent-mux-delegate-job`** | Delegates a job to a session (`herdr:<name>`) or to a runner on another host (`remote:<name>`) and waits for its result. See [Delegation contract](#-delegation-contract). |
|
||||||
| **`multi-agent-mux-loop`** | Plan, implement, review: an optional planner and a creator, then one or all reviewers until a unanimous `PASS` or `--max-loop`. It also supports `--review-only` for work finished outside the loop. Passing reports are promoted to `.agents/reports/<session>/`. |
|
| **`multi-agent-mux-loop`** | Plan, implement, review: an optional planner and a creator, then the selected reviewers (`--reviewer A,B`, or `--all-reviewer` for every running reviewer except the creator) until all of them `PASS` or `--max-loop` is reached. With neither option the creator reviews its own work. `--review-only` reviews work finished outside the loop. Passing reports are promoted to `.agents/reports/<session>/`. |
|
||||||
| **`multi-agent-mux-orc-onboard`** | Registers the orchestrator's own session id, so session discovery never mistakes it for a sub-agent. |
|
| **`multi-agent-mux-orc-onboard`** | Registers the orchestrator's own session id, so session discovery never mistakes it for a sub-agent. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📐 Architecture
|
## 📐 Architecture
|
||||||
|
|
||||||
MAM has two layers that share one registry:
|
MAM has two layers, each with its own registry:
|
||||||
|
|
||||||
1. **Sessions (herdr)**: `lib.sh` and the create/stop/resume/status/monitor scripts run the agents and keep `.mam/agent-sessions.yaml` (+ `.mam/agent-sessions.db`) authoritative.
|
1. **Sessions (herdr)**: `lib.sh` and the create/stop/resume/status/monitor scripts run the agents and keep `.mam/agent-sessions.yaml` (+ `.mam/agent-sessions.db`) authoritative.
|
||||||
2. **Jobs (delegation)**: `lib_py/delegation/` registers a job in `.mam/jobs/<id>.json`, injects the brief, and follows the job's events over MQTT. Every event is also appended to the job's audit log `.mam/delegate_job_logs/<id>/`.
|
2. **Jobs (delegation)**: `lib_py/delegation/` registers a job in `.mam/jobs/<id>.json`, injects the brief, and follows the job's events over MQTT. Every event is also appended to the job's audit log `.mam/delegate_job_logs/<id>/`.
|
||||||
@@ -87,15 +87,15 @@ MAM has two layers that share one registry:
|
|||||||
|
|
||||||
### 🔒 Herdr server isolation
|
### 🔒 Herdr server isolation
|
||||||
|
|
||||||
- **Dedicated server per workspace (opt-in).** `create --herdr-session <name>` (or `HERDR_SESSION_NAME`) runs the agents on their own herdr server and socket, separate from your default herdr. The server name is recorded on the session row, and every later command routes to it.
|
- **A dedicated server per workspace.** By default `create` runs the agents on a herdr server named after the workspace (its slug), with its own socket, separate from your default herdr. `--herdr-session <name>` or `HERDR_SESSION_NAME` picks another name. The server name is recorded on the session row, and every later command routes to it.
|
||||||
- **The shim.** Sourcing `lib.sh` writes `<workspace>/.mam/shim/herdr` and puts it first on `PATH`. The shim provides the tmux-style commands the scripts use (`new-session`, `has-session`, …) on top of herdr's agent API. It routes each call with `--session <server>`, and it starts the server headless if the server is not running.
|
- **The shim.** Sourcing `lib.sh` writes `<workspace>/.mam/shim/herdr` and puts it first on `PATH`. The shim provides the tmux-style commands the scripts use (`new-session`, `has-session`, …) on top of herdr's agent API. For a named server it adds `--session <server>` to each call, and it starts that server headless if it is not running. It does not start one when `MAM_NO_AUTOSTART=1`, which `status` and `monitor` set, so looking never starts a server. Calls for the `default` server go to your ambient herdr unchanged.
|
||||||
- **No surprise relaunches.** A server MAM starts uses your herdr config with `resume_agents_on_restore = false`. A herdr restart therefore does not relaunch saved agents without their permission flags.
|
- **No surprise relaunches.** A server MAM starts normally uses your herdr config plus `resume_agents_on_restore = false`. A herdr restart therefore does not relaunch saved agents without their permission flags. If that config cannot be built, the bootstrap prints a warning and the server starts with restore on.
|
||||||
- **One pane claim at a time.** Pane placement holds a per-server lock (`lib_py/pane_lock.py`, `flock` on macOS and Linux). Concurrent `create` calls in one folder each get their own pane.
|
- **One pane claim at a time.** Pane placement holds a per-server lock (`lib_py/pane_lock.py`, `flock` on macOS and Linux). Concurrent `create` calls in one folder each get their own pane.
|
||||||
|
|
||||||
### 🛡️ Registry writes and concurrency
|
### 🛡️ Registry writes and concurrency
|
||||||
|
|
||||||
- **Serialised writes.** Every change to `agent-sessions.yaml` goes through `lib.sh::atomic_dump_yaml`. It runs under an exclusive SQLite transaction (`BEGIN IMMEDIATE`) on `.mam/agent-sessions.db` and is validated before the YAML is rewritten atomically. Job records use their own registry lock.
|
- **Serialised writes.** Every change to `agent-sessions.yaml` goes through `lib.sh::atomic_dump_yaml`. It runs under an exclusive SQLite transaction (`BEGIN IMMEDIATE`) on `.mam/agent-sessions.db` and is validated before the YAML is rewritten atomically. Job records use their own registry lock.
|
||||||
- **One interpreter.** The scripts run their Python in the workspace `.venv` (`AGENT_PYTHON_BIN`, else the active or nearest `.venv`).
|
- **Python interpreter.** Registry writes and the delegation helpers pick their Python in this order: `AGENT_PYTHON_BIN`, the active virtualenv, the nearest `.venv` above the skills, then `python3` on `PATH`. Some shell paths, such as the shim and parts of `create`, call `python3` from `PATH` directly.
|
||||||
- **Network filesystems.** If `.mam/` is on NFS/CIFS/SSHFS, `lib.sh` warns, and SQLite switches its journal from `WAL` to `DELETE`.
|
- **Network filesystems.** If `.mam/` is on NFS/CIFS/SSHFS, `lib.sh` warns, and SQLite switches its journal from `WAL` to `DELETE`.
|
||||||
- **One loop per checkout.** A loop lock refuses a second `run_loop.sh` in the same control plane.
|
- **One loop per checkout.** A loop lock refuses a second `run_loop.sh` in the same control plane.
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ The full specification is in [MESSAGING.md §2.5](MESSAGING.md).
|
|||||||
| `1` | failure, or no `started` within T |
|
| `1` | failure, or no `started` within T |
|
||||||
| `2` | no event for K×N (`deadline_timeout`) or budget spent (`wall_clock_timeout`) |
|
| `2` | no event for K×N (`deadline_timeout`) or budget spent (`wall_clock_timeout`) |
|
||||||
| `3` | broker unavailable, and no terminal status on disk |
|
| `3` | broker unavailable, and no terminal status on disk |
|
||||||
| `5` | session busy, or the record was taken over while waiting (never delivered) |
|
| `5` | not delivered (`cancelled`): the session was busy, the target was not found, or the record was taken over while waiting |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -157,9 +157,10 @@ sequenceDiagram
|
|||||||
|
|
||||||
## 🔐 Security
|
## 🔐 Security
|
||||||
|
|
||||||
- **Signed events**: every job gets its own token (`auth_token`). Each event and each retained state message carries an HMAC-SHA256 signature made with it, and the subscriber drops anything that fails verification. The token itself never goes over the wire.
|
- **Signed events**: a job gets its own token (`auth_token`) when you pass one, or automatically when the broker uses TLS or a username. Each event and each retained state message then carries an HMAC-SHA256 signature made with it, and the subscriber drops anything that fails verification. Events and state messages carry only the signature, not the token. On a broker with neither TLS nor a username (the PoC setup), a job has no token and signatures are not checked.
|
||||||
|
- **Remote dispatch carries the token**: the `remote:<name>` dispatch message includes the job's token (signed with `MAM_DISPATCH_SECRET`, not encrypted). Use it only on an access-controlled private broker.
|
||||||
- **Replay and ordering**: each event carries an increasing `seq`. The subscriber accepts only sequence numbers higher than any it has seen and records gaps as `seq_gap`. Timestamps are informational only.
|
- **Replay and ordering**: each event carries an increasing `seq`. The subscriber accepts only sequence numbers higher than any it has seen and records gaps as `seq_gap`. Timestamps are informational only.
|
||||||
- **Credentials**: broker credentials live only in `.mam.env` (never in a job record). Remote dispatch is signed with `MAM_DISPATCH_SECRET`.
|
- **Credentials**: broker credentials live only in `.mam.env`, never in a job record.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -171,10 +172,9 @@ sequenceDiagram
|
|||||||
|
|
||||||
- The suite has about 1,900 tests, and a full run takes over an hour.
|
- The suite has about 1,900 tests, and a full run takes over an hour.
|
||||||
- It runs its own in-process MQTT broker, so it never needs `.mam.env`. Don't export `.mam.env` for tests.
|
- It runs its own in-process MQTT broker, so it never needs `.mam.env`. Don't export `.mam.env` for tests.
|
||||||
- Tests run in sandboxes:
|
- Tests run in sandboxes, with a temporary HOME, registry and audit-log directory, and a mock herdr:
|
||||||
- they never write to the checkout's `.mam/`, your agent homes (`~/.claude/projects`, `~/.codex`, `~/.gemini`, …) or your herdr config;
|
- the tests that drive a real herdr start their own throwaway herdr server;
|
||||||
- a test that does not use the mock herdr starts its own throwaway herdr server;
|
- after the run, session-level guards check the real control plane and your agent homes (`~/.claude/projects`, `~/.codex`, `~/.gemini`, …) and fail the run if a test left entries there.
|
||||||
- session-level guards fail the run if a test leaves files behind in the real control plane or in an agent home.
|
|
||||||
- Some tests are platform-specific. A few need Python 3.11+ (`tomllib`), Linux (`/proc`) or the `nats-docker` submodule; elsewhere they are skipped.
|
- Some tests are platform-specific. A few need Python 3.11+ (`tomllib`), Linux (`/proc`) or the `nats-docker` submodule; elsewhere they are skipped.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -219,16 +219,18 @@ bash deploy/install.sh
|
|||||||
|
|
||||||
# 2. Start a creator and a reviewer on a dedicated herdr server
|
# 2. Start a creator and a reviewer on a dedicated herdr server
|
||||||
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
||||||
--workspace "$PWD" --agent claude --role developer --herdr-session my-project --onboard
|
--workspace "$PWD" --agent claude --role developer --session creator-claude-01 \
|
||||||
|
--herdr-session my-project --onboard
|
||||||
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
||||||
--workspace "$PWD" --agent codex --role reviewer --herdr-session my-project --onboard
|
--workspace "$PWD" --agent codex --role reviewer --session reviewer-codex-01 \
|
||||||
|
--herdr-session my-project --onboard
|
||||||
|
|
||||||
# 3. Check the sessions
|
# 3. Check the sessions
|
||||||
bash .agents/skills/multi-agent-mux-status/scripts/status.sh
|
bash .agents/skills/multi-agent-mux-status/scripts/status.sh
|
||||||
|
|
||||||
# 4. Run a task through the loop
|
# 4. Run a task through the loop
|
||||||
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||||
--creator <creator-session> --all-reviewer --task "Fix the typo in docs/guide.md"
|
--creator creator-claude-01 --all-reviewer --task "Fix the typo in docs/guide.md"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in new issue
Block a user