diff --git a/.agents/AGENT.ko.md b/.agents/AGENT.ko.md new file mode 100644 index 0000000..a393206 --- /dev/null +++ b/.agents/AGENT.ko.md @@ -0,0 +1,169 @@ +# AGENT.md + +본 문서는 새로운 프로젝트에 **MQTT 메시징 백플레인 및 Tmux 기반 멀티 에이전트 오케스트레이션 워크플로우**를 도입하고, 협업하는 에이전트들이 일관된 규칙과 아키텍처에 따라 안전하고 견고하게 작업을 수행할 수 있도록 정의한 공통 지침 및 규약입니다. + +새로운 프로젝트에서 작업하는 모든 에이전트는 작업을 시작하기 전 이 문서를 반드시 정독하고 규약을 준수해야 합니다. + +--- + +## 1. 에이전트의 역할 정의 (Agent Roles) + +역할군 간의 책임 및 권한을 명확히 분리하여 병목을 줄이고 작업의 완성도를 높입니다. + +### 👑 General Manager (총괄 매니저) +- **주요 책무**: 사용자와 직접 소통하여 요구사항 접수, 상세 작업 계획 수립, 팀장 에이전트 할당 및 작업 위임, 전체 워크플로우 통제 및 최종 완료 보고. +- **모호성 제거**: 사용자의 요구사항에 모호한 부분이 있다면 작업을 추측하여 진행하지 말고, 즉시 사용자에게 질문하여 명확히 해야 합니다 (`/grill-me` 슬래시 명령어 권장). + +### 👥 Team Leaders (팀장) +새롭게 생성되는 에이전트(`antigravity`, `claude`, `cline`, `hermes` 등)는 각 팀의 **팀장** 역할을 수행합니다. 총괄 매니저로부터 작업을 위임받아 개발 또는 리뷰 워크플로우를 주도합니다. +- **Developer Team Leader (개발 팀장)**: + - 총괄 매니저로부터 작업을 위임받습니다. + - **작업 분석 및 계획**: 주어진 작업을 철저히 분석하고, 작은 단위로 문제를 나누어 세부 계획을 수립합니다. + - **내부 병렬 처리**: 내부적으로 subagent를 활용해 위임받은 작업을 병렬적으로 처리할 수 있습니다. + - **리뷰 타당성 검증 및 거부**: 리뷰어가 지적한 피드백을 면밀히 검토합니다. 타당한 제안은 수렴하여 코드를 수정하지만, 타당하지 않다고 판단되는 안건은 반영하지 않고 **그 명확한 이유를 작성하여 리뷰어에게 되돌려 보냅니다**. + - **완료 신호 송신**: 모든 리뷰어들로부터 `PASS`를 획득하고 변경 사항이 검증되면, 최초 작업을 위임받았던 개발 팀장이 총괄 매니저에게 최종 작업 완료 신호를 송신합니다. +- **Reviewer Team Leader (리뷰어 팀장)**: + - 개발 팀장으로부터 리뷰 요청을 접수합니다. + - **문제 제시에 대한 이유와 개선 방향 포함**: 단순한 반려(`NOT PASS`) 통보는 금지됩니다. 이슈를 제기할 때는 **반드시 해당 문제가 발생하는 구체적인 이유와 확실한 개선 방향(코드 대안 포함)을 함께 작성**해야 합니다. + - **합의 루프**: 모든 지적 사항이 해결되고 최종 `PASS`를 발행할 때까지 리뷰 루프에 동참합니다. + +### 🛡️ 역할 범위 준수 원칙 (Role Suitability Check) +- 모든 에이전트는 자신에게 부여된 역할에 부합하는 작업만을 수행해야 합니다. (예: 개발 팀장은 최종 PASS 여부를 결정하지 않으며, 리뷰어 팀장은 직접 프로젝트 소스코드를 작성하지 않습니다.) +- **자신의 역할에 맞지 않는 작업이 지시된 경우**, 에이전트는 반드시: + 1. 해당 작업을 수행하기에 가장 적합한 에이전트 세션을 추천하여 위임을 유도하거나, + 2. 프로젝트 연속성을 위해 극히 필요한 경우 직접 작업을 수행합니다. + +--- + +## 2. 메시징 백플레인 & 레지스트리 규약 + +에이전트 간의 비동기 소통과 상태 관리는 분산 이벤트 채널 및 파일/DB 레지스트리를 통해 제어됩니다. + +### 📡 MQTT 백플레인 (MQTT Backplane) +- **이벤트 라이프사이클**: + - `started` (작업 개시) ➡️ `progress`/`permission_required` (진행 상황 공유) ➡️ `completed` (성공 종료) 또는 `error` (실패 종료) + - `completed` 및 `error`는 단 한 번만 발행되는 단말(Terminal) 이벤트입니다. +- **메시지 발행/구독 규칙**: + - MQTT는 영속 큐를 보장하지 않으므로, 에이전트 구동 전 **반드시 구독자(`job_subscriber.py`)가 먼저 백그라운드에서 대기**해야 합니다 (Subscribe-before-Publish 원칙). + - 단말 이벤트 발행 시 브로커에 `retain=True`로 영속화하여 늦게 합류한 구독자도 최종 상태를 읽을 수 있도록 조치합니다. + - 전송 데이터에는 비밀번호, 개인키 등의 중요 비밀 정보나 절대 경로가 포함되지 않도록 보편화(Generalised)해야 합니다. + +### 🗃️ 레지스트리 및 상태 관리 +- 본 아키텍처는 목적에 따라 두 가지 레지스트리를 분리하여 운영합니다: + - **잡 레지스트리 (Job Registry)**: 각 비동기 잡의 메타데이터와 생명주기는 개별 JSON 파일(`.mam/jobs/.json`)로 기록되며, 다중 세션 간의 동시 청구(claiming) 경합은 파일 단위의 `fcntl` advisory lock(`registry_lock` via `registry.py`)을 통해 방어합니다. + - **세션 레지스트리 (Session Registry)**: TMUX 모니터링 상태 및 에이전트 구동 정보는 SQLite WAL 데이터베이스(`.mam/agent-sessions.db`)를 통해 단일 호스트 내에서 안정적인 동시 트랜잭션으로 일관되게 제어합니다. 단, SQLite WAL 모드는 NFS(네트워크 파일 시스템) 환경에서는 완전한 파일 락이 보장되지 않으므로 로컬 파일 시스템 사용을 권장합니다. + +### 🛡️ 보안 프로토콜 (HMAC-SHA256) +- **무인증 PoC 모드**: 잡 레지스트리 생성 시 `auth_token`이 `null`로 지정된 경우(PoC 기본 모드), 별도의 서명 검증을 생략하고 모든 이벤트를 수용합니다 (`verify_hmac`이 항상 `True`를 반환). +- **인증 Production 모드**: 실배포 환경이나 인증이 필요한 연동 단계에서는 각 잡마다 고유 암호화 토큰(`auth_token`)을 발급합니다. 퍼블리셔는 이 토큰을 키로 삼아 `hmac_sig` 서명을 페이로드에 동반해야 하며, 수신단(`verify_hmac`)에서 서명이 없거나 일치하지 않는 메시지는 즉시 드랍하여 다운그레이드 공격을 원천 차단합니다. +- **롤아웃 전략**: 보안 스킴 갱신 시 송수신 노드 간 불일치로 인한 이벤트 드랍을 피하기 위해, 과도기적 하이브리드 포맷 전송(평문 유출 위험 있음)을 배제하고 **모든 노드를 일제히 업데이트하는 "동시 롤아웃(Simultaneous Rollout)"**을 채택해야 합니다. + +--- + +## 3. 협업 워크플로우 실행 절차 (Workflow Loop) + +```mermaid +sequenceDiagram + autonumber + actor User as 사용자 + participant GM as General Manager + participant DTL as Developer Team Leader + participant RTL as Reviewer Team Leaders + participant M as MQTT Backplane + + User->>GM: 요구사항 전달 + GM->>DTL: 작업 위임 (예: 랜딩 페이지 제작) + Note over DTL: 작업 분석, 세분화 및 subagent 병렬 구동 + DTL->>M: 'started' 이벤트 발행 + Note over DTL: 코드 변경 및 구현 + DTL->>M: 'completed' 발행 + DTL->>RTL: 리뷰 요청 (랜딩 페이지를 제작했습니다. 리뷰를 진행해주세요) + Note over RTL: 교차 분석 & 검증 + alt 결함 발견 (리뷰어 피드백) + RTL->>DTL: NOT PASS / 피드백 (반드시 이유와 확실한 개선 방향 포함) + Note over DTL: DTL이 피드백의 타당성 검증 + alt 타당한 피드백 + Note over DTL: DTL이 수용하여 코드 수정 + else 타당하지 않은 피드백 + DTL->>RTL: 반론 및 거부 이유 전달 (부적절한 항목 미반영) + end + DTL->>RTL: 재리뷰 요청 (리뷰 안건 수정 완료) + else 검증 통과 + RTL->>DTL: PASS + end + DTL->>GM: 최종 완료 신호 송신 + GM->>User: 사용자에게 작업 완료 통보 +``` + +1. **계획 수립 및 할당**: 총괄 매니저는 개발 팀장에게 작업을 인가합니다. +2. **분석 및 내부 실행**: 개발 팀장은 작업을 분석하고 세분화하여 계획을 세운 뒤 내부 subagent를 가동하여 구현을 완료합니다. 이후 `started`를 거쳐 `completed` 이벤트를 발행하고 리뷰어에게 검수를 요청합니다. +3. **이의 제기 및 정제 루프**: + - 리뷰어 팀장은 상세 피드백 시 반드시 이유와 보완 방향을 제시해야 합니다. + - 개발 팀장은 의견을 검토해 타당하면 수정하고, 타당하지 않으면 반론과 근거를 회신합니다. + - 리뷰어 전원이 `PASS`를 인가할 때까지 이 과정이 반복됩니다. +4. **최종 보고**: 개발 팀장이 총괄 매니저에게 완료 신호를 보내면 총괄 매니저가 사용자에게 완료를 알립니다. + +--- + +## 4. 분석 인프라 패턴 & 실무 가이드 (Infra Patterns) + +장기 실행 에이전트 분석 중 발생하는 유실 및 인프라적 장애를 예방하기 위한 중요 지침입니다. + +### 📸 TUI 뷰포트 절단 방지 (Pane Snapshotting 3대 규칙) +TMUX 환경에서 실행되는 에이전트가 화면 스크롤 한계로 인해 이전 출력이나 장문의 디버깅 로그를 잃지 않도록 아래의 **스냅샷 패턴을 의무적으로 수행**합니다. +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 상태를 마지막으로 캡처하여 전체 작업 궤적을 보존합니다. + +### 📄 장문 브리핑 전달 방식 +- TMUX `send-keys`나 입력 버퍼를 통해 수백 줄의 장문 지시나 프롬프트를 직렬로 입력하면, 에이전트의 TUI가 이를 모두 온전히 소화하지 못하고 일부 문자나 문단이 탈취/누락될 수 있습니다. +- **해결 지침**: 지시 사항이 긴 경우, 반드시 `/tmp/brief-.md` 등의 파일 경로로 지시문을 별도 작성해 전달하고, 에이전트에는 `"Read /tmp/brief-... and execute"` 라는 단순화된 실행 명령만 전달하십시오. + +### ⏱️ 타임아웃 구성 및 정렬 규칙 +- **잡 실행 제한 (`timeout_sec` & `idle_timeout_sec`)**: 각 잡은 전체 실행 만료 시간(`timeout_sec`, 기본 3600s)과 메세지 미수신 유휴 시간(`idle_timeout_sec`, 기본 120s)을 독립적으로 가집니다. +- **모니터 유휴 대기 (`SUB_IDLE_TIMEOUT`)**: 모니터 스크립트(`reconcile.sh`)의 유휴 대기 시간(`SUB_IDLE_TIMEOUT`) 기본값은 잡 최대 예산에 맞춰 `3600s`(1시간) 이상으로 항상 넉넉히 설정해야 합니다. 모니터가 작업 완료 전에 유휴 감지로 조기 자동 종료되어 백그라운드 태스크 관리를 소실하는 문제를 방지하기 위함입니다. + +--- + +## 5. 새 프로젝트 적용 체크리스트 (Setup Checklist) + +새 프로젝트에 이 에이전트 오케스트레이션 모델을 구축할 때의 체크리스트입니다. + +- [ ] **가상환경 의존성**: `pyyaml`, `paho-mqtt` 등 필요한 Python 패키지가 `.venv` 또는 `requirements.txt`에 포함되었는가? +- [ ] **환경 설정 파일**: MQTT 브로커 주소 및 보안 Credential이 `.env` 파일에 안전하게 로드되고 공유되는가? +- [ ] **디렉토리 규약**: 레지스트리 경로(`.mam/jobs/`) 및 로깅 경로(`.mam/delegate_job_logs/`)가 `.gitignore`에 등록되었는가? +- [ ] **스크립트 구비**: `mqtt_common.py`, `publish_event.py`, `job_subscriber.py`, `registry.py` 등의 핵심 모듈이 배치되었는가? +- [ ] **HMAC 활성화**: 새로운 레지스트리 잡 발급 시 난수 기반의 `auth_token`이 정상적으로 주입되고, 서명 기반의 상호 인증이 활성화되는가? +- [ ] **운영 헌장 배치**: 본 규약 파일(`AGENT.md`)이 새 프로젝트의 **.agents/ 디렉터리**에 배치되었는가? (프로젝트 루트를 깔끔하게 유지하면서도 온보딩하는 에이전트들이 규칙을 이해할 수 있도록 `.agents/` 경로 배치가 권장됩니다.) + +--- + +*본 가이드는 협업 효율성과 코드 보안의 엄격한 균형을 유지하기 위한 규범입니다. 변경 사항이 필요한 경우 총괄 매니저 및 전체 팀장의 합의를 거쳐 본 문서를 업데이트해야 합니다.* + +--- + +## 6. AIoT 멀티 에이전트 인터페이스 모듈 설계 지침 (A2A 및 Agent Card) + +향후 연구 및 개발 단계에서 구현할 통합 인터페이스 모듈(Unified Interface Module)은 이종 에이전트 간의 자율 협업 및 상호운용성 확보를 위해 **A2A(Agent-to-Agent)** 표준을 포함하며, 다음의 설계 지침을 철저히 준수해야 합니다. + +### 📇 1. 에이전트 카드(Agent Card)의 표준 지원 +- **규격화된 JSON 스키마**: 인터페이스 모듈은 에이전트의 메타데이터 및 입출력 인터페이스를 정의한 에이전트 카드를 JSON 형태로 자동 생성 및 서빙해야 합니다. +- **필수 명세 항목**: + - 에이전트의 명칭 및 페르소나/역할 (Name, Persona, Role) + - 호출 가능한 엔드포인트 정보 (Endpoint URI 및 프로토콜 규격) + - 입력 데이터 스키마 (Tool Schema 및 입출력 제약사항) + - 보안 인증 및 인가 사양 (Authentication & Security Credentials) +- **배포 및 탐색 규격**: 시스템 내의 다른 이종 에이전트들이 실시간으로 탐색할 수 있도록 `/.well-known/agent-card.json` 표준 경로를 통한 에이전트 카드 서빙 인프라를 내장해야 합니다. + +### 📡 2. A2A 및 ACP 프로토콜 커넥터 설계 +- **프로토콜 상호운용성**: IBM ACP(Agent Communication Protocol)와 Linux Foundation A2A 표준의 병합 사양에 기반하여, 에이전트 간 비동기 위임(Task Delegation), 제어권 이관, 상태 동기화가 가능한 프로토콜 커넥터를 인터페이스 모듈 내에 구현해야 합니다. +- **이벤트 기반 자율 제어**: 상시 센서 모니터링은 경량 물리 노드 및 모니터 프로그램이 수행하도록 분리하고, 감지된 물리 이벤트는 A2A 프로토콜 메시지를 통해 비동기적으로 특정 AI 에이전트에 작업을 자율 위임하고 처리 후 즉시 생명주기 및 제어권을 회수하는 비동기 인터페이스를 탑재해야 합니다. + +### 🛠️ 3. 워크플로우 정합성 및 이슈 추적 +- **실행 일관성 보장**: 이종 에이전트가 동일한 작업을 재수행할 때 동일한 결과를 얻을 수 있도록 워크플로우 실행을 원자적(Atomic)으로 통제해야 합니다. +- **표준 이슈 트래킹**: 실행 예외나 네트워크 단절 등의 장애가 발생한 경우, 복구 및 추적이 용이하도록 표준 규격화된 이슈 추적 인터페이스(Issue Tracking Interface)를 연동 및 마련해야 합니다. + +### 🛡️ 4. 멱등성(Idempotency) 보장 스키마 +- **물리 제어 명령의 멱등성 분류**: 인터페이스 모듈은 전달되는 제어 명령을 멱등성 보장 여부에 따라 분류해야 합니다. 정보 조회·상태 설정 등 멱등성이 자연 보장되는 명령과, 농약 투포·급수·밸브 개폐 등 부작용이 누적되어 재실행 시 치명적 안전 사고로 이어질 수 있는 비멱등성(non-idempotent) 물리 제어 명령을 명시적으로 구분해야 합니다. +- **중복 발행 억제 메커니즘**: 비동기 재시도 루프에서 동일한 제어 의도가 새로운 작업 ID로 중복 발행되는 것을 방지하기 위해, 제어 의도 식별자(Control Intent Key) 기반의 중복 억제(Deduplication) 스키마를 인터페이스 계층에 내장해야 합니다. 재시도 시 신규 작업 ID가 아닌 원본 제어 의도에 바인딩된 멱등성 키를 재사용하여 동일 제어의 재실행 가능성을 시스템 차원에서 통제해야 합니다. +- **물리 장치측 사전 검증**: 중복 제어가 물리 장치에 도달하기 전에 인터페이스 모듈이 사전 검증(pre-flight check)을 수행하여, 이미 실행된 제어 의도에 대한 재실행 요청을 사전 차단하거나 장치측 상태와 대조 후 승인하는 안전망을 제공해야 합니다. \ No newline at end of file diff --git a/.agents/AGENT.md b/.agents/AGENT.md new file mode 100644 index 0000000..173ea0f --- /dev/null +++ b/.agents/AGENT.md @@ -0,0 +1,169 @@ +# AGENT.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. + +All agents working on a new project must read this document thoroughly and comply with the defined protocols before starting any tasks. + +--- + +## 1. Agent Roles Definition (Agent Roles) + +We clearly separate responsibilities and permissions between roles to reduce bottlenecks and enhance the quality of execution. + +### 👑 General Manager (Orchestrator) +- **Core Responsibility**: Interact directly with the user, receive high-level requirements, establish task plans, delegate tasks to Team Leaders, control the overall workflow, and report completion back to the user. +- **Ambiguity Resolution**: If a user's requirements contain ambiguous details, do not guess. Immediately ask the user for clarification (we recommend using the `/grill-me` slash command). + +### 👥 Team Leaders (팀장) +Newly spawned agents (e.g., `antigravity`, `claude`, `cline`, `hermes`) act as **Team Leaders** of their respective groups. They receive delegated tasks from the General Manager and manage implementation or review workflows. +- **Developer Team Leader (개발 팀장)**: + - Receives tasks from the General Manager. + - **Task Breakdown & Planning**: Thoroughly analyzes the task, breaks it down into small units, and creates a plan. + - **Internal Parallelism**: Can run subagents in parallel internally to handle the delegated work. + - **Review Integrity & Refusal**: Thoroughly reviews feedback from Reviewers. Adopts/implements recommendations if valid. If any recommendation is judged invalid, the Developer Team Leader must **not** implement it, but instead return the refutation along with detailed reasons to the Reviewer. + - **Completion Signal**: Once all reviewers yield a `PASS` and changes are verified, the Developer Team Leader who first received the task sends a completion signal back to the General Manager. +- **Reviewer Team Leader (리뷰어 팀장)**: + - Receives review requests from the Developer Team Leader. + - **Detailed Feedback with Directions**: Simply rejecting changes (`NOT PASS`) is forbidden. Reviewers **must** specify the exact reason for the issue and provide a concrete, stable, and verified alternative direction for improvement. + - **Consensus Loop**: Engages in the review cycle until all objections are resolved and a final `PASS` is issued. + +### 🛡️ Role Suitability Check Principle (자신의 역할 범위 수행 원칙) +- Every agent must only perform tasks suitable for its designated role (e.g., Developer Team Leaders do not issue final reviews, and Reviewer Team Leaders do not write project code). +- **If an agent receives a task that does not fit its role**, it must either: + 1. Recommend the optimal agent session to delegate the task to, or + 2. Perform the task directly if strictly necessary for project continuity. + +--- + +## 2. Messaging Backplane & Registry Protocol + +Asynchronous communication and state management between agents are controlled via distributed event channels and file/DB registries. + +### 📡 MQTT Backplane +- **Event Lifecycle**: + - `started` (Job execution starts) ➡️ `progress`/`permission_required` (Share intermediate progress) ➡️ `completed` (Successful termination) or `error` (Failed termination) + - `completed` and `error` are terminal events that are published exactly once. +- **Publish/Subscribe Rules**: + - Since MQTT does not guarantee persistent queues, the subscriber (`job_subscriber.py`) **must be running in the background before the agent starts** (the Subscribe-before-Publish principle). + - When publishing terminal events, publish with `retain=True` on the broker so that subscribers joining late can still read the final state. + - Generalize all transmitted data to ensure that sensitive secrets like passwords, private keys, or absolute system paths are not included. + +### 🗃️ 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/.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. + +### 🛡️ 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`). +- **Authenticated Production Mode**: In production environments or integrations requiring authentication, a unique cryptographic token (`auth_token`) is issued for each job. The publisher must include an `hmac_sig` signature in the payload keyed by this token, and the receiving end (`verify_hmac`) will immediately drop messages that lack a signature or have mismatching signatures to prevent downgrade attacks. +- **Rollout Strategy**: To avoid event drops caused by inconsistencies between publishing and receiving nodes when updating security schemes, hybrid transition formats (which risk leaking plaintext tokens) must not be used. Instead, adopt a **"Simultaneous Rollout"** where all nodes are updated at once. + +--- + +## 3. Collaborative Workflow Execution Loop (Workflow Loop) + +```mermaid +sequenceDiagram + autonumber + actor User as User + participant GM as General Manager + participant DTL as Developer Team Leader + participant RTL as Reviewer Team Leaders + participant M as MQTT Backplane + + User->>GM: Hand over requirements + GM->>DTL: Delegate task (e.g., create landing page) + Note over DTL: Analyze, breakdown & spawn parallel subagents + DTL->>M: Publish 'started' event + Note over DTL: Modify code & implement + DTL->>M: Publish 'completed' + DTL->>RTL: Request review (I created landing page. Please review it) + Note over RTL: Cross-analysis & verification + alt Defect Found (Reviewer feedback) + RTL->>DTL: NOT PASS / Feedback (Must include reason & improvement direction) + Note over DTL: DTL checks validity of suggestions + alt Valid feedback + Note over DTL: DTL adopts and modifies code + else Invalid feedback + DTL->>RTL: Send refutation & reasons (Did not reflect inappropriate parts) + end + DTL->>RTL: Request review again (Modified review items) + else Verification Pass + RTL->>DTL: PASS + end + DTL->>GM: Send completion signal + GM->>User: Notify task completion +``` + +1. **Planning and Allocation**: The General Manager delegates the task to the Developer Team Leader. +2. **Analysis and Internal Execution**: The Developer Team Leader analyzes the task, breaks it down, plans execution, and optionally spawns parallel subagents. It publishes `started`, completes the task, and requests review from the Reviewer Team Leader. +3. **Objection & Refinement Loop**: + - The Reviewer Team Leader must provide clear reasons and improvement directions for any issues. + - The Developer Team Leader validates the feedback. Valid suggestions are implemented; invalid ones are refuted with reasons and returned to the reviewer. + - This cycle repeats until all reviewers issue a `PASS`. +4. **Completion and Report**: The Developer Team Leader sends the final completion signal to the General Manager, who notifies the user. + +--- + +## 4. Analysis Infrastructure Patterns & Practical Guide (Infra Patterns) + +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**: +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. + +### 📄 Handling Long Briefing Instructions +- Sending long instructions or prompts (hundreds of lines) sequentially via TMUX `send-keys` or input buffers can overwhelm the agent's TUI, leading to lost characters or truncated paragraphs. +- **Resolution**: If instructions are long, write them separately to a file path (e.g., `/tmp/brief-.md`) and send a simplified execution command to the agent: `"Read /tmp/brief-... and execute"`. + +### ⏱️ Timeout Configuration & Alignment Rules +- **Job Execution Limits (`timeout_sec` & `idle_timeout_sec`)**: Each job independently manages its overall execution timeout (`timeout_sec`, default 3600s) and idle timeout without receiving messages (`idle_timeout_sec`, default 120s). +- **Monitor Idle Waiting (`SUB_IDLE_TIMEOUT`)**: The idle timeout for the monitor script (`reconcile.sh`), `SUB_IDLE_TIMEOUT`, must always be set generously to `3600s` (1 hour) or more to align with the maximum job budget. This prevents the monitor from terminating early due to idle detection, which would lose control over background tasks before they finish. + +--- + +## 5. Setup Checklist for New Projects (Setup Checklist) + +Use this checklist when deploying this agent orchestration model to a new project: + +- [ ] **Virtualenv Dependencies**: Are required Python packages like `pyyaml` and `paho-mqtt` included in `.venv` or `requirements.txt`? +- [ ] **Configuration File**: Are the MQTT broker address and security credentials safely loaded and shared via the `.env` file? +- [ ] **Directory Convention**: Are the registry path (`.mam/jobs/`) and logging path (`.mam/delegate_job_logs/`) added to `.gitignore`? +- [ ] **Core Scripts**: Are the core scripts (`mqtt_common.py`, `publish_event.py`, `job_subscriber.py`, and `registry.py`) in place? +- [ ] **HMAC Enablement**: When a new registry job is created, is a random `auth_token` correctly injected, and is signature-based mutual authentication active? +- [ ] **Charter Placement**: Is this protocol file (`AGENT.md`) placed in the **.agents/ directory** of the new project? (Placing it in `.agents/` is essential to keep the project root clean while allowing onboarding agents to align on the rules.) + +--- + +*This guide balances collaboration efficiency with strict code security. Any required changes must be discussed and agreed upon by the General Manager and all Team Leaders before updating this document.* + +--- + +## 6. AIoT Multi-Agent Interface Module Design Guidelines (A2A and Agent Card) + +The Unified Interface Module to be implemented in future research and development stages must incorporate the **A2A (Agent-to-Agent)** standard to achieve autonomous collaboration and interoperability among heterogeneous agents, and must comply with the following design guidelines: + +### 📇 1. Standard Support for Agent Cards +- **Structured JSON Schema**: The interface module must automatically generate and serve Agent Cards defining the agent's metadata and input/output interfaces in JSON format. +- **Mandatory Specifications**: + - Agent Name and Persona/Role (Name, Persona, Role) + - Callable Endpoint Information (Endpoint URI and Protocol Specifications) + - Input Data Schema (Tool Schema and I/O constraints) + - Authentication and Authorization Specifications (Authentication & Security Credentials) +- **Deployment and Discovery Specification**: An Agent Card serving infrastructure must be built-in to serve the metadata via the standard path `/.well-known/agent-card.json`, allowing other heterogeneous agents in the system to discover it in real-time. + +### 📡 2. A2A and ACP Protocol Connector Design +- **Protocol Interoperability**: Implement a protocol connector inside the interface module based on the merged specifications of IBM ACP (Agent Communication Protocol) and Linux Foundation A2A standards to handle task delegation, control transfer, and state synchronization. +- **Event-Driven Autonomous Control**: Decouple sensor monitoring to be executed by lightweight physical nodes and monitoring programs, and implement an asynchronous interface that delegates tasks to specific AI agents upon detecting physical events via A2A protocol messages, reclaiming lifecycles and control rights immediately upon task completion. + +### 🛠️ 3. Workflow Coherence and Issue Tracking +- **Execution Consistency**: Control workflow execution atomically to guarantee identical outputs when heterogeneous agents re-execute the same tasks. +- **Standardized Issue Tracking**: Provide a standardized Issue Tracking Interface to support debugging, tracking, and recovery in case of execution exceptions or network disconnections. + +### 🛡️ 4. Idempotency Assurance Schema +- **Idempotency Classification of Physical Control Commands**: The interface module must classify control commands by their idempotency guarantees. It must explicitly distinguish between commands where idempotency is naturally guaranteed (e.g., information queries, state settings) and non-idempotent physical control commands where side effects accumulate and re-execution may lead to critical safety incidents (e.g., pesticide spraying, irrigation, valve actuation). +- **Duplicate Dispatch Suppression Mechanism**: To prevent the same control intent from being dispatched under a new job ID during asynchronous retry loops, a deduplication schema based on a Control Intent Key must be embedded in the interface layer. Upon retry, the system must reuse the idempotency key bound to the original control intent rather than issuing a new job ID, thereby systemically controlling the possibility of duplicate execution of the same control. +- **Pre-flight Verification at the Interface Layer**: Before a duplicate control reaches the physical device, the interface module must perform a pre-flight check that pre-blocks re-execution requests for already-executed control intents, or approves them only after cross-checking against device-side state, providing a safety net against physical double actuation. \ No newline at end of file diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index ee93964..f4fb789 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -19,7 +19,7 @@ WORKSPACE_ROOT="$(cd "$SKILL_DIR/../.." && pwd)" AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}" # Workspace-relative defaults with environment overrides (Phase Z) -HOME_DIR="${HOME_DIR:-$WORKSPACE_ROOT}" +HOME_DIR="${HOME_DIR:-$HOME}" CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$HOME/.claude/projects}" LOCAL_BIN="${LOCAL_BIN:-$HOME/.local/bin}" @@ -305,6 +305,8 @@ def _validate(d): raise SystemExit(f"VALIDATE: tmux_sessions[{i}] not a mapping") if not s.get('name') or not s.get('status'): raise SystemExit(f"VALIDATE: tmux_sessions[{i}] missing name/status") + if s.get('role') is not None and (not isinstance(s['role'], str) or not s['role'].strip()): + raise SystemExit(f"VALIDATE: tmux_sessions[{i}] {s.get('name')!r} role must be a non-empty string") if s['status'] not in valid: raise SystemExit(f"VALIDATE: tmux_sessions[{i}] {s.get('name')!r} bad status {s['status']!r}") if not isinstance(s.get('pane'), dict): @@ -366,10 +368,17 @@ try: d['tmux_sessions'] = [] old_terminals = get_terminal_set(d) + old_roles = {s.get('name'): s.get('role') for s in db_sessions if s.get('role')} # --- caller mutation (module scope: sees d, yaml, os, glob, subprocess) --- exec(compile(os.environ['AGENT_SESSIONS_MUTATION'], '', 'exec'), globals()) + # Role immutability check + for s in d.get('tmux_sessions', []): + name = s.get('name') + if name in old_roles and s.get('role') != old_roles[name]: + raise SystemExit(f"VALIDATE: role of session {name!r} cannot be modified from {old_roles[name]!r} to {s.get('role')!r}") + _validate(d) # Separate globals and sessions for normalization @@ -475,7 +484,7 @@ def db_exists(uuid): def hermes_exists(uuid): - hdb = f"{home}/.mam/state.db" + hdb = f"{home}/.hermes/state.db" if not os.path.exists(hdb): return False try: @@ -487,6 +496,10 @@ def hermes_exists(uuid): return False +def cline_exists(uuid): + return os.path.exists(f"{home}/.cline/data/sessions/{uuid}/{uuid}.json") + + def emit(u): print(u) raise SystemExit(0) @@ -536,6 +549,10 @@ for s in sessions: cand = s.get('hermes_conversation_id_own') if cand and hermes_exists(cand): emit(cand) + if agent == 'cline' and name.endswith('-creator-cline'): + cand = s.get('cline_conversation_id_own') + if cand and cline_exists(cand): + emit(cand) # 2) disk scan scoped to THIS workspace if agent == 'claude': @@ -565,7 +582,7 @@ elif agent == 'agy': if cand and db_exists(cand): emit(cand) elif agent == 'hermes': - hdb = f"{home}/.mam/state.db" + hdb = f"{home}/.hermes/state.db" if os.path.exists(hdb): cand = None try: @@ -578,6 +595,27 @@ elif agent == 'hermes': cand = None if cand: emit(cand) +elif agent == 'cline': + sessions_dir = f"{home}/.cline/data/sessions" + if os.path.isdir(sessions_dir): + candidates = [] + for session_folder in glob.glob(f"{sessions_dir}/*"): + if os.path.isdir(session_folder): + folder_name = os.path.basename(session_folder) + json_file = f"{session_folder}/{folder_name}.json" + if os.path.exists(json_file): + candidates.append(json_file) + candidates.sort(key=os.path.getmtime, reverse=True) + for j in candidates: + try: + with open(j) as f: + sdata = json.load(f) + if sdata.get('cwd') == ws or sdata.get('workspace_root') == ws: + sid = sdata.get('session_id') + if sid: + emit(sid) + except Exception: + pass # 3) agent_identities cache, ONLY when its project_cwd == this workspace ai = {} @@ -609,6 +647,10 @@ if ai_agent.get('project_cwd') == ws: cand = ai_agent.get('session_id') or ai.get('conversation_id') if cand and hermes_exists(cand): emit(cand) + elif agent == 'cline': + cand = ai_agent.get('session_id') or ai.get('conversation_id') + if cand and cline_exists(cand): + emit(cand) print('') PYEOF diff --git a/.agents/skills/multi-agent-mux-create/SKILL.md b/.agents/skills/multi-agent-mux-create/SKILL.md index 70983e9..76cacfb 100644 --- a/.agents/skills/multi-agent-mux-create/SKILL.md +++ b/.agents/skills/multi-agent-mux-create/SKILL.md @@ -74,12 +74,12 @@ To prevent this, you can run this skill inside an **isolated tmux server** using ``` 2. **Via Option Flag**: ```bash - bash scripts/create_session.sh --workspace /path/to/project --agent claude --tmux-server multi-agent-canary + bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --tmux-server multi-agent-canary ``` 3. **Submit Job Integration**: You can automatically register a delegated job with a prompt when creating a session: ```bash - bash scripts/create_session.sh --workspace /path/to/project --agent claude --submit-job "Task prompt here" + bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --submit-job "Task prompt here" ``` ### Recommended Alias @@ -173,7 +173,7 @@ Use the `agent-sessions-yaml-edit` script in `scripts/` to safely append (preser ```bash bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \ - --workspace "$WORKSPACE" --agent "$AGENT" --session "$SESSION_NAME" + --workspace "$WORKSPACE" --agent "$AGENT" --role "$ROLE" --session "$SESSION_NAME" ``` The script handles the YAML append, pane capture, and the `last_visible_status` placeholder. diff --git a/.agents/skills/multi-agent-mux-create/scripts/create_session.sh b/.agents/skills/multi-agent-mux-create/scripts/create_session.sh index 72b5e11..f8a2261 100755 --- a/.agents/skills/multi-agent-mux-create/scripts/create_session.sh +++ b/.agents/skills/multi-agent-mux-create/scripts/create_session.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # create_session.sh — multi-agent-mux-create 의 부속 스크립트 # Usage: -# bash create_session.sh --workspace --agent [--session ] [--wrapper] +# bash create_session.sh --workspace --agent --role [--session ] [--wrapper] # # 동작: # 1) preflight: tmux/claude/agy 가용성, workspace 존재 @@ -23,11 +23,12 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)/lib.sh" usage() { cat < --agent [options] +Usage: $0 --workspace --agent --role [options] Options: --workspace PATH project directory (required) - --agent AGENT claude | agy | hermes (required) + --agent AGENT claude | agy | hermes | cline (required) + --role ROLE assigned role (required) --session NAME tmux session name (default: derived from workspace) --wrapper force use of ~/.local/bin/ wrapper even if not present --dry-run print commands without executing @@ -39,6 +40,7 @@ EOF WORKSPACE="" AGENT="" +ROLE="" SESSION_NAME="" USE_WRAPPER=0 DRY_RUN=0 @@ -49,6 +51,7 @@ while [ $# -gt 0 ]; do case "$1" in --workspace) WORKSPACE="$2"; shift 2 ;; --agent) AGENT="$2"; shift 2 ;; + --role) ROLE="$2"; shift 2 ;; --session) SESSION_NAME="$2"; shift 2 ;; --wrapper) USE_WRAPPER=1; shift ;; --dry-run) DRY_RUN=1; shift ;; @@ -66,6 +69,7 @@ fi # Preflight [ -n "$WORKSPACE" ] || { echo "ERROR: --workspace required" >&2; usage; exit 2; } [ -n "$AGENT" ] || { echo "ERROR: --agent required" >&2; usage; exit 2; } +[ -n "$ROLE" ] || { echo "ERROR: --role required" >&2; usage; exit 2; } [ -d "$WORKSPACE" ] || { echo "ERROR: workspace $WORKSPACE not a directory" >&2; exit 1; } command -v tmux >/dev/null || { echo "ERROR: tmux not installed" >&2; exit 1; } command -v "$AGENT" >/dev/null || { echo "ERROR: $AGENT CLI not in PATH" >&2; exit 1; } @@ -86,6 +90,11 @@ elif [ "$AGENT" = "hermes" ]; then echo "ERROR: hermes is not functional. Run 'hermes setup' first." >&2 exit 1 fi +elif [ "$AGENT" = "cline" ]; then + if ! cline history --json >/dev/null 2>&1; then + echo "ERROR: cline is not functional or configured." >&2 + exit 1 + fi fi # 세션 이름 — lib.sh::derive_session_name 이 단일 소스 (P0-A) @@ -119,7 +128,10 @@ spawn() { hermes) _tmux new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "hermes" ;; - *) echo "ERROR: --agent must be claude, agy or hermes, got: $AGENT" >&2; exit 2 ;; + cline) + _tmux new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "cline -i" + ;; + *) echo "ERROR: --agent must be claude, agy, hermes or cline, got: $AGENT" >&2; exit 2 ;; esac } @@ -145,6 +157,7 @@ case "$AGENT" in claude) CMD_FULL='claude --dangerously-skip-permissions' ;; agy) CMD_FULL='agy --dangerously-skip-permissions' ;; hermes) CMD_FULL='hermes' ;; + cline) CMD_FULL='cline -i' ;; esac # 시작 명령 @@ -161,7 +174,7 @@ case "$AGENT" in START_CMD="$local_tmux new-session -d -s \"$SESSION_NAME\" -x 140 -y 40 -c \"$WORKSPACE\" \"claude --dangerously-skip-permissions\"" fi ;; - agy|hermes) + agy|hermes|cline) START_CMD="$local_tmux new-session -d -s \"$SESSION_NAME\" -x 140 -y 40 -c \"$WORKSPACE\" \"$CMD_FULL\"" ;; esac @@ -174,6 +187,8 @@ if [ -n "$SUBMIT_JOB_PROMPT" ]; then delegate_agent="claude-code" elif [ "$AGENT" = "hermes" ]; then delegate_agent="hermes-agent" + elif [ "$AGENT" = "cline" ]; then + delegate_agent="cline-agent" else delegate_agent="antigravity-cli" fi @@ -191,7 +206,7 @@ fi # 모든 값은 환경변수로 전달 — heredoc interpolation 없음 (P1-B). # 자식 pid 는 bash 에서 pgrep 으로 미리 구함 (P2: 도구명 필터). CHILD_PID=0 -if { [ "$AGENT" = "agy" ] || [ "$AGENT" = "hermes" ]; } && [ -n "$PANE_PID" ]; then +if { [ "$AGENT" = "agy" ] || [ "$AGENT" = "hermes" ] || [ "$AGENT" = "cline" ]; } && [ -n "$PANE_PID" ]; then CHILD_PID=$(pgrep -P "$PANE_PID" -x "$AGENT" 2>/dev/null | head -1 || true) CHILD_PID="${CHILD_PID:-0}" fi @@ -201,9 +216,10 @@ atomic_dump_yaml "$AGENT_SESSIONS_YAML" \ TMUX_EPOCH="$TMUX_EPOCH" PANE_PID="$PANE_PID" PANE_CWD="$PANE_CWD" \ CMD_FULL="$CMD_FULL" START_CMD="$START_CMD" CHILD_PID="$CHILD_PID" \ TMUX_SERVER_NAME="${TMUX_SERVER_NAME:-default}" \ - DELEGATE_JOB_ID="$DELEGATE_JOB_ID" <<'PYEOF' + DELEGATE_JOB_ID="$DELEGATE_JOB_ID" ROLE="$ROLE" <<'PYEOF' name = os.environ['SESSION_NAME'] agent = os.environ['AGENT'] +role = os.environ['ROLE'] pid = os.environ.get('PANE_PID', '') epoch = os.environ.get('TMUX_EPOCH', '') server_name = os.environ.get('TMUX_SERVER_NAME', 'default') @@ -222,6 +238,7 @@ sessions[:] = [s for s in sessions if s.get('name') != name] entry = { 'name': name, 'status': 'running', + 'role': role, 'tmux_session_created_at': os.environ['NOW_ISO'], 'tmux_session_epoch': int(epoch) if epoch.isdigit() else 0, 'tmux_server': server_name, @@ -265,6 +282,11 @@ elif agent == 'hermes': entry['child_pid'] = int(cp) if cp.isdigit() else 0 entry['hermes_conversation_id_own'] = None entry['last_visible_status'] = "TUI started; awaiting first user message" +elif agent == 'cline': + cp = os.environ.get('CHILD_PID', '0') + entry['child_pid'] = int(cp) if cp.isdigit() else 0 + entry['cline_conversation_id_own'] = None + entry['last_visible_status'] = "TUI started; awaiting first user message" sessions.append(entry) diff --git a/.agents/skills/multi-agent-mux-delegate-job/README.md b/.agents/skills/multi-agent-mux-delegate-job/README.md index 7fb2bd8..5c709a1 100644 --- a/.agents/skills/multi-agent-mux-delegate-job/README.md +++ b/.agents/skills/multi-agent-mux-delegate-job/README.md @@ -1,7 +1,7 @@ # multi-agent-mux-delegate-job 스킬 -작업(Job)을 자율 에이전트(claude-code/codex/opencode/human)에게 위임하고 MQTT -이벤트 채널로 비동기 관찰하는 Hermes 스킬. **시작점은 [`SKILL.md`](./SKILL.md).** +작업(Job)을 자율 에이전트(claude-code/hermes/agy/cline/codex/opencode/human)에게 위임하고 MQTT +이벤트 채널로 비동기 관찰하는 범용 에이전트 협업 스킬. **시작점은 [`SKILL.md`](./SKILL.md).** - 프로토콜/스키마: [`job-protocol.md`](./job-protocol.md) - 브로커 PoC→운영 전환: [`mqtt-broker-setup.md`](./mqtt-broker-setup.md) diff --git a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md index 4c3d324..7ff015f 100644 --- a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md +++ b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md @@ -1,385 +1,94 @@ --- name: multi-agent-mux-delegate-job -description: "Delegate a unit of work to any autonomous agent (claude-code, codex, opencode, or a human) and observe it asynchronously over an MQTT event channel. Each job gets a unique id, a registry record (prompt, broker, status, timeouts), and a single per-job topic that carries started/permission_required/progress/completed/error events as schema-versioned JSON. The delegator starts a subscriber first, runs the agent, and treats a completed/error event or a timeout as the job's terminal state. Ships a working reference implementation (publish_event.py, job_subscriber.py, registry.py, mqtt_common.py, multi-agent-mux-delegate-job wrapper) plus a PoC-to-production path: validate on a public broker, then move to an authenticated TLS broker by changing config only — no code change. Use when you need fire-and-observe delegation, multi-job fan-out across tmux sessions, or a uniform completion-signal protocol shared by several agent types." -version: 1.0.0 -author: Hermes Agent +description: "Delegate a unit of work to any autonomous agent (claude-code, hermes, agy, cline, codex, or a human) and observe it asynchronously over an MQTT event channel. Supported roles include orchestrator, worker, and reviewer." +version: 1.1.0 +author: Multi-Agent System license: MIT platforms: [linux, macos, windows] -metadata: - hermes: - tags: [agent-delegation, mqtt, jobs, orchestration, async-completion] - related_skills: [claude-code, codex, opencode, hermes-agent-skill-authoring] --- # multi-agent-mux-delegate-job — Async Job Delegation over MQTT -Delegate a unit of work to an autonomous agent, then **observe** it instead of -blocking on it. Every job gets a unique id and a registry record; the agent -publishes lifecycle events (`started`, `permission_required`, `progress`, -`completed`, `error`) to a per-job MQTT topic; the delegator subscribes and -treats `completed`/`error` — or a timeout — as the terminal state. +Delegate a unit of work to any autonomous agent, then **observe** it asynchronously instead of blocking. Every job gets a unique ID and a registry record. The worker agent publishes lifecycle events (`started`, `permission_required`, `progress`, `completed`, `error`) to a per-job MQTT topic, and the delegator/orchestrator subscribes to verify the final state. -This skill is a **reference implementation**: copy the files in this directory -into your project and customise. The `communication_over_mqtt` project is the -canonical concrete instance. +This skill allows any agent (`claude-code`, `hermes`, `agy`, `cline`, etc.) to play any role: **Orchestrator/Delegator**, **Worker/Implementer**, or **Reviewer**. -## Overview +--- -The model is deliberately small. A **job** is one delegated task. An **agent** -is a worker (a claude-code tmux session, a codex run, a human). The **registry** -(`.mam/jobs/.json`) holds everything about a job so nothing important -lives in environment variables — which means one tmux session can process many -jobs sequentially, and many sessions can fan out in parallel, with no env -collisions. The **event channel** is one MQTT topic per job carrying JSON -payloads; `event` discriminates the type. +## Roles in Multi-Agent Mux -Responsibility is split into exactly one entry point each: -[`publish_event.py`](./scripts/publish_event.py) emits events (registry lookup, -monotonic `seq`, retry+backoff) and [`job_subscriber.py`](./scripts/job_subscriber.py) -observes them (timeouts, terminal state machine, defensive parsing). Shared -logic lives in [`mqtt_common.py`](./scripts/mqtt_common.py); registry I/O in -[`registry.py`](./scripts/registry.py). The demo `publisher.py`/`subscriber.py` -in the host project stay frozen. +- **Orchestrator (Delegator)**: Initiates the job, coordinates other agents, handles loops and reviews, and commits final changes. +- **Worker (Implementer)**: Receives the brief file or task prompt, performs the implementation, and emits started/completed/error events. +- **Reviewer**: Evaluates git diffs or artifacts produced by the worker, and responds with a `completed` event containing `"PASS"` or feedback. -Two stages, same code. **PoC** runs on the public `broker.hivemq.com` to wire up -the protocol. **Production** moves to your own authenticated TLS broker — the -switch is **config only** (env vars + the registry `broker.*` block), never a -code change. See [`mqtt-broker-setup.md`](./mqtt-broker-setup.md). +--- -## When to Use / When NOT to Use +## Core Commands (CLI) -**Use when:** -- you want **fire-and-observe** delegation — kick off work and get a completion - signal rather than blocking a terminal; -- several agent types (claude-code, codex, opencode, human) must follow **one** - completion protocol; -- you need **multi-job fan-out** across tmux sessions with safe job claiming; -- you want a clean PoC → authenticated-broker upgrade path. - -**Do NOT use when:** -- a one-shot `claude -p '…'` that returns inline is enough (no async signal - needed) — just use the [claude-code](../claude-code/SKILL.md) skill directly; -- you need request/response RPC or large artifact transfer (this is a - one-direction event stream, not a data bus); -- the payload would carry secrets and you're still on the public broker — move - to the own-broker stage first. - -## Quick Start - -The one-line wrapper handles register + subscriber-first + agent launch. If -you're new, **start here** and only fall back to the manual 5-step flow when -you need finer control. +The `multi-agent-mux-delegate-job` bash wrapper handles job registration, subscriber management, agent session targeting, and validation hooks: ```bash -# 1) one line: register → start subscriber → launch agent in tmux -# (uses public broker by default; last stdout line is the audit-log dir) +# 1) Submit a new job to a targeted agent session (e.g. tmux session name 'demo') multi-agent-mux-delegate-job submit \ - --agent claude-code \ - --prompt "정렬 문제 10개를 만들어 sort_problems.md로 저장" \ - --workdir /path/to/project \ - --agent-session tmux:demo \ + --agent \ + --agent-session tmux: \ + --prompt "Task description or instructions here" \ --timeout 3600 --idle-timeout 120 -# → stdout: registered job: -# subscriber pid: … -# agent launched in tmux session: demo -# subscriber output: -# /path/to/project/.mam/delegate_job_logs/ ← audit log dir -# 2) at any time, query the job or its audit log -multi-agent-mux-delegate-job status --job -multi-agent-mux-delegate-job logs # pretty timeline -multi-agent-mux-delegate-job logs --list # every job, live status +# 2) Submit a job with a feedback loop (Worker-Reviewer Loop) +multi-agent-mux-delegate-job submit \ + --agent --agent-session tmux: \ + --type loop --reviewer --reviewer-session tmux: \ + --prompt "Task description" -# 3) run a user-supplied validator against the job's artifacts -multi-agent-mux-delegate-job verify --job --validate ./validate.sh +# 3) Check job status and audit logs +multi-agent-mux-delegate-job status --job +multi-agent-mux-delegate-job logs # Chronological log of events +multi-agent-mux-delegate-job list # Summary of all registered jobs + +# 4) Verify job artifacts with a validation script +multi-agent-mux-delegate-job verify --job --validate ./validate.sh ``` -The wrapper enforces the **subscribe-before-publish** ordering and **forwards -the freshly-minted `JOB_ID` into the agent's prompt** (so the agent calls -`publish_event.py --job ` with the right id — see Pitfall §"Wrong job_id -propagated to the agent"). When you need finer control, the manual flow is: +--- -```bash -# Manual 5-step (same outcome, more knobs) -PY=.venv/bin/python -SKILL=./.agents/skills/multi-agent-mux-delegate-job/scripts +## Task Delegation Types -# 1) register -JID=$($PY "$SKILL/registry.py" register \ - --prompt "…" --agent claude-code --agent-session tmux:demo \ - --timeout 3600 --idle-timeout 120) +Supported job types include: +- `direct` (default): Single agent execution (direct tasking). +- `loop` (Worker-Reviewer Loop): Alternates worker execution and reviewer evaluation until reviewer approves (`PASS`) or iterations run out. +- `discuss` (Research & Discussion): Collaboration between two agents to reach a consensus (e.g., agreeing on a design or plan). -# 2) START THE SUBSCRIBER FIRST (MQTT does not queue non-retained msgs) -$PY "$SKILL/job_subscriber.py" --job "$JID" --timeout 3600 --idle-timeout 120 & +For detailed state machine diagrams and configurations, see [DELEGATION_TYPES.md](./DELEGATION_TYPES.md). -# 3) pass JID to the agent and instruct it to publish events with --job "$JID" -# (don't hard-code a job id you saw earlier — see Pitfall §"Wrong job_id") +--- -# 4) on completion the subscriber prints events and exits 0/1/2 +## The Event Protocol Contract -# 5) inspect any time -$PY "$SKILL/registry.py" get --job "$JID" -$PY "$SKILL/registry.py" logs "$JID" # positional job id -$PY "$SKILL/registry.py" logs --list -``` +Every agent participating in the delegation contract must follow the same lifecycle publishing protocol using `publish_event.py`: -## Job Protocol +1. **On Start**: Publish `started` event. + `python3 .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py --job "$JOB_ID" --event started` +2. **On Tool/Permission Prompt**: Publish `permission_required` event. + `python3 ... --job "$JOB_ID" --event permission_required --detail ":"` +3. **On Progress Update (Optional)**: Publish `progress` event. + `python3 ... --job "$JOB_ID" --event progress --detail ""` +4. **On Success**: Publish `completed` event. + `python3 ... --job "$JOB_ID" --event completed --detail ""` (Reviewer should include `"PASS"` in the detail to approve). +5. **On Failure/Feedback**: Publish `error` event. + `python3 ... --job "$JOB_ID" --event error --detail ""` -One topic per job: `python/mqtt/jobs//events`. Payload (JSON, UTF-8, -`schema_version=1`): - -```json -{ "schema_version": 1, "seq": 7, "job_id": "abc12345", - "event": "started|permission_required|progress|completed|error", - "timestamp": "2026-06-19T09:32:00Z", "detail": "generalised text", - "data": { "optional": "metadata" } } -``` - -- `seq` is monotonic per job (first = 1); the subscriber uses it to spot - reorder/duplication. -- `timestamp` is advisory — timeouts are measured from **receive** time. -- `detail`/`data` carry **no** secrets or absolute paths. -- A `schema_version` or `job_id` mismatch is **dropped** (defensive parsing). - -`started` and `completed`/`error` are the mandatory bookends; `completed`→exit 0, -`error`→exit 1. Full catalogue + production `auth_token` handling: -[`job-protocol.md`](./job-protocol.md). - -## Registry Format - -``` -.mam/jobs/.json # metadata record (single source of truth) -.mam/jobs/.events.log # append-only JSON-lines log (debug, optional) -.mam/jobs/.lock # fcntl advisory lock for the registry -``` - -The record holds `status`, `prompt`, `agent`, `agent_session`, a `broker` block, -`topic_prefix`, `timeout_sec`/`idle_timeout_sec`, `expected_artifacts`, -`last_seq`, and (production) `auth_token`. Because the `broker` block lives in -the record, `publish_event.py` connects from the registry alone. Concurrency, -the atomic rename trick, and multi-session job claiming are in -[`registry.md`](./registry.md). +--- ## Audit Logs -Every job's lifecycle is mirrored to a **persistent, append-only audit log** -under `.mam/delegate_job_logs/` (override with `DELEGATE_JOB_LOGS_DIR`; -default `/.mam/delegate_job_logs`). Unlike the registry — live state -mutated in place and liable to be cleaned up — the audit log is durable -history you can replay after the fact. It is git-ignored. +Job lifecycle execution events are persistently mirrored to an append-only log under `.mam/delegate_job_logs//` (containing `meta.json`, `events.ndjson`, and `status.json`). Use `multi-agent-mux-delegate-job logs ` to view the timeline. -``` -.mam/delegate_job_logs// - meta.json # registration snapshot: prompt, agent, broker, timeouts, … - events.ndjson # append-only, one JSON event per line, in time order - status.json # current status only (fast point-query) -``` +--- -**What is logged, automatically:** +## Best Practices and Pitfalls -| When | `events.ndjson` line | Written by | -|------|----------------------|------------| -| job registered | `registered` (also seeds meta.json + status.json) | `registry.register_job` | -| any status change | `status_changed` (`from`/`to`; also rewrites status.json) | `update_job_status`, `pick_pending` | -| event published | `published` (carries the exact payload — reproducible) | `publish_event.py` | -| event received | `received` (subscriber's external view) | `job_subscriber.py` | - -Both the emitter side (`published`) and the observer side (`received`) are -recorded, so a dropped publish or a missed receive is still visible from the -other. Every write is **best-effort and isolated** — an fcntl-locked append -guarded by `try/except` that only ever emits a `logger.warning`, so a logging -failure can never break a publish, a subscribe, or a registry write. stdout is -never touched. - -**Reading them:** - -```bash -multi-agent-mux-delegate-job logs # pretty-print one job's timeline -multi-agent-mux-delegate-job logs --list # summarise every logged job (with live status) -# or directly via the registry CLI: -$PY scripts/registry.py logs [--tail N] [--json] -$PY scripts/registry.py logs --list [--json] -``` - -`submit` prints the job's audit-log directory as its last stdout line, so a -caller can `tail -n1` to locate it. - -## Broker Setup - -| Stage | Broker | Auth | Transport | -|-------|--------|------|-----------| -| PoC | `broker.hivemq.com` | none | 1883 plaintext | -| Production | self-hosted Mosquitto/EMQX | user/pass + ACL | 8883 TLS | - -All connection settings come from env (`MQTT_BROKER`, `MQTT_PORT`, `MQTT_TLS`, -`MQTT_USERNAME`/`MQTT_PASSWORD`, `MQTT_CA_CERTS`, …) resolved by -`broker_config_from_env()`, with the registry `broker.*` block overriding per -job. Moving to your own broker is **config only**: install Mosquitto, set -`persistence true` + `acl_file` + `password_file` + a TLS `listener 8883`, grant -the worker `write python/mqtt/jobs/+/events` and Hermes `read`, then flip -`MQTT_TLS=1` and fill the registry `broker.*`. Step-by-step (conf, ACL, -`mosquitto_passwd`, self-signed/private-CA certs, cut-over verification): -[`mqtt-broker-setup.md`](./mqtt-broker-setup.md). - -## Agent Adapters - -Each agent voluntarily follows the contract: receive a `JOB_ID` (or registry -path), call `publish_event.py` at lifecycle points, exit 0/1/2. **The contract -in one line**: every event call uses `--job "$JOB_ID"` where `$JOB_ID` is the -**freshly-issued id from the registry record for *this* delegation** — never a -job_id you saw in an earlier session (Pitfall §"Wrong job_id propagated to the -agent"). - -- **claude-code** — Claude Code calls `publish_event.py` via its Bash tool at - lifecycle points. `submit --mode tmux` injects a prompt that already names - `$JOB_ID`; if you drive claude manually, hand it the id explicitly. Reference - instruction block (the wrapper injects something equivalent): - - ```text - Your job_id is "$JOB_ID" (read it from the registry record for this delegation — - do not reuse any job_id you saw before). - - On start: $PY multi-agent-mux-delegate-job/scripts/publish_event.py --job "$JOB_ID" --event started - On permission: $PY … --job "$JOB_ID" --event permission_required --detail ":" - On progress: $PY … --job "$JOB_ID" --event progress --detail "" - On success: $PY … --job "$JOB_ID" --event completed --detail "" - On failure: $PY … --job "$JOB_ID" --event error --detail "" - - Task: - - The subscriber for "$JOB_ID" is already running; your completed/error event - ends the job. Exit codes: 0 completed, 1 error, 2 publish failure. - ``` - - See [claude-code](../claude-code/SKILL.md) for tmux orchestration patterns. -- **codex** — same contract. Invoke `codex exec ""` or - wire `publish_event.py` as an MCP tool so the agent can call it directly. -- **opencode** — wire `publish_event.py` as a tool/command the agent can call; - identical event points. -- **human** — a person does the work, reads the registry record, then runs - `publish_event.py --job --event completed` (or `error`) by hand. - -## User Interface - -The [`multi-agent-mux-delegate-job`](./multi-agent-mux-delegate-job) bash wrapper bundles register + -subscribe-first + run-agent + validate: - -```bash -multi-agent-mux-delegate-job submit --agent claude-code \ - --prompt "정렬 문제 10개를 만들어 sort_problems.md로 저장" \ - --workdir /path/to/project --timeout 3600 [--validate ./validate.sh] -multi-agent-mux-delegate-job status --job # one record, pretty-printed -multi-agent-mux-delegate-job list # all jobs, one line each -multi-agent-mux-delegate-job verify --job --validate ./validate.sh # runs it, reports exit code -multi-agent-mux-delegate-job wait [--job ] # block until terminal (else --wait-any) -``` - -`submit` **always starts the subscriber before the agent** (the ordering -dependency), runs the agent in `--mode print` (one-shot) or `--mode tmux`, and -calls `--validate` afterward if given. The skill automates job-id generation, -registry creation, broker resolution, subscriber-first ordering, agent launch, -and completion detection; it does **not** automate the agent's internals or your -business-logic validation — those are hooks you fill (`validate.sh` reads -`$JOB_ID`/`$REGISTRY_DIR`). - -## Common Pitfalls - -- **Publishing before subscribing** — MQTT does not queue non-retained messages - for absent subscribers. Start `job_subscriber.py` *before* the agent, or rely - on retained terminal events (production). `submit` enforces this. -- **Wrong job_id propagated to the agent** — the wrapper prints a fresh `JOB_ID` - on every `submit`. If your agent instruction (or the wrapper's prompt template) - hard-codes an old job_id, the agent calls `publish_event.py --job `, - the subscriber's defensive parser drops it as a `job_id` mismatch, and the - delegator waits until idle timeout (exit 2). Fix: instruct the agent to - **read the job_id from the registry record for *this* delegation** (or pass it - in via env / `--prompt` interpolation), never from prior runs. `submit`'s - default prompt template interpolates `$JOB_ID` for you — if you build a custom - prompt, do the same. -- **tmux session name collision** — `submit --mode tmux` derives the session - name from `--agent-session tmux:` (default `tmux:claude`). If a session - with that name is already attached (e.g. you ran the demo and the previous - session is still open), `tmux new-session -d -s ` fails and the agent - never launches. Pick a unique `--agent-session` per concurrent delegation - (e.g. `tmux:demo`, `tmux:claude-a`, `tmux:claude-b`) or kill the stale one - (`tmux kill-session -t claude`) before re-running. -- **Timeout before `started`** — a cold-starting agent may not emit `started` - for a while; the wall-clock timeout starts at subscribe time so a stuck agent - still terminates. Don't set `--timeout` so low you false-positive a slow start. -- **No retry on publish** — a dropped `completed` would hang the delegator - forever; `publish_event.py` retries with exponential backoff and exits 2 if it - still fails, so the delegator is never left waiting silently. -- **QoS-1 duplicates / reorders** — a terminal event can arrive twice, or - `error` can trail `completed`; the subscriber's terminal state machine - finalises each job once and ignores the rest. -- **Trusting the public broker** — anyone can publish there; never make a real - decision on a PoC signal. Add `auth_token` + an authenticated broker first. -- **Secrets in `detail`/`data`** — keep payloads generalised; no paths, keys, or - tokens (except the production `auth_token` in `data`). - -## Subagent Orchestration Pattern - -When using this skill from a Hermes `delegate_task` subagent to dispatch work to -a coding-agent CLI (agy/claude) running in a tmux session, the following pattern -has been verified (2026-06-21, 6-batch refactoring sprint): - -### Roles -- **Main worker** (implementation): one agent session (e.g. `agy-new`) receives - brief files and executes code changes. -- **Reviewers** (spec compliance + code quality): two other agent sessions - (e.g. `agy-existing`, `claude-existing`) review the diff in parallel. -- **Hermes** (orchestrator): dispatches subagents, verifies diffs, commits, - and falls back to direct fixes when reviewers find issues. - -### Key lessons learned -1. **Brief delivery via file path** — don't paste long briefs inline via - `tmux send-keys`; the TUI may swallow them. Instead, send a short instruction - like "follow /tmp/batch1-brief.md" and let the agent read the file. -2. **Polling vs MQTT subscriber** — for short tasks (<5min), pane polling - (`capture-pane` + grep for completion markers) is simpler and more reliable - than registering a job via `registry.py` + `job_subscriber.py`. Use MQTT - subscriber only for long-running jobs (>5min) where push notification matters. -3. **Reviewers catch different bugs** — in practice, agy (Flash) caught - semantic issues (slash matching, export scope), while claude (Opus) caught - API signature mismatches (paho v2 5-arg vs 4-arg `on_disconnect`). Two - reviewers with different models provide complementary coverage. -4. **Hermes fallback fix** — when reviewers find a small, well-defined issue - (wrong argument count, missing slash), Hermes should fix it directly rather - than re-dispatching the implementer. This saves a full round-trip. -5. **Batch grouping** — group 2-3 FW items per batch when they touch different - files (no file overlap). This amortises the dispatch overhead. Items touching - the same file must be in separate batches to avoid conflicts. -6. **Pane Snapshots & Truncation Prevention** — to prevent long agent responses from being scrolled out and truncated due to TUI viewport limitations, enforce the following snapshotting pattern: - - Immediately after dispatching a brief, capture the pre-brief pane buffer via `capture-pane -S -200`. - - During long execution, run a background loop taking incremental snapshots (e.g. every 30 seconds `>> /tmp/pane-snap.txt`). - - Immediately after job termination, capture the entire final pane state to ensure no terminal logs are lost. - -## Verification Checklist - -- [ ] `started` → `completed` over the public broker: subscriber prints the - lines and exits **0**. -- [ ] `error` path: subscriber exits **1**. -- [ ] timeout path: no terminal event within `--timeout`/`--idle-timeout` → - exit **2**. -- [ ] polluted payload (bad JSON, wrong `schema_version`, wrong `job_id`) is - dropped with a warning, not crashed on. -- [ ] one tmux session processes two registry jobs in sequence; a second - session with a different `agent_session` claims only its own. -- [ ] broker cut-over: same scripts reach an authenticated TLS broker with env - changes only; a credential without write ACL is rejected; a late - subscriber still receives the retained terminal event. -- [ ] `publisher.py`/`subscriber.py`/`README.md` demo on `python/mqtt/sample` - still works unchanged (regression). -- [ ] **audit log integrity** — for a completed job, - `.mam/delegate_job_logs//events.ndjson` contains `registered` → - `received started` → `published completed` (in that order), and - `status.json.status == "completed"` matches the registry record. A - logging failure (e.g. read-only log dir) does not break the publish or - subscribe path — only a `logger.warning` is emitted. -- [ ] **end-to-end demo smoke** — run - `multi-agent-mux-delegate-job submit --agent claude-code --agent-session tmux:demo-smoke - --prompt "echo hello and call publish_event.py --job - --event completed" --timeout 120` and confirm - (a) registered job id echoed, (b) subscriber pid echoed, (c) tmux session - name printed, (d) `events.ndjson` grows as the agent runs, (e) final - stdout line is the audit-log dir. +- **Subscribe-Before-Publish**: The subscriber must be running before the agent starts publishing. The `submit` command handles this automatically by launching the subscriber in the background first. +- **Fresh job_id Propagation**: Make sure the worker agent receives the correct `JOB_ID` generated for the current run, rather than reusing stale IDs from previous sessions. +- **Brief delivery via file path**: For long or complex prompts, write the instructions to a file (e.g. `/tmp/task-brief.md`) and pass a short prompt pointing to the file path to prevent terminal buffer overflows. +- **Batch Grouping**: Group non-overlapping tasks into batches to parallelize execution across multiple agent sessions, reducing overhead. diff --git a/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job b/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job index 8b32861..79d6f9f 100755 --- a/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job +++ b/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job @@ -16,6 +16,13 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Load local .env if it exists in current dir or workspace root +if [[ -f .env ]]; then + set -a; source .env; set +a +elif [[ -f "$SCRIPT_DIR/../../.env" ]]; then + set -a; source "$SCRIPT_DIR/../../.env"; set +a +fi + # Pick an interpreter: prefer a project .venv, else python3. pick_python() { local py_bin @@ -46,6 +53,8 @@ multi-agent-mux-delegate-job [options] submit --agent --prompt [--workdir ] [--agent-session