Compare commits
3
Commits
230e262414
...
35fc44f269
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35fc44f269 | ||
|
|
7fde1d2b8a | ||
|
|
626f35adfc |
@@ -1,105 +0,0 @@
|
||||
# Multi-Agent Collaboration Workflow Reference Guide
|
||||
|
||||
본 문서는 대규모 프로젝트나 정밀한 설계·구현 요구사항을 처리하기 위해 **Planner, Developer, Reviewer** 에이전트 간의 역할 분담 및 피드백 루프를 운영하는 **다중 에이전트 협업 워크플로우(Multi-Agent Collaboration Workflow)**를 정의합니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 역할 정의 및 분담 (Roles & Responsibilities)
|
||||
|
||||
협업 시스템은 각 에이전트의 책임 영역을 명확히 격리하여 상호 교차 검증을 강제합니다.
|
||||
|
||||
```
|
||||
┌──────────────────────┐
|
||||
│ User Prompt │
|
||||
└──────────┬───────────┘
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ 1. Planner Agent │ ◄──────────────────┐
|
||||
│ - Plan & Checklist │ │
|
||||
└──────────┬───────────┘ │
|
||||
▼ │
|
||||
┌──────────────────────┐ │
|
||||
│ 2. Developer Agent │ │
|
||||
│ - Code & DoD Verify │ │
|
||||
└──────────┬───────────┘ │
|
||||
▼ │ (NOT PASS Feedback)
|
||||
┌──────────────────────┐ │
|
||||
│ 3. Reviewer Agents │ │
|
||||
│ - Dual Peer Review │ ───────────────────┘
|
||||
└──────────┬───────────┘
|
||||
▼ (PASS)
|
||||
┌──────────────────────┐
|
||||
│ 4. Done & Commit │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
### 1.1. Planner Agent (설계 및 통제)
|
||||
- **목적**: 요구사항을 명세화하고, 구현 단계의 설계 결함이나 모순(Contradiction)을 사전에 차단합니다.
|
||||
- **역할**:
|
||||
- 사용자 요구사항에 따른 구현 목표 및 범위 수립.
|
||||
- `implementation_plan.md` 및 `task.md` (체크리스트) 작성 및 버전 관리(Rev.1, Rev.2, ...).
|
||||
- 리뷰어 피드백 발생 시 설계 변경의 파급 범위를 계산하여 계획 갱신.
|
||||
- **핵심 원칙**: 직접 코드를 수정하지 않고 오직 설계와 체크리스트 자산만 관리합니다.
|
||||
|
||||
### 1.2. Developer Agent (구현 및 자가 검증)
|
||||
- **목적**: Planner가 제공한 체크리스트를 기반으로 실제 리포지토리 코드를 물리적으로 수정 및 구현합니다.
|
||||
- **역할**:
|
||||
- `task.md`를 순차적으로 완료 상태(`[x]`)로 업데이트하며 구현 수행.
|
||||
- 커밋 전 **Definition of Done (DoD)** 체크리스트를 자체 실행하여 금지된 코드 패턴, 메모리/구조적 사이드 이펙트 유무 자가 검토.
|
||||
- 수정 사항을 단일 원자적(Atomic) 커밋으로 마감하고 리뷰어에게 전달.
|
||||
|
||||
### 1.3. Reviewer Agents (교차 피드백 및 검증)
|
||||
- **목적**: 구현된 결과물이 최초 설계서 및 학술적 제약 요건에 일치하는지 제3자의 관점에서 엄격하게 검토합니다.
|
||||
- **역할**:
|
||||
- **Reviewer A (Claude)**: 학술 서사와 코드 설계 간의 상위 논리적 정합성 및 결함(예: HOLB 해소 주장과 단일 커넥션 다이얼러의 모순 등) 검증.
|
||||
- **Reviewer B (Cline)**: 전이 조건, 예외 처리, 타입 시그니처, 텔레메트리 매핑 등 하위 레벨 구현의 세부 사항 기계적 검증.
|
||||
- **판정 규칙**: 두 리뷰어 모두 **PASS** 판정을 내릴 때까지 개발자는 마감할 수 없으며, 반려 시 **1단계(Planner)**로 피드백이 환류됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 세부 운영 단계 (Step-by-Step Workflow)
|
||||
|
||||
### 1단계: 설계 수립 (Planning Phase)
|
||||
1. 사용자가 요구사항을 제시하면, **Planner 에이전트**가 프로젝트의 전반적인 구조를 파악합니다.
|
||||
2. `implementation_plan.md` 및 `task.md`를 작성하여 개발을 위한 로드맵을 제공합니다.
|
||||
3. 사용자가 해당 구현 계획을 승인하면 다음 단계로 이행합니다.
|
||||
|
||||
### 2단계: 코드 구현 및 자가 검증 (Development Phase)
|
||||
1. **Developer 에이전트**가 배정된 태스크의 코드를 수정합니다.
|
||||
2. 작업 진행 중 예상치 못한 설계 변경 필요성이 감지되면 작업을 멈추고 **Planner 에이전트**에게 계획 수정을 먼저 위임합니다.
|
||||
3. 구현 완료 후 아래의 **DoD 검증**을 수행합니다:
|
||||
- 핵심 기능의 타입 매핑 확인.
|
||||
- 공유 자원(`tls.Config` 등) 변경 시 사이드 이펙트 방지(복제 후 변이 적용 등).
|
||||
- 문서-코드 간 주장의 정합성 체크.
|
||||
4. 검증 완료 후 단일 커밋을 작성합니다.
|
||||
|
||||
### 3단계: 피드백 루프 및 통과 (Review Phase)
|
||||
1. Developer 에이전트가 리뷰어 세션에 작업 완료 사실과 변경 범위를 전달합니다.
|
||||
2. 리뷰어들은 `git diff`를 바탕으로 개별 검증을 수행하고 보고서 형태의 리뷰 피드백을 출력합니다.
|
||||
- **반려 (`NOT PASS`)** -> 피드백 요약본을 Planner 에이전트에게 전송하여 상위 레벨 계획(Rev.n) 개시.
|
||||
- **통과 (`PASS`)** -> 모든 검토 사항이 해결되었음을 명시.
|
||||
3. 모든 리뷰어가 PASS를 발행하면 작업을 완결하고, 다른 에이전트 세션들은 종료하지 않고 다음 태스크 지시가 있을 때까지 프롬프트 대기 상태(Standby)로 유지합니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Best Practices & 템플릿
|
||||
|
||||
### 3.1. 작업 시작 시 Planner 에이전트 프롬프트 템플릿
|
||||
```
|
||||
[역할 요구]
|
||||
프로젝트의 구조 및 내용을 파악하고, 현재 작업 요건에 맞춰 구현 계획서(implementation_plan.md) 및 태스크 체크리스트(task.md)를 작성해 주세요.
|
||||
|
||||
[검토 초점]
|
||||
- 설계 수준에서 논리적 모순이 발생할 여지가 없는지
|
||||
- naive 구현과 대비되는 핵심 차별점이 코드 명세에 정확히 명시되었는지
|
||||
```
|
||||
|
||||
### 3.2. 피드백 루프 환류 시 프롬프트 템플릿
|
||||
```
|
||||
Reviewer 검토 결과 구현 코드 차원에서 아래의 블로킹(NOT PASS) 피드백이 발생했습니다.
|
||||
위 피드백을 수용하여 설계 문서를 수정하는 계획을 수립하고, implementation_plan.md (Rev.[N]) 및 task.md 내용을 업데이트해 주세요.
|
||||
|
||||
[피드백 내용]
|
||||
- [블로킹 항목 1]
|
||||
- [블로킹 항목 2]
|
||||
```
|
||||
+12
-2
@@ -16,6 +16,9 @@
|
||||
|
||||
if [ -z "${BASH_VERSION:-}" ]; then
|
||||
echo "ERROR: lib.sh must be executed/sourced from bash (foreign shell detected)" >&2
|
||||
# Reachable when this file is executed directly rather than sourced;
|
||||
# `return` only fails (falling through to `exit`) in that path.
|
||||
# shellcheck disable=SC2317
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
@@ -1018,10 +1021,10 @@ start_watchdog() {
|
||||
# Start the wildcard monitor subscriber daemon with --idle-timeout 0 (never idle out)
|
||||
# and ensure it runs with $workdir as cwd to anchor relative log paths.
|
||||
local orig_pwd="$PWD"
|
||||
cd "$workdir"
|
||||
cd "$workdir" || return 1
|
||||
nohup bash "$monitor_script" --subscribe --idle-timeout 0 >> "$log_file" 2>&1 &
|
||||
pid=$!
|
||||
cd "$orig_pwd"
|
||||
cd "$orig_pwd" || return 1
|
||||
fi
|
||||
|
||||
echo "$pid"
|
||||
@@ -1168,6 +1171,10 @@ send_keys_safe() {
|
||||
_sks_tmux set-buffer -b "sks_$job_id" "$text"
|
||||
_sks_tmux paste-buffer -b "sks_$job_id" -t "$sess"
|
||||
_sks_tmux delete-buffer -b "sks_$job_id" 2>/dev/null || true
|
||||
if [[ "$sess" =~ "agy" ]]; then
|
||||
_sks_tmux send-keys -t "$sess" C-m
|
||||
return 0
|
||||
fi
|
||||
sleep 0.5
|
||||
local pane_content was_popup=0
|
||||
pane_content=$(_pane_capture "$sess")
|
||||
@@ -1185,6 +1192,9 @@ send_keys_safe() {
|
||||
local cur_content
|
||||
cur_content=$(_pane_capture "$sess")
|
||||
# Hardened Submission Checks
|
||||
if printf '%s\n' "$cur_content" | grep -Eq "● |Twisting|Thinking"; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$was_popup" = "0" ] && ! _pane_tail "$sess" 3 | grep -Fq "$marker" && [ "$cur_content" != "$pre_submit" ]; then
|
||||
return 0
|
||||
elif [ "$was_popup" = "1" ] && \
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
> **Safety Guard**: `--max-loop` and `--plan-talk` restrict API cost runaways.
|
||||
> **Single source of truth**: `./.mam/agent-sessions.yaml`.
|
||||
|
||||
수동 템플릿 작성 및 수동 프롬프트 환류는 폐지되었습니다. Planner, Creator, Reviewer 간의 모든 협업 피드백 루프는 본 스킬(`run_loop.sh`)만을 단독으로 사용하여 자동으로 오케스트레이션합니다.
|
||||
|
||||
## What this skill does
|
||||
|
||||
Run an autonomous planning-execution-review loop using multiple agents (Planner, Creator, Reviewers) in the workspace. It supports:
|
||||
@@ -16,6 +18,59 @@ Run an autonomous planning-execution-review loop using multiple agents (Planner,
|
||||
|
||||
---
|
||||
|
||||
## Roles & Responsibilities
|
||||
|
||||
협업 시스템은 각 에이전트의 책임 영역을 명확히 격리하여 상호 교차 검증을 강제합니다.
|
||||
|
||||
```
|
||||
┌──────────────────────┐
|
||||
│ User Prompt │
|
||||
└──────────┬───────────┘
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ 1. Planner Agent │ ◄──────────────────┐
|
||||
│ - Plan & Checklist │ │
|
||||
└──────────┬───────────┘ │
|
||||
▼ │
|
||||
┌──────────────────────┐ │
|
||||
│ 2. Creator Agent │ │
|
||||
│ - Code & DoD Verify │ │
|
||||
└──────────┬───────────┘ │
|
||||
▼ │ (NOT PASS Feedback)
|
||||
┌──────────────────────┐ │
|
||||
│ 3. Reviewer Agents │ │
|
||||
│ - Dual Peer Review │ ───────────────────┘
|
||||
└──────────┬───────────┘
|
||||
▼ (PASS)
|
||||
┌──────────────────────┐
|
||||
│ 4. Done & Standby │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
### Planner (설계 및 통제)
|
||||
- **목적**: 요구사항을 명세화하고, 구현 단계의 설계 결함이나 모순(Contradiction)을 사전에 차단합니다.
|
||||
- **역할**:
|
||||
- 사용자 요구사항에 따른 구현 목표 및 범위 수립.
|
||||
- `implementation_plan.md` 및 `task.md` (체크리스트) 작성 및 버전 관리(Rev.1, Rev.2, ...).
|
||||
- 리뷰어 피드백 발생 시 설계 변경의 파급 범위를 계산하여 계획 갱신.
|
||||
- **핵심 원칙**: 직접 코드를 수정하지 않고 오직 설계와 체크리스트 자산만 관리합니다.
|
||||
|
||||
### Creator (구현 및 자가 검증)
|
||||
- **목적**: Planner가 제공한 체크리스트를 기반으로 실제 리포지토리 코드를 물리적으로 수정 및 구현합니다.
|
||||
- **역할**:
|
||||
- `task.md`를 순차적으로 완료 상태(`[x]`)로 업데이트하며 구현 수행.
|
||||
- 커밋 전 **Definition of Done (DoD)** 체크리스트를 자체 실행하여 금지된 코드 패턴, 메모리/구조적 사이드 이펙트 유무 자가 검토.
|
||||
- 수정 사항을 단일 원자적(Atomic) 커밋으로 마감하고 리뷰어에게 전달.
|
||||
|
||||
### Reviewer Agents (교차 피드백 및 검증)
|
||||
- **목적**: 구현된 결과물이 최초 설계서 및 제약 요건에 일치하는지 제3자의 관점에서 엄격하게 검토합니다.
|
||||
- **역할**:
|
||||
- 상위 논리적 정합성(설계 주장과 구현 간 모순 여부) 검증.
|
||||
- 전이 조건, 예외 처리, 타입 시그니처 등 하위 레벨 구현의 세부 사항 기계적 검증.
|
||||
- **판정 규칙**: 지정 또는 자동으로 수집된 모든 리뷰어 세션이 만장일치로 **PASS** 판정을 내릴 때까지 Creator는 마감할 수 없으며, 반려 시 **Planner**에게 피드백이 환류됩니다.
|
||||
|
||||
---
|
||||
|
||||
## Specification & Flow
|
||||
|
||||
```mermaid
|
||||
@@ -65,6 +120,33 @@ sequenceDiagram
|
||||
|
||||
---
|
||||
|
||||
## Feedback Loop Cadence
|
||||
|
||||
1. **Planning Phase**: 사용자가 요구사항을 제시하면 Planner가 프로젝트 구조를 파악하고 `implementation_plan.md`/`task.md`로 로드맵을 제공합니다. 사용자가 승인하면 다음 단계로 이행합니다.
|
||||
2. **Execution Phase**: Creator가 배정된 태스크의 코드를 수정합니다. 진행 중 예상치 못한 설계 변경 필요성이 감지되면 작업을 멈추고 Planner에게 계획 수정을 먼저 위임합니다. 구현 완료 후 DoD(타입 매핑, 공유 자원 사이드 이펙트 방지, 문서-코드 정합성)를 자체 검증한 뒤 단일 커밋을 작성합니다.
|
||||
3. **Review Phase**: Creator가 리뷰어 세션에 작업 완료 사실과 변경 범위(`git diff`)를 전달합니다. 리뷰어는 검증 후 리포트 **마지막에 단독 행**으로 판정을 남깁니다:
|
||||
- **반려 (`[VERDICT: NOT PASS]`)** → 피드백 요약을 Planner에게 전송하여 상위 레벨 계획(Rev.n)을 개시합니다.
|
||||
- **통과 (`[VERDICT: PASS]`)** → 모든 검토 사항이 해결되었음을 명시합니다.
|
||||
4. 지정되거나 자동 수집된 리뷰어 전원이 PASS를 발행해야 완결되며, `--max-loop N`회 내에 도달하지 못하면 안전을 위해 루프를 중단합니다.
|
||||
5. 완결 후에도 에이전트 세션은 종료하지 않고, 다음 태스크 지시가 있을 때까지 프롬프트 대기 상태(Standby)로 유지됩니다.
|
||||
|
||||
---
|
||||
|
||||
## CLI Option ↔ Workflow Phase Mapping
|
||||
|
||||
워크플로우 단계별로 활용할 수 있는 `run_loop.sh` 옵션 규격은 다음과 같습니다:
|
||||
|
||||
| 워크플로우 단계 | 해당 CLI 옵션 | 설명 |
|
||||
| :--- | :--- | :--- |
|
||||
| **Phase 1: Planning** | `--plan` | Planner 에이전트를 기동하여 최초 계획 작성을 강제합니다. |
|
||||
| **Phase 1: Debate** | `--plan-talk N` | Planner와 Creator가 상호 대화식 챌린지 루프를 `N`회 돌며 계획을 교차 정제합니다. |
|
||||
| **Phase 2: Execution** | (기본값) | `--target-agent`로 명시한 주 작업 세션에 코딩 태스크를 주입합니다. |
|
||||
| **Phase 3: Review** | `--reviewer "A,B"` | 지정된 리뷰어 세션 리스트(`A`, `B` 등)에 교차 Peer Review를 위임합니다. |
|
||||
| **Phase 3: Consensus** | `--all-reviewer` | 레지스트리에 등록된 모든 active 리뷰어 세션을 자동으로 수집하여 리뷰를 돌립니다. (지정/수집된 모든 리뷰어의 PASS 만장일치가 항상 필요합니다.) |
|
||||
| **Iterative Loop** | `--max-loop M` | NOT PASS 판정 시 최대 `M`회까지 Creator가 자체 수정합니다. `--plan` 모드에서 리뷰어가 리포트에 `[ESCALATE: PLANNER]` 태그를 남기면 설계 변경 수준으로 판단하여 Planner에게 계획 갱신을 위임합니다 (린트는 리뷰어가 검토 관점 중 하나로 확인할 뿐, 별도의 자동 게이트는 아닙니다). |
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
```bash
|
||||
@@ -74,10 +156,12 @@ bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
--task "Fix typo in deploy/README.md"
|
||||
|
||||
# 2. Collaborative planning + Targeted Reviewers + Safety limits
|
||||
# (실전 자율 루프 기동의 표준 패턴 — 리뷰어 2인 지정 + 전원 합의 + 최대 3회 반복)
|
||||
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
--plan \
|
||||
--plan-talk 1 \
|
||||
--reviewer "canary-projects-multi-agent-mux-reviewer-cline,canary-projects-multi-agent-mux-reviewer-claude" \
|
||||
--all-reviewer \
|
||||
--max-loop 3 \
|
||||
--verbose \
|
||||
--target-agent "canary-projects-multi-agent-mux-creator-claude" \
|
||||
@@ -94,6 +178,8 @@ bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Incorrect Verdict format**: Reviewers MUST output `[VERDICT: PASS]` or `[VERDICT: NOT PASS]` in their final reports for the loop parser to recognize results. If missing, the parser falls back to scanning for "PASS" or "not pass" but warns.
|
||||
- **Session Availability**: Ensure the referenced planner, creator, and reviewer sessions are running or alive before launching `run_loop.sh`.
|
||||
- **NFS Lock Shadowing**: Spawning simultaneous loop processes will serialize job database transactions. Let one loop finish before launching another.
|
||||
- **Incorrect Verdict format (앵커링 파서 하드닝)**: 리뷰 리포트 파일 내에서 `[VERDICT: PASS]` 또는 `[VERDICT: NOT PASS]` 토큰은 반드시 리포트의 **마지막에 단독 행**으로 기재되어야 합니다. 코드 인용이나 변경 diff 내에 등장하는 토큰은 매칭 대상에서 완전 배제됩니다.
|
||||
- **Fail-closed on missing verdict**: 최종 Verdict 토큰이 누락되거나 리포트 픽업에 실패하면, 파서는 **경고 후 통과시키는 것이 아니라** 안전을 위해 즉시 `NOT PASS`로 판정(fail-closed)하고 교정 사이클을 수행합니다. 리뷰어에게는 반드시 리포트 끝에 단독 행으로 토큰을 찍도록 지시해야 합니다.
|
||||
- **Session Availability**: `run_loop.sh` 기동 전에 참조되는 Planner, Target Agent, Reviewer 세션들이 모두 tmux 세션으로 기동되어 (`status.sh` 기준 `alive` 및 `running`) 있어야 합니다.
|
||||
- **동시 루프 기동 금지 (NFS Lock Shadowing)**: 동일한 작업 트리 내에서 다수의 `run_loop.sh` 제어기를 동시에 기동하면 SQLite DB 갱신 경합 및 YAML 데이터 오염이 발생합니다. 하나의 루프가 끝날 때까지 다른 루프를 병렬로 기동하지 마십시오.
|
||||
- **원자적 아카이빙 (Promotion)**: 루프 성공 종료 시 최종 계획서와 검증 리포트들은 `.agents/reports/<session_name>/` 디렉토리로 원자적으로 덮어쓰기(`mv -f`)되어 보존됩니다. 해당 경로의 리포트들로 VCS 추적성을 확보해야 합니다.
|
||||
|
||||
@@ -8,6 +8,7 @@ set -euo pipefail
|
||||
# 1. Load Common Framework Library
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "$REPO_ROOT/.agents/skills/lib.sh"
|
||||
|
||||
# Default configuration parameters
|
||||
@@ -83,6 +84,22 @@ log_error() {
|
||||
echo -e "\033[1;31m[✗]\033[0m $1"
|
||||
}
|
||||
|
||||
# --all-reviewer silently takes precedence over an explicit --reviewer list;
|
||||
# warn so the discarded list isn't mistaken for having been honored (P2-1).
|
||||
if [ "$ALL_REVIEWERS" = true ] && [ -n "$REVIEWER_LIST" ]; then
|
||||
log_warn "--all-reviewer takes precedence; ignoring --reviewer list ('$REVIEWER_LIST')."
|
||||
fi
|
||||
|
||||
# Verdict must occupy the report's last non-blank line — a standalone token
|
||||
# quoted mid-report (e.g. as a formatting example) never matches (P0-2).
|
||||
has_verdict() {
|
||||
local file="$1" verdict="$2"
|
||||
local last_line pattern
|
||||
last_line=$(grep -v '^[[:space:]]*$' "$file" 2>/dev/null | tail -n 1)
|
||||
pattern="^\[VERDICT: ${verdict}\][[:space:]]*\r?\$"
|
||||
[[ "$last_line" =~ $pattern ]]
|
||||
}
|
||||
|
||||
# Helper: Blocking wait for a delegate job's completion or error state (with safety timeout)
|
||||
wait_for_job() {
|
||||
local job_id="$1"
|
||||
@@ -121,100 +138,58 @@ except Exception:
|
||||
return 1
|
||||
}
|
||||
|
||||
# Resolve active reviewers from SQL DB or YAML (excluding $TARGET_AGENT)
|
||||
# Resolve active reviewers (excluding $TARGET_AGENT) via lib.sh's
|
||||
# load_state_json — the single source of truth for the merged DB/YAML
|
||||
# session state, instead of hand-rolling a 4th copy of that lookup (P1-2).
|
||||
resolve_all_reviewers() {
|
||||
TARGET_AGENT="$TARGET_AGENT" python3 -c "
|
||||
import sqlite3, os, yaml, json
|
||||
yaml_path = '.mam/agent-sessions.yaml'
|
||||
db_path = '.mam/agent-sessions.db'
|
||||
TARGET_AGENT="$TARGET_AGENT" MAM_STATE_JSON="$(load_state_json)" python3 -c "
|
||||
import os, json
|
||||
d = json.loads(os.environ.get('MAM_STATE_JSON', '{}'))
|
||||
target_agent = os.environ.get('TARGET_AGENT')
|
||||
reviewers = []
|
||||
if os.path.exists(db_path):
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.execute('SELECT data FROM sessions')
|
||||
for r in cursor.fetchall():
|
||||
s = json.loads(r[0])
|
||||
if s.get('role') == 'reviewer' and s.get('name') != target_agent:
|
||||
reviewers.append(s.get('name'))
|
||||
conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
if not reviewers and os.path.exists(yaml_path):
|
||||
try:
|
||||
with open(yaml_path) as f:
|
||||
d = yaml.safe_load(f) or {}
|
||||
for s in d.get('tmux_sessions', []):
|
||||
if s.get('role') == 'reviewer' and s.get('name') != target_agent:
|
||||
reviewers.append(s.get('name'))
|
||||
except Exception:
|
||||
pass
|
||||
reviewers = [s.get('name') for s in d.get('tmux_sessions', [])
|
||||
if s.get('role') == 'reviewer' and s.get('name') != target_agent]
|
||||
print(','.join(reviewers))
|
||||
"
|
||||
}
|
||||
|
||||
# Resolve target agent type (claude, cline, agy)
|
||||
# Resolve target agent type (claude, cline, agy) via load_state_json.
|
||||
resolve_agent_type() {
|
||||
local name="$1"
|
||||
NAME="$name" python3 -c "
|
||||
import sqlite3, os, yaml, json
|
||||
yaml_path = '.mam/agent-sessions.yaml'
|
||||
db_path = '.mam/agent-sessions.db'
|
||||
NAME="$name" MAM_STATE_JSON="$(load_state_json)" python3 -c "
|
||||
import os, json
|
||||
name = os.environ.get('NAME')
|
||||
agent = 'claude'
|
||||
if os.path.exists(db_path):
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
row = conn.execute('SELECT data FROM sessions WHERE name=?', (name,)).fetchone()
|
||||
if row:
|
||||
agent = json.loads(row[0]).get('agent', 'claude')
|
||||
conn.close()
|
||||
print(agent)
|
||||
raise SystemExit(0)
|
||||
except Exception:
|
||||
pass
|
||||
if os.path.exists(yaml_path):
|
||||
try:
|
||||
with open(yaml_path) as f:
|
||||
d = yaml.safe_load(f) or {}
|
||||
for s in d.get('tmux_sessions', []):
|
||||
d = json.loads(os.environ.get('MAM_STATE_JSON', '{}'))
|
||||
agent = None
|
||||
for s in d.get('tmux_sessions', []):
|
||||
if s.get('name') == name:
|
||||
print(s.get('agent', 'claude'))
|
||||
raise SystemExit(0)
|
||||
except Exception:
|
||||
pass
|
||||
print('claude')
|
||||
agent = s.get('agent') or s.get('pane', {}).get('cmd')
|
||||
break
|
||||
if not agent:
|
||||
# Exact hyphen-segment match, not a naive substring 'in' check, so a
|
||||
# decoy substring inside an unrelated segment can't misclassify.
|
||||
segments = name.split('-')
|
||||
if 'agy' in segments:
|
||||
agent = 'agy'
|
||||
elif 'cline' in segments:
|
||||
agent = 'cline'
|
||||
elif 'hermes' in segments:
|
||||
agent = 'hermes'
|
||||
else:
|
||||
agent = 'claude'
|
||||
print(agent)
|
||||
"
|
||||
}
|
||||
|
||||
# Resolve planner session dynamically
|
||||
# Resolve planner session dynamically via load_state_json.
|
||||
resolve_planner_session() {
|
||||
python3 -c "
|
||||
import sqlite3, os, yaml, json
|
||||
yaml_path = '.mam/agent-sessions.yaml'
|
||||
db_path = '.mam/agent-sessions.db'
|
||||
MAM_STATE_JSON="$(load_state_json)" python3 -c "
|
||||
import os, json
|
||||
d = json.loads(os.environ.get('MAM_STATE_JSON', '{}'))
|
||||
planner = 'canary-projects-multi-agent-mux-planner-claude'
|
||||
if os.path.exists(db_path):
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
row = conn.execute('SELECT name FROM sessions WHERE role=\"planner\" LIMIT 1').fetchone()
|
||||
if row:
|
||||
planner = row[0]
|
||||
conn.close()
|
||||
print(planner)
|
||||
raise SystemExit(0)
|
||||
except Exception:
|
||||
pass
|
||||
if os.path.exists(yaml_path):
|
||||
try:
|
||||
with open(yaml_path) as f:
|
||||
d = yaml.safe_load(f) or {}
|
||||
for s in d.get('tmux_sessions', []):
|
||||
for s in d.get('tmux_sessions', []):
|
||||
if s.get('role') == 'planner':
|
||||
print(s.get('name'))
|
||||
raise SystemExit(0)
|
||||
except Exception:
|
||||
pass
|
||||
planner = s.get('name')
|
||||
break
|
||||
print(planner)
|
||||
"
|
||||
}
|
||||
@@ -248,7 +223,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
log_info "Requesting initial implementation plan from Planner..."
|
||||
PLAN_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \
|
||||
--agent-session "tmux:$PLANNER_SESSION" \
|
||||
--agent "claude" \
|
||||
--agent "$(resolve_agent_type "$PLANNER_SESSION")" \
|
||||
--type "direct" \
|
||||
--prompt "태스크 목표를 바탕으로 구체적인 구현 계획서를 작성해주세요. 목표: $TASK")
|
||||
|
||||
@@ -266,7 +241,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
fi
|
||||
|
||||
# Retrieve plan text safely
|
||||
PLAN_FILE=$(find ".mam/jobs/$PLAN_JOB_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
PLAN_FILE=$(find ".mam/jobs/$PLAN_JOB_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$PLAN_FILE" ] || [ ! -f "$PLAN_FILE" ]; then
|
||||
log_error "Final plan file not found."
|
||||
exit 1
|
||||
@@ -298,7 +273,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CRITIQUE_FILE=$(find ".mam/jobs/$DEBATE_JOB_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
CRITIQUE_FILE=$(find ".mam/jobs/$DEBATE_JOB_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$CRITIQUE_FILE" ] || [ ! -f "$CRITIQUE_FILE" ]; then
|
||||
log_error "Critique file not found."
|
||||
exit 1
|
||||
@@ -308,7 +283,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
log_info "Planner refining plan with Creator's feedback..."
|
||||
REFINE_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \
|
||||
--agent-session "tmux:$PLANNER_SESSION" \
|
||||
--agent "claude" \
|
||||
--agent "$(resolve_agent_type "$PLANNER_SESSION")" \
|
||||
--type "direct" \
|
||||
--prompt "작업자(Creator)로부터 다음 이의제기 피드백을 받았습니다. 피드백을 반영하여 계획서를 정교하게 업데이트(Refine)하여 다시 출력해주세요. 피드백:\n$CRITIQUE_TEXT\n기존 계획서:\n$CURRENT_PLAN")
|
||||
|
||||
@@ -325,7 +300,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REFINE_FILE=$(find ".mam/jobs/$REFINE_JOB_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
REFINE_FILE=$(find ".mam/jobs/$REFINE_JOB_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$REFINE_FILE" ] || [ ! -f "$REFINE_FILE" ]; then
|
||||
log_error "Refinement plan file not found."
|
||||
exit 1
|
||||
@@ -343,6 +318,11 @@ fi
|
||||
# PHASE 2: IMPLEMENTATION (CREATION)
|
||||
# ===========================================================================
|
||||
log_info "=== Phase 2: Code Implementation ==="
|
||||
|
||||
# Fix the pre-implementation commit as the diff baseline so review diffs stay
|
||||
# cumulative and non-empty even after the Creator commits per DoD (P0-1).
|
||||
BASE_COMMIT=$(git rev-parse HEAD 2>/dev/null || echo "")
|
||||
|
||||
EXECUTION_PROMPT="다음 작업 목표를 완성해주세요: $TASK"
|
||||
if [ "$PLAN_MODE" = true ]; then
|
||||
EXECUTION_PROMPT="최종 합의된 다음 계획서에 입각하여 코드를 수정 및 구현해주세요. 계획서:\n$CURRENT_PLAN"
|
||||
@@ -395,7 +375,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
--agent-session "tmux:$TARGET_AGENT" \
|
||||
--agent "$(resolve_agent_type "$TARGET_AGENT")" \
|
||||
--type "direct" \
|
||||
--prompt "작업 완료 상태에 대해 스스로 검증(Self-Review)하여 결함이 없음을 확인하고 종결해주세요. 리포트에 반드시 '[VERDICT: PASS]' 혹은 '[VERDICT: NOT PASS]' 태그를 명시해주세요.")
|
||||
--prompt "작업 완료 상태에 대해 스스로 검증(Self-Review)하여 결함이 없음을 확인하고 종결해주세요. 리뷰 리포트 마지막에 단독 행으로 반드시 '[VERDICT: PASS]' 혹은 '[VERDICT: NOT PASS]' 태그를 명시해주세요.")
|
||||
|
||||
SELF_REV_ID=$(extract_job_id "$SELF_REV_OUTPUT")
|
||||
if [ -z "$SELF_REV_ID" ]; then
|
||||
@@ -405,8 +385,8 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
CREATED_JOBS+=("$SELF_REV_ID")
|
||||
wait_for_job "$SELF_REV_ID"
|
||||
|
||||
REPORT_FILE=$(find ".mam/jobs/$SELF_REV_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -n "$REPORT_FILE" ] && [ -f "$REPORT_FILE" ] && grep -q "\[VERDICT: PASS\]" "$REPORT_FILE" && ! grep -q "\[VERDICT: NOT PASS\]" "$REPORT_FILE"; then
|
||||
REPORT_FILE=$(find ".mam/jobs/$SELF_REV_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -n "$REPORT_FILE" ] && [ -f "$REPORT_FILE" ] && has_verdict "$REPORT_FILE" "PASS" && ! has_verdict "$REPORT_FILE" "NOT PASS"; then
|
||||
log_success "Self-Review PASS."
|
||||
break
|
||||
else
|
||||
@@ -425,14 +405,20 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
|
||||
for rev in "${REVIEWERS[@]}"; do
|
||||
log_info "Requesting code review from Reviewer '$rev'..."
|
||||
# Cumulative working tree diff (M-6)
|
||||
# Cumulative diff since BASE_COMMIT (M-6): includes committed AND
|
||||
# uncommitted changes, so it stays non-empty even after the Creator
|
||||
# commits per the documented DoD (bare `git diff` alone would not).
|
||||
if [ -n "$BASE_COMMIT" ]; then
|
||||
CHANGES_DIFF=$(git diff "$BASE_COMMIT" 2>/dev/null || echo "No git diff available")
|
||||
else
|
||||
CHANGES_DIFF=$(git diff 2>/dev/null || echo "No git diff available")
|
||||
fi
|
||||
|
||||
REV_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \
|
||||
--agent-session "tmux:$rev" \
|
||||
--agent "$(resolve_agent_type "$rev")" \
|
||||
--type "direct" \
|
||||
--prompt "다음 구현 사항(작업 목표: $TASK) 및 누적 변경분(git diff)에 대해 린트, 동작성, 유실 등의 관점에서 교차 코드 리뷰를 수행해주세요. 확인 후 최종 Verdict로 '[VERDICT: PASS]' 혹은 '[VERDICT: NOT PASS]' 태그를 리뷰 리포트에 명시적으로 작성해주세요. 변경분:\n$CHANGES_DIFF")
|
||||
--prompt "다음 구현 사항(작업 목표: $TASK) 및 누적 변경분(git diff)에 대해 린트, 동작성, 유실 등의 관점에서 교차 코드 리뷰를 수행해주세요. 확인 후 최종 Verdict로 '[VERDICT: PASS]' 혹은 '[VERDICT: NOT PASS]' 태그를 리뷰 리포트 마지막에 단독 행으로 명시적으로 작성해주세요. 만약 단순 버그 수정으로는 부족하고 설계 변경/재작업 수준의 재계획이 필요하다고 판단되면, 리포트 아무 곳에나 단독 행으로 '[ESCALATE: PLANNER]' 태그도 함께 남겨주세요. 변경분:\n$CHANGES_DIFF")
|
||||
|
||||
REV_JOB_ID=$(extract_job_id "$REV_OUTPUT")
|
||||
if [ -z "$REV_JOB_ID" ]; then
|
||||
@@ -459,7 +445,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
fi
|
||||
|
||||
# Parse verdict from report file (fails-safe, M-2 anchored checks)
|
||||
REPORT_FILE=$(find ".mam/jobs/$job_id" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
REPORT_FILE=$(find ".mam/jobs/$job_id" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$REPORT_FILE" ] || [ ! -f "$REPORT_FILE" ]; then
|
||||
log_warn "Reviewer '$rev' report not found. Counting as NOT PASS."
|
||||
all_passed=false
|
||||
@@ -469,7 +455,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
REPORT_CONTENT=$(cat "$REPORT_FILE" 2>/dev/null || echo "")
|
||||
|
||||
# Precedence rules: NOT PASS wins over PASS. Absence of verdict tags is treated as NOT PASS (fail-closed)
|
||||
if grep -q "\[VERDICT: NOT PASS\]" "$REPORT_FILE" || ! grep -q "\[VERDICT: PASS\]" "$REPORT_FILE"; then
|
||||
if has_verdict "$REPORT_FILE" "NOT PASS" || ! has_verdict "$REPORT_FILE" "PASS"; then
|
||||
log_warn "Reviewer '$rev': NOT PASS"
|
||||
all_passed=false
|
||||
FEEDBACK_AGGREGATE="$FEEDBACK_AGGREGATE\n--- Reviewer ($rev) Feedback ---\n$REPORT_CONTENT"
|
||||
@@ -487,9 +473,11 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Re-planning check for complex refactoring feedback
|
||||
# Re-planning check: rely on the explicit '[ESCALATE: PLANNER]' tag a
|
||||
# reviewer is instructed to emit, rather than sniffing English keywords
|
||||
# (reviewers report in Korean, so keyword matching never fired) (P1-1).
|
||||
COMPLEX_FIX=false
|
||||
if echo "$FEEDBACK_AGGREGATE" | grep -Eiq "refactor|complex|design|architect"; then
|
||||
if echo "$FEEDBACK_AGGREGATE" | grep -qE '^\[ESCALATE: PLANNER\][[:space:]]*\r?$'; then
|
||||
COMPLEX_FIX=true
|
||||
fi
|
||||
|
||||
@@ -498,7 +486,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
|
||||
REFINE_PLAN_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \
|
||||
--agent-session "tmux:$PLANNER_SESSION" \
|
||||
--agent "claude" \
|
||||
--agent "$(resolve_agent_type "$PLANNER_SESSION")" \
|
||||
--type "direct" \
|
||||
--prompt "리뷰어들로부터 다음과 같이 정교한 코드 수정 피드백이 도착했습니다. 해당 피드백을 수렴하여 구현 계획서(Plan)를 갱신(Refine)하여 다시 작성해주세요. 피드백:\n$FEEDBACK_AGGREGATE\n기존 계획서:\n$CURRENT_PLAN")
|
||||
|
||||
@@ -510,7 +498,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
CREATED_JOBS+=("$REFINE_PLAN_ID")
|
||||
wait_for_job "$REFINE_PLAN_ID"
|
||||
|
||||
REFINE_PLAN_FILE=$(find ".mam/jobs/$REFINE_PLAN_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
REFINE_PLAN_FILE=$(find ".mam/jobs/$REFINE_PLAN_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$REFINE_PLAN_FILE" ] || [ ! -f "$REFINE_PLAN_FILE" ]; then
|
||||
log_error "Refined plan file not found."
|
||||
exit 1
|
||||
@@ -543,6 +531,30 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
loop_count=$((loop_count + 1))
|
||||
done
|
||||
|
||||
# Promote finalized plan and passed reviewer reports to durable location
|
||||
if [ "$PLAN_MODE" = true ] && [ -n "${CURRENT_PLAN:-}" ] && [ -n "${PLAN_JOB_ID:-}" ]; then
|
||||
plan_dest_dir=".agents/reports/$PLANNER_SESSION"
|
||||
log_info "Promoting final plan to durable location: $plan_dest_dir"
|
||||
mkdir -p "$plan_dest_dir"
|
||||
echo "$CURRENT_PLAN" > "$plan_dest_dir/plan-${PLAN_JOB_ID}.md.tmp"
|
||||
mv -f "$plan_dest_dir/plan-${PLAN_JOB_ID}.md.tmp" "$plan_dest_dir/plan-${PLAN_JOB_ID}.md"
|
||||
fi
|
||||
|
||||
if [ "${#REVIEWERS[@]}" -gt 0 ]; then
|
||||
log_info "Promoting final review reports to durable location..."
|
||||
for idx in "${!JOB_IDS[@]}"; do
|
||||
job_id="${JOB_IDS[$idx]}"
|
||||
rev="${JOB_REVS[$idx]}"
|
||||
report_file=$(find ".mam/jobs/$job_id" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -n "$report_file" ] && [ -f "$report_file" ]; then
|
||||
dest_dir=".agents/reports/$rev"
|
||||
mkdir -p "$dest_dir"
|
||||
cp "$report_file" "$dest_dir/report-${job_id}.md.tmp"
|
||||
mv -f "$dest_dir/report-${job_id}.md.tmp" "$dest_dir/report-${job_id}.md"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# PHASE 4: CLEANUP
|
||||
# ===========================================================================
|
||||
|
||||
@@ -67,4 +67,4 @@ Strong success criteria let you loop independently. Weak criteria ("make it work
|
||||
|
||||
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
||||
|
||||
Read [MULTI_AGENT_RULES.md](.agents/MULTI_AGENT_RULES.md) (or [Korean version](.agents/MULTI_AGENT_RULES.ko.md)) and [multi_agent_workflow.md](.agents/multi_agent_workflow.md) first before working and follow the instructions for orchestration and collaboration.
|
||||
Read [MULTI_AGENT_RULES.md](.agents/MULTI_AGENT_RULES.md) (or [Korean version](.agents/MULTI_AGENT_RULES.ko.md)) and [multi-agent-mux-loop/SKILL.md](.agents/skills/multi-agent-mux-loop/SKILL.md) first before working and follow the instructions for orchestration and collaboration.
|
||||
Reference in New Issue
Block a user