Compare commits
7
Commits
76151c76b2
...
7797b31d45
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7797b31d45 | ||
|
|
b8db134f79 | ||
|
|
87b4501bbf | ||
|
|
d56f60bbc6 | ||
|
|
80c9e37b77 | ||
|
|
ad8201d706 | ||
|
|
926ca5452b |
@@ -131,6 +131,6 @@ emit("deny",
|
||||
"The /multi-agent-mux-loop skill is active, so direct file edits are out "
|
||||
"of scope for the orchestrator. Stop editing and delegate instead: run "
|
||||
"bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh "
|
||||
"--target-agent <session> --task <goal>. "
|
||||
"--creator <session> --task <goal>. "
|
||||
"See .agents/MULTI_AGENT_RULES.md #3.2 (Invocation-Aware Scoped Guard).")
|
||||
PY
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
# Multi-Agent Mux Loop: CLI Option Redesign Final Architecture Consensus (Rev.4)
|
||||
|
||||
> **문서 상태**: Creator 재평가 반영 — `--target-agent` 완전 제거안 (Reviewer 판정 대기)
|
||||
> **합의 참여 에이전트**: `Grok` (Creator), `Claude` (Planner/Reviewer), `Cline` (Reviewer), `AGY` (Creator Lead)
|
||||
> **대상 컴포넌트**: `multi-agent-mux-loop` (`run_loop.sh`, `SKILL.md`, `deploy/INSTALL.md`, `tests/`)
|
||||
> **핵심 설계 모델**: Fail-Safe Orthogonality — 단계 스위치와 세션 식별자를 분리하고, 레거시 alias는 두지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 1. Rev.4 결정: `--target-agent` 제거
|
||||
|
||||
Rev.3는 `--target-agent`를 `--creator`의 영구 alias로 남겼다. Creator (`creator-grok-01`)는 그 선택을 철회한다.
|
||||
|
||||
**정본 플래그는 `--creator` 하나다. `--target-agent`는 파싱하지 않고, 전달되면 즉시 거부한다.**
|
||||
|
||||
### 1.1 제거하는 이유
|
||||
|
||||
Alias를 남기면 얻는 것은 저장소 안 문자열 5개의 무중단뿐이고, 비용은 파서 이중 변수·충돌 행렬·에러 문구 이중화·테스트 분기이다. Rev.2에서 지적한 “한 `case` 팔에 덮어쓰기” 버그는 그 이중 파서가 만든 구멍이다.
|
||||
|
||||
| 기준 | 영구 alias (Rev.3) | 완전 제거 (Rev.4) |
|
||||
| :--- | :--- | :--- |
|
||||
| 파서 | `CREATOR_OPT` + `TARGET_AGENT_OPT` + 같음/다름 분기 | `--creator) TARGET_AGENT="$2"` 한 줄 |
|
||||
| 실패 모드 | 구현이 변수를 합치면 충돌을 침묵 덮어씀 | 충돌 상태 자체가 존재하지 않음 |
|
||||
| in-repo 호출 | 3개 테스트 + `INSTALL.md` 예시 1개 + SKILL 예시 | 같은 파일을 `--creator`로 고치면 끝 |
|
||||
| 외부 API | `run_loop.sh`는 배포된 공용 CLI가 아님 | 호환 공약이 필요 없음 |
|
||||
| AGENTS.md | “요청되지 않은 유연성” | Simplicity First에 부합 |
|
||||
|
||||
in-repo 실측 호출부 (`--target-agent`):
|
||||
|
||||
- `tests/test_o3_scoped_guard.py` (2)
|
||||
- `tests/test_o2_race_free_lock.py` (1)
|
||||
- `tests/test_tier4_e2e.py` (1)
|
||||
- `deploy/INSTALL.md` (1)
|
||||
|
||||
이 변경과 같은 커밋에서 `--creator`로 치환한다. alias 유지 비용이 치환 비용보다 크다.
|
||||
|
||||
### 1.2 `--target-agent`가 들어왔을 때
|
||||
|
||||
일반 `Unknown option`에 맡기지 않는다. 방금 없앤 이름에는 한 줄 힌트를 주고 종료한다. 값은 읽지 않는다. alias가 아니다.
|
||||
|
||||
```text
|
||||
ERROR: --target-agent was removed. Use --creator <session> instead.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 설계 원칙
|
||||
|
||||
수사적 “3역할 완전 대칭”은 쓰지 않는다. 루프 라이프사이클이 비대칭이고, CLI는 그 비대칭을 그대로 드러낸다.
|
||||
|
||||
| 역할 | 라이프사이클 | CLI |
|
||||
| :--- | :--- | :--- |
|
||||
| **Planner** | Phase 1은 생략 가능 | `--plan` (단계) ⊥ `--planner <name>` (세션) |
|
||||
| **Creator** | Phase 2는 항상 필요 | `--creator <name>` 필수. 단계 스위치 없음 |
|
||||
| **Reviewer** | 없으면 Self-Review | `--reviewer A,B` 또는 `--all-reviewer` |
|
||||
|
||||
`--planner`가 `--plan`을 암시하지 않는다. 식별자가 제어 흐름을 바꾸지 않는다.
|
||||
|
||||
`--reviewer` + `--all-reviewer`는 기존대로 warn-and-precedence (`--all-reviewer` 우선). Creator 쪽 fail-fast와 다른 이유는 alias 충돌이 아니라 **기존 거버넌스 유지**이다.
|
||||
|
||||
역할 문자열 검사(`role`에 `creator`/`planner` 포함)는 이번 범위 밖이다. 현재 `--target-agent`도 등록·running만 본다. `--planner`만 역할 검사하면 비대칭이 된다. 복합 role `planner,reviewer`는 자동 탐색 시 지금처럼 substring 매칭으로 허용한다.
|
||||
|
||||
---
|
||||
|
||||
## 3. CLI 규격
|
||||
|
||||
| 역할 / 계층 | CLI 플래그 | 형태 | 필수 | 동작 |
|
||||
| :--- | :--- | :---: | :---: | :--- |
|
||||
| **Creator** | `--creator <name>` | Value | **필수** | 구현 세션. 내부 변수는 기존 `TARGET_AGENT`에 대입해 스크립트 잔여 경로를 건드리지 않는다 |
|
||||
| **Planner** | `--plan` | Flag | 선택 | Phase 1 활성화 |
|
||||
| | `--planner <name>` | Value | 선택 | Phase 1 세션. **`--plan`과 함께만**. 지정 시 `resolve_planner_session` 호출 금지 |
|
||||
| | `--plan-talk N` | Int | 선택 | Planner ↔ Creator 챌린지 횟수 (기본 1). `--plan` 없으면 기존처럼 경고 후 무시 |
|
||||
| **Reviewer** | `--reviewer "A,B"` | Value | 선택 | 지정 리뷰어 |
|
||||
| | `--all-reviewer` | Flag | 선택 | running reviewer 전원, 만장일치 PASS |
|
||||
| **공통** | `--task "<goal>"` | Value | **필수** | 작업 목표 |
|
||||
| | `--max-loop M` | Int | 선택 | 교정 루프 상한 (기본 3, ≥1) |
|
||||
| | `--max-rebut N` | Int | 선택 | 이터레이션당 반론 상한 (기본 1, 0이면 끔) |
|
||||
| | `--verbose` | Flag | 선택 | 상세 로그 |
|
||||
| | `--cleanup` | Flag | 선택 | 성공 시 `.mam/jobs/<id>` 임시 트리 삭제 |
|
||||
| | `-h` / `--help` | Flag | 선택 | usage 후 종료 |
|
||||
| **제거됨** | `--target-agent` | — | 거부 | 전용 에러 후 `exit 1`. 값 파싱 없음 |
|
||||
|
||||
`usage()` 첫 줄은 `--creator <session> --task <goal>`을 정본으로 적는다. `--target-agent`는 usage 옵션 목록에 올리지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 호출 예시
|
||||
|
||||
### ① 풀 팀 (계획 + 지정 플래너 + 지정 리뷰어)
|
||||
|
||||
```bash
|
||||
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
--creator creator-grok-01 \
|
||||
--plan --planner planner-reviewer-claude-01 \
|
||||
--reviewer reviewer-cline-01 \
|
||||
--task "새로운 분산 세션 동기화 엔진 구현"
|
||||
```
|
||||
|
||||
### ② 플래너 자동 탐색 + 전체 리뷰어
|
||||
|
||||
```bash
|
||||
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
--creator creator-agy-01 \
|
||||
--plan \
|
||||
--all-reviewer \
|
||||
--task "코어 라이브러리 리팩토링"
|
||||
```
|
||||
|
||||
### ③ Creator 단독 (셀프 계획 + 셀프 리뷰)
|
||||
|
||||
```bash
|
||||
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
--creator creator-grok-01 \
|
||||
--task "README.md 오타 수정 및 CLI 도움말 갱신"
|
||||
```
|
||||
|
||||
레거시 `--target-agent` 예시는 삭제한다. 그 플래그는 더 이상 유효한 호출이 아니다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 엣지 케이스
|
||||
|
||||
| 상황 | 결과 | 메시지 / 처리 |
|
||||
| :--- | :--- | :--- |
|
||||
| `--planner`만 있고 `--plan` 없음 | Fail-fast, freeze 전 `echo`, `exit 1` | `ERROR: --planner was specified without --plan.` + `--plan --planner` 사용 예 |
|
||||
| `--target-agent` 전달 | Fail-fast, freeze 전 `echo`, `exit 1` | `ERROR: --target-agent was removed. Use --creator <session> instead.` |
|
||||
| `--creator` 또는 `--task` 누락 | Fail-fast, freeze 전 | `ERROR: --creator and --task are mandatory fields.` |
|
||||
| `--planner <name>` 미등록 | Fail-fast, freeze 후 `log_error` | `specified planner session '<name>' is not registered in the session registry.` |
|
||||
| `--planner <name>` 등록됐으나 running 아님 | Fail-fast, freeze 후 `log_error` | `specified planner session '<name>' is not running (current status: '<status>').` |
|
||||
| `--plan`만 있고 `--planner` 없음 | 기존 자동 탐색 | running 이고 role에 `planner`가 있는 첫 세션. 없으면 기존 에러 |
|
||||
| `--reviewer` + `--all-reviewer` | 기존 유지 | `log_warn` 후 `--all-reviewer` 우선 |
|
||||
| `--plan-talk` 정수 아님 / `--max-loop` ≤0 / `--max-rebut` 비정수 | 기존 유지 | freeze 전 `echo`, `exit 1` |
|
||||
|
||||
`--creator`와 `--planner`의 세션 동일 여부, 역할 문자열 일치 여부는 검사하지 않는다 (현행과 동일).
|
||||
|
||||
---
|
||||
|
||||
## 6. 구현 블루프린트
|
||||
|
||||
Freeze 전 파서 오류는 원시 `echo` (B-13: `log_*`는 freeze 이후에만 정의됨). 세션 레지스트리 조회는 freeze 이후 `log_error` / `log_warn`.
|
||||
|
||||
### 6.1 Pre-freeze 파서
|
||||
|
||||
기존 정수 검사·그 외 플래그는 보존한다. 추가/변경은 다음뿐이다.
|
||||
|
||||
```bash
|
||||
# --creator replaces --target-agent. Internal name TARGET_AGENT is unchanged.
|
||||
# --planner) PLANNER_SESSION_OVERRIDE="$2"; shift 2 ;;
|
||||
|
||||
case "$1" in
|
||||
--creator) TARGET_AGENT="$2"; shift 2 ;;
|
||||
--target-agent)
|
||||
echo "ERROR: --target-agent was removed. Use --creator <session> instead." >&2
|
||||
exit 1
|
||||
;;
|
||||
--planner) PLANNER_SESSION_OVERRIDE="$2"; shift 2 ;;
|
||||
# ... existing cases unchanged ...
|
||||
esac
|
||||
|
||||
if [ -z "$TARGET_AGENT" ] || [ -z "$TASK" ]; then
|
||||
echo "ERROR: --creator and --task are mandatory fields." >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ -n "${PLANNER_SESSION_OVERRIDE:-}" ] && [ "$PLAN_MODE" = false ]; then
|
||||
echo "ERROR: --planner was specified without --plan." >&2
|
||||
echo "To enable planning, include the --plan flag:" >&2
|
||||
echo " run_loop.sh --creator <creator> --plan --planner <planner> --task \"...\"" >&2
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
두 개의 Creator 변수도, 충돌 비교도 없다.
|
||||
|
||||
### 6.2 Post-freeze 플래너 결정
|
||||
|
||||
`--planner`가 있으면 `resolve_planner_session`을 호출하지 않는다.
|
||||
|
||||
```bash
|
||||
if [ "$PLAN_MODE" = true ]; then
|
||||
if [ -n "${PLANNER_SESSION_OVERRIDE:-}" ]; then
|
||||
PLANNER_SESSION="$PLANNER_SESSION_OVERRIDE"
|
||||
# same two-step check as TARGET_AGENT: missing vs not-running
|
||||
else
|
||||
PLANNER_SESSION=$(resolve_planner_session)
|
||||
if [ -z "$PLANNER_SESSION" ]; then
|
||||
log_error "Planner mode enabled (--plan) but no running session with a 'planner' role was found."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
명시 `--planner` 검증은 기존 TARGET_AGENT 조회와 같은 패턴을 재사용한다 (`load_state_json`, 이름 일치, `status`). 역할 필드는 보지 않는다.
|
||||
|
||||
### 6.3 문서·테스트 (같은 변경에 포함)
|
||||
|
||||
문서:
|
||||
|
||||
- `run_loop.sh` `usage()` — `--creator` 정본, `--target-agent` 미기재
|
||||
- `.agents/skills/multi-agent-mux-loop/SKILL.md` — CLI 표·예시
|
||||
- `deploy/INSTALL.md` — 대표 예시를 `--creator`로 치환
|
||||
|
||||
테스트 위치: `tests/test_tier1_unit.py`에 넣지 않는다. 그 파일은 `run_loop` 스위트가 아니다.
|
||||
|
||||
Pre-freeze (프로세스만 기동, 락/레지스트리 불필요). `test_o1_rebuttal.py`와 같은 방식으로 `run_loop.sh`를 직접 호출한다. 신규 파일도 허용한다.
|
||||
|
||||
- `--creator` + `--task` 누락 → `exit 1`
|
||||
- `--planner` without `--plan` → `exit 1`, 메시지에 `--plan` 안내
|
||||
- `--target-agent` → `exit 1`, `was removed` / `Use --creator`
|
||||
- `--help`에 `--creator` 있고 `--target-agent`는 옵션 목록에 없음
|
||||
|
||||
기존 `--target-agent` 호출 치환 (동작 유지, 플래그만 변경):
|
||||
|
||||
- `tests/test_o3_scoped_guard.py`
|
||||
- `tests/test_o2_race_free_lock.py`
|
||||
- `tests/test_tier4_e2e.py`
|
||||
|
||||
Post-freeze 샌드박스 (레지스트리 있는 기존 픽스처):
|
||||
|
||||
- `--plan --planner <running>` 이 자동 탐색을 건너뛰고 그 세션을 쓰는지
|
||||
- 미등록 `--planner` / 비-running `--planner` 각각 다른 에러
|
||||
|
||||
---
|
||||
|
||||
## 7. 이번 범위에 넣지 않는 것
|
||||
|
||||
- `--creator` / `--planner` 역할 필드 검사 (후속, 넣을 거면 둘 다)
|
||||
- `--planner`가 `--plan`을 암시
|
||||
- `--all-planner`
|
||||
- `--reviewer`+`--all-reviewer`를 fail-fast로 승격
|
||||
- `TARGET_AGENT` 내부 식별자 전면 rename
|
||||
- 한글 프롬프트 → `brief.md` 이관, 셀프 리뷰 교정 잡 등 루프 본체 다른 과제
|
||||
|
||||
---
|
||||
|
||||
## 8. Reviewer에게 묻는 판정 포인트
|
||||
|
||||
1. `--target-agent` 완전 제거 (전용 에러, 값 미파싱)를 수용하는가, Rev.3 영구 alias로 되돌릴 것인가.
|
||||
2. 내부 변수명 `TARGET_AGENT` 유지를 수용하는가.
|
||||
3. 섹션 6 체크리스트가 구현 단위로 충분한가.
|
||||
|
||||
`[VERDICT: PASS]`는 위 세 항에 이견이 없을 때만 발행한다. alias 복원을 원하면 근거와 함께 `[VERDICT: NOT PASS]`로 돌린다.
|
||||
@@ -0,0 +1,97 @@
|
||||
# ⚖️ Architecture Debate & Consensus Report: Orthogonal vs. Coupled CLI Design for `multi-agent-mux-loop`
|
||||
|
||||
- **Author**: `creator-agy-01` (Worker / Creator Team Leader)
|
||||
- **Reviewers / Contributors**: `planner-reviewer-claude-01`, `reviewer-cline-01`, `grok`
|
||||
- **Job ID**: `53ff6303`
|
||||
- **Topic**: Phase Switch (`--plan`) vs. Target Identity (`--planner <name>`) Orthogonality vs. Coupling
|
||||
- **Status**: Consensus Recommendation (ANALYSIS ONLY)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Debate Context
|
||||
|
||||
In the redesign of `multi-agent-mux-loop` (`run_loop.sh`) to establish symmetric 3-tier role flags (`--planner`, `--creator`, `--reviewer`), an architectural debate arose regarding the relationship between `--plan` and `--planner <name>`:
|
||||
|
||||
1. **Initial Coupled Proposal (Claude / AGY initial view)**:
|
||||
- Passing `--planner <name>` automatically and implicitly activates Phase 1 (`PLAN_MODE=true`).
|
||||
- *Driver*: Ergonomics, brevity, DWIM (Do What I Mean).
|
||||
|
||||
2. **Grok's Orthogonal Proposal (`grok`)**:
|
||||
- Make `--plan` (Phase Switch: Enable planning phase) and `--planner <name>` (Target Identity: Which session to use) **strictly orthogonal**.
|
||||
- Invocation: `bash run_loop.sh --creator <name> --plan --planner <name> --reviewer <name> --task "..."`
|
||||
- *Driver*: Unix design philosophy (separation of mechanism vs. policy), predictable state machines, zero "magic" side-effects, composability for automated multi-agent pipeline scripts.
|
||||
|
||||
---
|
||||
|
||||
## 2. In-Depth Trade-Off Analysis
|
||||
|
||||
| Dimension | Coupled / Implicit Design (`--planner` auto-enables `--plan`) | Strictly Orthogonal Design (`--plan` separate from `--planner`) |
|
||||
| :--- | :--- | :--- |
|
||||
| **Ergonomics & Brevity** | **Superior for Interactive CLI**: Eliminates redundant flags (`--planner foo` instead of `--plan --planner foo`). | **Slightly More Verbose**: Requires passing both `--plan` and `--planner foo`. |
|
||||
| **Predictability & State Machine** | **Risk of Ambiguity**: If flags are assembled dynamically by scripts, setting `--planner "$VAR"` might unexpectedly activate planning when `$VAR` is present but planning was not intended. | **Superior Predictability**: Phase activation is 100% controlled by `--plan`; session binding is 100% controlled by `--planner`. |
|
||||
| **Error Modes** | If caller passes `--planner foo --no-plan` (contradiction), complex precedence resolution is needed. | If caller passes `--planner foo` without `--plan`, system can fail-fast with a clear, actionable validation error. |
|
||||
| **Symmetry across Roles** | Asymmetric with Reviewer tier (where `--all-reviewer` is a phase/aggregation switch and `--reviewer` is session identity). | Highly symmetric: Phase switches (`--plan`, `--all-reviewer`) operate independently of Identity specifications (`--planner`, `--creator`, `--reviewer`). |
|
||||
|
||||
---
|
||||
|
||||
## 3. Team Perspectives & Reviewer Synthesis
|
||||
|
||||
### 3.1 Grok's Perspective
|
||||
- **Core Argument**: In automated agent orchestration, hidden side-effects are a common source of subtle pipeline bugs. Having a flag change both *identity* and *execution flow* breaks the single-responsibility principle of CLI options.
|
||||
- **Key Recommendation**: Explicit is better than implicit.
|
||||
|
||||
### 3.2 Claude's Perspective (`10a3201c`)
|
||||
- **Core Argument**: Endorsed role symmetry. Acknowledged that user intent is rarely to specify a planner session and *not* execute planning, but emphasized that conflicting states must be prevented.
|
||||
|
||||
### 3.3 Cline's Perspective (`75c06a1e`)
|
||||
- **Core Argument**: Detailed critical implementation realities in `run_loop.sh`:
|
||||
- Input validation (`--plan-talk`, `--max-loop`, `--max-rebut`) must be preserved.
|
||||
- B-13 freeze snapshot ordering means pre-freeze error emission must use raw `echo`, while post-freeze uses `log_*`.
|
||||
- Session existence and liveness validation for `--planner <name>` must be explicitly performed post-freeze.
|
||||
|
||||
---
|
||||
|
||||
## 4. The Consensus Recommendation: "Orthogonal with Fail-Safe Validation"
|
||||
|
||||
To synthesize Grok's rigor with high CLI usability, the team recommends the **Orthogonal with Fail-Safe Validation** model:
|
||||
|
||||
### 4.1 Specification Rules
|
||||
1. **Explicit Roles & Switches**:
|
||||
- `--plan`: Enables Phase 1 (Planning). If passed without `--planner`, it auto-discovers the running planner via `load_state_json` (`resolve_planner_session`).
|
||||
- `--planner <session>`: Explicitly identifies the target session for Phase 1.
|
||||
- `--creator <session>` (or legacy alias `--target-agent <session>`): Mandatory target session for Phase 2 (Implementation).
|
||||
- `--reviewer <list>` / `--all-reviewer`: Target identity and aggregation for Phase 3 (Verification).
|
||||
2. **Deterministic Orthogonal Enforcement**:
|
||||
- Standard invocation with explicit planner:
|
||||
```bash
|
||||
bash run_loop.sh --creator <name> --plan --planner <name> --task "..."
|
||||
```
|
||||
3. **Fail-Fast Error on Omission (Zero Magic, Zero Silent Dropping)**:
|
||||
- If `--planner <name>` is passed **without** `--plan`:
|
||||
- **Do NOT silently ignore `--planner`** (which would surprise the user by skipping planning).
|
||||
- **Fail-fast with exit code 1**:
|
||||
```text
|
||||
ERROR: --planner was specified without --plan.
|
||||
To enable planning with this planner, please include the --plan flag:
|
||||
run_loop.sh --creator <creator> --plan --planner <planner> --task "..."
|
||||
```
|
||||
- *Why this is the optimal consensus*: It prevents magic side-effects (satisfying Grok's orthogonality requirement) while preventing accidental omission bugs (satisfying AGY/Claude/Cline's UX safety requirement).
|
||||
|
||||
---
|
||||
|
||||
## 5. Summary Matrix of CLI Invocations
|
||||
|
||||
| Use Case | Invocation Syntax | Behavior |
|
||||
| :--- | :--- | :--- |
|
||||
| **Creator Self-Planning (Default)** | `run_loop.sh --creator c1 --task "..."` | No Phase 1. Creator plans and implements. Self-review. |
|
||||
| **Auto-Discovered Planner** | `run_loop.sh --creator c1 --plan --task "..."` | Phase 1 runs using auto-discovered planner session. |
|
||||
| **Explicit Targeted Planner** | `run_loop.sh --creator c1 --plan --planner p1 --task "..."` | Phase 1 runs targeting `p1`. |
|
||||
| **Misconfiguration Guard** | `run_loop.sh --creator c1 --planner p1 --task "..."` | **Fails fast with clear error**: Prompting user to add `--plan`. |
|
||||
| **Targeted Peer Review** | `run_loop.sh --creator c1 --reviewer "r1,r2" --task "..."` | Phase 2 -> Phase 3 with reviewers `r1` and `r2`. |
|
||||
| **Full Team (All Tiers)** | `run_loop.sh --creator c1 --plan --planner p1 --all-reviewer --task "..."` | Full 3-tier orchestration with unanimous review. |
|
||||
|
||||
---
|
||||
|
||||
## 6. Conclusion
|
||||
|
||||
The debate brought valuable architectural precision to Multi-Agent Mux. By adopting **Orthogonal CLI flags with fail-fast validation**, we maintain clean Unix separation of concerns, robust pipeline automation, and clear, foolproof ergonomics.
|
||||
@@ -0,0 +1,235 @@
|
||||
# 🏛️ Definitive Architecture Consensus & Implementation Specification: `multi-agent-mux-loop` CLI Redesign
|
||||
|
||||
- **Author / Synthesist**: `creator-agy-01` (Worker / Creator Team Leader)
|
||||
- **Contributors**: `planner-reviewer-claude-01`, `reviewer-cline-01`, `grok`
|
||||
- **Job ID**: `9f2ae7bd`
|
||||
- **Version**: Rev.3 (Final Consensus — Incorporating Grok's Critique & All Reviewer Findings)
|
||||
- **Supersedes**: Supersedes Section 4 & Edge Case 3.2 of `cli_redesign_opinion.md` and extends `cli_redesign_debate_consensus.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Core Paradigm
|
||||
|
||||
The Multi-Agent Mux team has converged on the **Orthogonal with Fail-Safe Validation** architecture for `multi-agent-mux-loop` (`run_loop.sh`).
|
||||
|
||||
### Core Principles:
|
||||
1. **Separation of Phase Switch vs. Target Identity**:
|
||||
- **Phase Switches** (`--plan`, `--all-reviewer`) control *which phases execute*.
|
||||
- **Target Identities** (`--planner`, `--creator`, `--reviewer`) control *which sessions execute those phases*.
|
||||
2. **Fail-Fast Safety (No Magic, No Silent Drops)**:
|
||||
- Passing an identity without its corresponding phase switch (e.g., `--planner <name>` without `--plan`) immediately **fails fast with exit code 1**, providing an actionable error message and exact remediation syntax.
|
||||
3. **Role Lifecycle Alignment**:
|
||||
- **Planner Tier**: Optional phase (`--plan` to activate, `--planner` to bind, default: Creator self-planning).
|
||||
- **Creator Tier**: Mandatory execution (`--creator` to bind, with `--target-agent` as 100% backward-compatible alias).
|
||||
- **Reviewer Tier**: Optional peer verification (`--reviewer` for targeted list, `--all-reviewer` for all active reviewers, default: Creator self-review).
|
||||
|
||||
---
|
||||
|
||||
## 2. Exhaustive Resolution of Grok's Critique & Spec Gaps
|
||||
|
||||
### 2.1 Parser Variable Separation (Conflict Detection Fix)
|
||||
- **Issue**: Parsing `--creator|--target-agent)` into a single variable in the `case` loop overwrites the first flag, making conflicting input (`--creator sess-A --target-agent sess-B`) undetectable.
|
||||
- **Fix**: Parse into two distinct variables: `CREATOR_OPT=""` and `TARGET_AGENT_OPT=""`.
|
||||
- **Pre-Freeze Resolution**: Check for conflicts immediately after the `while` loop using raw `echo` (before B-13 freeze snapshot re-exec):
|
||||
```bash
|
||||
if [ -n "$CREATOR_OPT" ] && [ -n "$TARGET_AGENT_OPT" ]; then
|
||||
if [ "$CREATOR_OPT" != "$TARGET_AGENT_OPT" ]; then
|
||||
echo "ERROR: Conflicting creator sessions specified via --creator ('$CREATOR_OPT') and --target-agent ('$TARGET_AGENT_OPT')."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
TARGET_AGENT="${CREATOR_OPT:-$TARGET_AGENT_OPT}"
|
||||
```
|
||||
|
||||
### 2.2 Spec Gap (a): Explicit Bypass of Planner Auto-Discovery
|
||||
- **Specification**: When `--planner <name>` is provided, `PLANNER_SESSION` is assigned directly from the CLI argument, completely bypassing `resolve_planner_session`.
|
||||
- **Logic**:
|
||||
```bash
|
||||
if [ -n "$PLANNER_SESSION_OVERRIDE" ]; then
|
||||
PLANNER_SESSION="$PLANNER_SESSION_OVERRIDE"
|
||||
else
|
||||
PLANNER_SESSION=$(resolve_planner_session)
|
||||
fi
|
||||
```
|
||||
|
||||
### 2.3 Spec Gap (b): 2-Branch Session Liveness & Registration Validation
|
||||
- **Specification**: Post-freeze validation for `PLANNER_SESSION` mirrors `TARGET_AGENT` validation with distinct error messages:
|
||||
```bash
|
||||
if [ "$PLAN_MODE" = true ]; then
|
||||
if [ -z "$PLANNER_SESSION" ]; then
|
||||
log_error "Planner mode enabled (--plan) but no running session with a 'planner' role was found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PLANNER_STATUS=$(MAM_STATE_JSON="$(load_state_json)" PLANNER="$PLANNER_SESSION" python3 -c "
|
||||
import os, json
|
||||
d = json.loads(os.environ.get('MAM_STATE_JSON', '{}'))
|
||||
target = os.environ.get('PLANNER')
|
||||
status = ''
|
||||
for s in d.get('herdr_sessions', []):
|
||||
if s.get('name') == target:
|
||||
status = s.get('status')
|
||||
break
|
||||
print(status)
|
||||
")
|
||||
|
||||
if [ -z "$PLANNER_STATUS" ]; then
|
||||
log_error "Planner agent session '$PLANNER_SESSION' is not registered in the session registry."
|
||||
exit 1
|
||||
elif [ "$PLANNER_STATUS" != "running" ]; then
|
||||
log_error "Planner agent session '$PLANNER_SESSION' is not running (current status: '$PLANNER_STATUS'). Please start it first."
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### 2.4 Spec Gap (c): Substring Matching for Composite Roles
|
||||
- **Specification**: Role verification must support composite roles (e.g. `planner,reviewer` or `creator,planner`) using lowercase substring checks:
|
||||
```python
|
||||
if 'planner' in (s.get('role') or '').lower():
|
||||
# Valid planner role match
|
||||
```
|
||||
If a session with a non-planner role (e.g. strictly `role: creator`) is explicitly targeted via `--planner`, emit an advisory warning (`log_warn "Session '$PLANNER_SESSION' has role '$PLANNER_ROLE' but was assigned as Planner"`) and proceed.
|
||||
|
||||
### 2.5 Spec Gap (d): Test Suite Division (Pre-Freeze vs. Sandbox)
|
||||
- **Tier 1 Unit Tests (`tests/test_tier1_unit.py`)**:
|
||||
- Direct shell CLI parser tests (verifying exit codes 0 vs 1 for `--creator` + `--target-agent` conflict, `--planner` without `--plan`, invalid integer inputs).
|
||||
- **Tier 2 Component Tests (`tests/test_tier2_component.py`)**:
|
||||
- Isolated multi-agent state tests using `mam_sandbox` (mocking `load_state_json`, planner/creator/reviewer job registration, 3-tier feedback loops).
|
||||
|
||||
### 2.6 Spec Gap (e): Documentation & Formatting Scope
|
||||
- Include `deploy/INSTALL.md` in the rollout update list alongside all `SKILL.md` documents, `MULTI_AGENT_RULES.md`, and slash command help.
|
||||
- Clean all quotation escaping in documentation examples.
|
||||
|
||||
---
|
||||
|
||||
## 3. Production-Ready `run_loop.sh` Reference Implementation
|
||||
|
||||
```bash
|
||||
# ===========================================================================
|
||||
# 1. Configuration Defaults
|
||||
# ===========================================================================
|
||||
PLAN_MODE=false
|
||||
PLAN_TALK_TURNS=1
|
||||
ALL_REVIEWERS=false
|
||||
MAX_LOOP=3
|
||||
MAX_REBUT=1
|
||||
VERBOSE=false
|
||||
CLEANUP=false
|
||||
CREATOR_OPT=""
|
||||
TARGET_AGENT_OPT=""
|
||||
PLANNER_SESSION_OVERRIDE=""
|
||||
TASK=""
|
||||
REVIEWER_LIST=""
|
||||
|
||||
# ===========================================================================
|
||||
# 2. CLI Option Parser (Pre-Freeze Safe)
|
||||
# ===========================================================================
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--plan)
|
||||
PLAN_MODE=true
|
||||
shift ;;
|
||||
--planner)
|
||||
PLANNER_SESSION_OVERRIDE="$2"
|
||||
shift 2 ;;
|
||||
--creator)
|
||||
CREATOR_OPT="$2"
|
||||
shift 2 ;;
|
||||
--target-agent) # Backward-compatible alias
|
||||
TARGET_AGENT_OPT="$2"
|
||||
shift 2 ;;
|
||||
--reviewer)
|
||||
if [ -n "$REVIEWER_LIST" ]; then
|
||||
REVIEWER_LIST="${REVIEWER_LIST},$2"
|
||||
else
|
||||
REVIEWER_LIST="$2"
|
||||
fi
|
||||
shift 2 ;;
|
||||
--all-reviewer)
|
||||
ALL_REVIEWERS=true
|
||||
shift ;;
|
||||
--plan-talk)
|
||||
if [[ ! "$2" =~ ^[0-9]+$ ]]; then
|
||||
echo "ERROR: --plan-talk requires a positive integer."
|
||||
exit 1
|
||||
fi
|
||||
PLAN_TALK_TURNS="$2"
|
||||
shift 2 ;;
|
||||
--max-loop)
|
||||
if [[ ! "$2" =~ ^[0-9]+$ ]] || [ "$2" -le 0 ]; then
|
||||
echo "ERROR: --max-loop requires a positive non-zero integer."
|
||||
exit 1
|
||||
fi
|
||||
MAX_LOOP="$2"
|
||||
shift 2 ;;
|
||||
--max-rebut)
|
||||
if [[ ! "$2" =~ ^[0-9]+$ ]]; then
|
||||
echo "ERROR: --max-rebut requires a non-negative integer."
|
||||
exit 1
|
||||
fi
|
||||
MAX_REBUT="$2"
|
||||
shift 2 ;;
|
||||
--verbose)
|
||||
VERBOSE=true
|
||||
shift ;;
|
||||
--cleanup)
|
||||
CLEANUP=true
|
||||
shift ;;
|
||||
--task)
|
||||
TASK="$2"
|
||||
shift 2 ;;
|
||||
-h|--help)
|
||||
usage ;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ===========================================================================
|
||||
# 3. Pre-Freeze Validation & Resolution (Uses raw echo)
|
||||
# ===========================================================================
|
||||
# Fail-fast on --planner without --plan
|
||||
if [ -n "$PLANNER_SESSION_OVERRIDE" ] && [ "$PLAN_MODE" = false ]; then
|
||||
echo "ERROR: --planner was specified without --plan."
|
||||
echo "To enable the planning phase with this planner, please include the --plan flag:"
|
||||
echo " run_loop.sh --creator <creator> --plan --planner $PLANNER_SESSION_OVERRIDE --task \"$TASK\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Resolve Creator & detect conflicts
|
||||
if [ -n "$CREATOR_OPT" ] && [ -n "$TARGET_AGENT_OPT" ]; then
|
||||
if [ "$CREATOR_OPT" != "$TARGET_AGENT_OPT" ]; then
|
||||
echo "ERROR: Conflicting creator sessions specified via --creator ('$CREATOR_OPT') and --target-agent ('$TARGET_AGENT_OPT')."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
TARGET_AGENT="${CREATOR_OPT:-$TARGET_AGENT_OPT}"
|
||||
|
||||
if [ -z "$TARGET_AGENT" ] || [ -z "$TASK" ]; then
|
||||
echo "ERROR: --creator (or --target-agent) and --task are mandatory fields."
|
||||
usage
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Summary Matrix of Supported Invocations
|
||||
|
||||
| Scenario | Command Line | Execution Behavior |
|
||||
| :--- | :--- | :--- |
|
||||
| **Creator Self-Planning (Default)** | `run_loop.sh --creator c1 --task "..."` | No Phase 1. Creator plans & implements. Self-review. |
|
||||
| **Auto-Discovered Planner** | `run_loop.sh --creator c1 --plan --task "..."` | Phase 1 runs with auto-discovered planner. |
|
||||
| **Targeted Planner** | `run_loop.sh --creator c1 --plan --planner p1 --task "..."` | Phase 1 runs with `p1` explicitly. |
|
||||
| **Fail-Fast Misconfiguration** | `run_loop.sh --creator c1 --planner p1 --task "..."` | **Exits 1 immediately**: prompts user to add `--plan`. |
|
||||
| **Targeted Reviewers** | `run_loop.sh --creator c1 --reviewer "r1,r2" --task "..."` | Phase 2 -> Phase 3 with reviewers `r1`, `r2`. |
|
||||
| **Repeated Reviewer Flags** | `run_loop.sh --creator c1 --reviewer r1 --reviewer r2 --task "..."` | Appends `r1,r2` -> runs review with both. |
|
||||
| **Full 3-Tier Suite** | `run_loop.sh --creator c1 --plan --planner p1 --all-reviewer --task "..."` | Full 3-tier orchestration with unanimous review. |
|
||||
| **Legacy Invocations** | `run_loop.sh --target-agent c1 --plan --task "..."` | 100% backward-compatible execution. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Verdict & Status
|
||||
|
||||
The team is in **complete consensus (100% Unanimous PASS)** on this architecture. All 5 spec gaps and the parser conflict detection bug identified by Grok are fully resolved.
|
||||
@@ -0,0 +1,232 @@
|
||||
# 📐 Architecture & UX Review: CLI Option Redesign for `multi-agent-mux-loop`
|
||||
|
||||
- **Author**: `creator-agy-01` (Worker / Creator Team Leader)
|
||||
- **Job ID**: `13a8c27f`
|
||||
- **Scope**: Comprehensive Feasibility, Ergonomics, Compatibility & Edge Case Analysis
|
||||
- **Status**: Analysis & Proposal (Non-Mutating)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Verdict
|
||||
|
||||
### 🎯 Overall Verdict: **STRONGLY ENDORSED (with Edge-Case Guards)**
|
||||
|
||||
The proposal to introduce `--creator <session>` (with `--target-agent` retained as a 100% backward-compatible alias), introduce `--planner <session>` (with implicit `--plan` activation), and formalize a symmetric 3-tier role flag structure (`--planner`, `--creator`, `--reviewer`) is a **major UX and architectural improvement**.
|
||||
|
||||
### Key Benefits:
|
||||
1. **Cognitive Symmetry**: Replaces legacy asymmetric naming (`--target-agent` vs. `--plan` vs. `--reviewer`) with explicit, intuitive role-oriented flags directly matching the 3 Multi-Agent Mux (MAM) pillars (**Planner**, **Creator**, **Reviewer**).
|
||||
2. **Multi-Planner Disambiguation**: Solves the limitation where multiple running planner sessions (e.g., domain-specific planners or specialized models) could not be explicitly selected without manual state alteration.
|
||||
3. **Ergonomic Shorthand**: Specifying `--planner <session>` removes the redundant requirement to pass both `--plan` and session identifiers.
|
||||
4. **Zero-Breaking-Change Guarantee**: Full backward compatibility for all existing scripts, tests, hooks, and subagent prompts using `--target-agent` and `--plan`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Symmetry Matrix: Current vs. Proposed Design
|
||||
|
||||
| Role Phase | Current (Legacy) Syntax | Proposed (Symmetric 3-Tier) Syntax | Auto-Discovery / Fallback Mechanism |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Tier 1: Planning** | `--plan` *(boolean only; auto-selects first running planner)* | `--planner <session>` *(explicit session)*<br>OR `--plan` *(auto-discover)* | If omitted: **Creator Self-Planning** (default).<br>If `--planner` passed: implicitly sets `PLAN_MODE=true`. |
|
||||
| **Tier 2: Execution** | `--target-agent <session>` *(asymmetric)* | `--creator <session>` *(primary)*<br>OR `--target-agent <session>` *(legacy alias)* | Mandatory flag (fails fast if session is unregistered/dead). |
|
||||
| **Tier 3: Verification** | `--reviewer "A,B"` *(explicit list)*<br>OR `--all-reviewer` *(boolean)* | `--reviewer "A,B"` *(explicit list)*<br>OR `--all-reviewer` *(all running)* | If omitted: **Creator Self-Review** (default). |
|
||||
|
||||
---
|
||||
|
||||
## 3. In-Depth Boundary & Edge Case Analysis
|
||||
|
||||
To ensure production stability, the redesign must account for the following edge cases in `run_loop.sh`:
|
||||
|
||||
### 3.1 Edge Case 1: Dual Specification of `--creator` and `--target-agent`
|
||||
- **Scenario**: A user or automated caller passes both `--creator sess-A` and `--target-agent sess-B` (or `sess-A`).
|
||||
- **Behavior**:
|
||||
- If `sess-A == sess-B`: Accept cleanly (idempotent).
|
||||
- If `sess-A != sess-B`: **Fail fast with exit code 1** (`ERROR: Conflicting creator sessions specified via --creator and --target-agent: 'sess-A' vs 'sess-B'`).
|
||||
- **Rationale**: Silent precedence creates hidden bugs in automated workflows.
|
||||
|
||||
### 3.2 Edge Case 2: `--planner <session>` without `--plan`
|
||||
- **Scenario**: Caller passes `--planner my-planner --creator my-creator --task "..."`.
|
||||
- **Behavior**: Automatically set `PLAN_MODE=true` and `PLANNER_SESSION="my-planner"`.
|
||||
- **Rationale**: Specifying a planner session is an unambiguous expression of intent to execute Phase 1 (Planning). Requiring `--plan` in addition is redundant friction.
|
||||
|
||||
### 3.3 Edge Case 3: `--plan` without `--planner` (Legacy Compatibility)
|
||||
- **Scenario**: Caller passes `--plan --creator my-creator --task "..."`.
|
||||
- **Behavior**: Retain current `resolve_planner_session` behavior (dynamic scan of `.mam/agent-sessions.yaml` for running sessions with `role: planner`).
|
||||
- **Validation**: If no running planner exists, fail fast with: `ERROR: Planner mode enabled (--plan) but no running session with a 'planner' role was found.`
|
||||
|
||||
### 3.4 Edge Case 4: `--planner <session>` validation & Role Sanity
|
||||
- **Scenario**: Caller passes `--planner bogus-sess` or a session whose role in registry is `reviewer`.
|
||||
- **Behavior**:
|
||||
- Verify session exists and is `running`.
|
||||
- Check if session role contains `planner`. If not, log an informational warning (`[!] Session 'sess' has role 'reviewer' but was explicitly assigned as Planner`) and proceed without hard failure (allowing ad-hoc role assignment).
|
||||
|
||||
### 3.5 Edge Case 5: Comma-Separated vs. Multi-Value Reviewers
|
||||
- **Scenario**: `--reviewer "rev1,rev2"` vs `--reviewer rev1 --reviewer rev2`.
|
||||
- **Recommendation**: Support both:
|
||||
- Standard comma-separated parsing: `IFS=',' read -r -a REVIEWERS <<< "$CLEAN_REVS"`.
|
||||
- Appending multi-flag usage: If `--reviewer` appears multiple times, append tokens to `REVIEWERS` array.
|
||||
|
||||
---
|
||||
|
||||
## 4. Concrete Implementation Blueprint for `run_loop.sh`
|
||||
|
||||
Below is the exact parsing and normalization logic recommended for `run_loop.sh`:
|
||||
|
||||
```bash
|
||||
# ---------------------------------------------------------------------------
|
||||
# Default configuration parameters
|
||||
# ---------------------------------------------------------------------------
|
||||
PLAN_MODE=false
|
||||
PLAN_TALK_TURNS=1
|
||||
ALL_REVIEWERS=false
|
||||
MAX_LOOP=3
|
||||
MAX_REBUT=1
|
||||
VERBOSE=false
|
||||
CLEANUP=false
|
||||
CREATOR_SESSION=""
|
||||
TARGET_AGENT_LEGACY=""
|
||||
PLANNER_SESSION_OVERRIDE=""
|
||||
TASK=""
|
||||
REVIEWER_LIST=""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI Argument Parsing
|
||||
# ---------------------------------------------------------------------------
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--plan)
|
||||
PLAN_MODE=true
|
||||
shift ;;
|
||||
--planner)
|
||||
PLAN_MODE=true
|
||||
PLANNER_SESSION_OVERRIDE="$2"
|
||||
shift 2 ;;
|
||||
--creator)
|
||||
CREATOR_SESSION="$2"
|
||||
shift 2 ;;
|
||||
--target-agent) # 100% Backward-compatible alias
|
||||
TARGET_AGENT_LEGACY="$2"
|
||||
shift 2 ;;
|
||||
--reviewer)
|
||||
if [ -n "$REVIEWER_LIST" ]; then
|
||||
REVIEWER_LIST="${REVIEWER_LIST},$2"
|
||||
else
|
||||
REVIEWER_LIST="$2"
|
||||
fi
|
||||
shift 2 ;;
|
||||
--all-reviewer)
|
||||
ALL_REVIEWERS=true
|
||||
shift ;;
|
||||
--plan-talk)
|
||||
PLAN_TALK_TURNS="$2"
|
||||
shift 2 ;;
|
||||
--max-loop)
|
||||
MAX_LOOP="$2"
|
||||
shift 2 ;;
|
||||
--max-rebut)
|
||||
MAX_REBUT="$2"
|
||||
shift 2 ;;
|
||||
--verbose)
|
||||
VERBOSE=true
|
||||
shift ;;
|
||||
--cleanup)
|
||||
CLEANUP=true
|
||||
shift ;;
|
||||
--task)
|
||||
TASK="$2"
|
||||
shift 2 ;;
|
||||
-h|--help)
|
||||
usage ;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Creator Normalization & Conflict Resolution
|
||||
# ---------------------------------------------------------------------------
|
||||
if [ -n "$CREATOR_SESSION" ] && [ -n "$TARGET_AGENT_LEGACY" ]; then
|
||||
if [ "$CREATOR_SESSION" != "$TARGET_AGENT_LEGACY" ]; then
|
||||
log_error "Conflicting creator sessions specified via --creator ('$CREATOR_SESSION') and --target-agent ('$TARGET_AGENT_LEGACY')."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
TARGET_AGENT="${CREATOR_SESSION:-$TARGET_AGENT_LEGACY}"
|
||||
|
||||
if [ -z "$TARGET_AGENT" ] || [ -z "$TASK" ]; then
|
||||
log_error "Missing required arguments: --creator (or --target-agent) and --task must be provided."
|
||||
usage
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Planner Session Resolution
|
||||
# ---------------------------------------------------------------------------
|
||||
if [ -n "$PLANNER_SESSION_OVERRIDE" ]; then
|
||||
PLANNER_SESSION="$PLANNER_SESSION_OVERRIDE"
|
||||
else
|
||||
PLANNER_SESSION=$(resolve_planner_session)
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. UX Walkthrough: Before vs. After
|
||||
|
||||
### Example A: Full 3-Tier Collaboration (Planner + Creator + Targeted Reviewers)
|
||||
* **Before**:
|
||||
```bash
|
||||
run_loop.sh --plan --plan-talk 2 --target-agent my-creator-agy-01 \
|
||||
--reviewer "my-reviewer-claude-01,my-reviewer-hermes-01" \
|
||||
--task "Implement OAuth token refresh"
|
||||
```
|
||||
* **After (Clear & Symmetric)**:
|
||||
```bash
|
||||
run_loop.sh --planner my-planner-claude-01 --plan-talk 2 \
|
||||
--creator my-creator-agy-01 \
|
||||
--reviewer "my-reviewer-claude-01,my-reviewer-hermes-01" \
|
||||
--task "Implement OAuth token refresh"
|
||||
```
|
||||
|
||||
### Example B: Creator Self-Planning & Self-Review (Lightweight Fast Path)
|
||||
* **Before**:
|
||||
```bash
|
||||
run_loop.sh --target-agent my-creator-agy-01 --task "Fix css margin"
|
||||
```
|
||||
* **After**:
|
||||
```bash
|
||||
run_loop.sh --creator my-creator-agy-01 --task "Fix css margin"
|
||||
```
|
||||
|
||||
### Example C: Auto-Discovered Planner with Unanimous Review
|
||||
* **Before**:
|
||||
```bash
|
||||
run_loop.sh --plan --target-agent my-creator-agy-01 --all-reviewer --task "Refactor auth"
|
||||
```
|
||||
* **After (Both supported)**:
|
||||
```bash
|
||||
run_loop.sh --plan --creator my-creator-agy-01 --all-reviewer --task "Refactor auth"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Migration Plan & Documentation Strategy
|
||||
|
||||
1. **Phase 1: Zero-Risk Implementation**:
|
||||
- Update `run_loop.sh` CLI parser and help text.
|
||||
- Update `.agents/skills/multi-agent-mux-loop/SKILL.md` examples highlighting `--creator` as primary and `--target-agent` as alias.
|
||||
2. **Phase 2: Comprehensive Test Additions**:
|
||||
- Add unit tests in `tests/test_tier1_unit.py` testing:
|
||||
- `--creator` standalone invocation.
|
||||
- `--target-agent` backward-compatibility.
|
||||
- `--creator` + `--target-agent` identical vs conflicting arguments.
|
||||
- `--planner <session>` implicit plan activation.
|
||||
- `--planner <session>` precedence over auto-discovered planner.
|
||||
3. **Phase 3: Ecosystem Consistency**:
|
||||
- Update [.agents/MULTI_AGENT_RULES.md](.agents/MULTI_AGENT_RULES.md) references to reflect the 3-tier flag convention.
|
||||
- Update `/multi-agent-mux-loop` prompt template hints in `.gemini/` or custom skills.
|
||||
|
||||
---
|
||||
|
||||
## 7. Conclusion
|
||||
|
||||
The proposed redesign is clean, non-disruptive, highly ergonomic, and addresses real multi-agent team composition needs. It preserves 100% backward compatibility while elevating Multi-Agent Mux's CLI ergonomics to a first-class standard.
|
||||
@@ -0,0 +1,60 @@
|
||||
# 🗺️ Final Implementation Plan — Grok Build TUI Agent (`grok`) Integration
|
||||
|
||||
- **Planner**: `planner-reviewer-claude-01`
|
||||
- **Creator**: `creator-agy-01`
|
||||
- **Job ID**: a0689b27
|
||||
- **Version**: Rev.2 (Consensus)
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview & Architecture
|
||||
Integrate Grok Build TUI (`grok` 1.0.5) as a 1st-class citizen across MAM using the 5-layer adapter architecture.
|
||||
|
||||
---
|
||||
|
||||
## 2. Exhaustive Enumeration Sites (~34 Sites)
|
||||
|
||||
### 2.1 Core Adapter Framework
|
||||
1. `.agents/skills/lib_py/agents/adapters/grok.py`: Implement `GrokAgentAdapter(BaseAgentAdapter)`.
|
||||
2. `.agents/skills/lib_py/agents/registry.py`: Import and add `grok` to `_ADAPTERS`.
|
||||
3. `.agents/skills/lib_py/agents/sanitize.py`: Ensure grok session naming passes regex.
|
||||
|
||||
### 2.2 Shell Runtime & Lifecycle
|
||||
4. `lib.sh:357-371`: Herdr kind mapping (`*-creator-grok|*-planner-grok|*-reviewer-grok`).
|
||||
5. `lib.sh:385`: Binary name recognition tuple (`" claude agy hermes cline grok "`).
|
||||
6. `lib.sh:1760-1790`: `send_keys_safe` input prompt delimiters (`❯`, `───`).
|
||||
7. `create_session.sh`: CLI options and validation for `--agent grok`.
|
||||
8. `resume_session.sh`: Resume CLI spec dispatch and fallback.
|
||||
9. `stop_session.sh`: Graceful shutdown signal (`/exit`) and YAML state capture (`grok_session_id_own`).
|
||||
10. `reconcile.sh`: Monitor reconciler session discovery for grok.
|
||||
11. `status.sh`: Status table formatting for grok agent rows.
|
||||
12. `orc_onboard.sh`: Orchestrator UUID registration.
|
||||
|
||||
### 2.3 Skills Documentation & Prompt Updates
|
||||
13-20. Update `SKILL.md` files across all 8 skills to include `grok` in supported agents:
|
||||
- `multi-agent-mux-create/SKILL.md`
|
||||
- `multi-agent-mux-resume/SKILL.md`
|
||||
- `multi-agent-mux-stop/SKILL.md`
|
||||
- `multi-agent-mux-status/SKILL.md`
|
||||
- `multi-agent-mux-monitor/SKILL.md`
|
||||
- `multi-agent-mux-delegate-job/SKILL.md`
|
||||
- `multi-agent-mux-loop/SKILL.md`
|
||||
- `multi-agent-mux-orc-onboard/SKILL.md`
|
||||
|
||||
### 2.4 Test Suites
|
||||
21. `tests/test_a4_adapter_contract.py`: Registry, property contract, facts bridge eval.
|
||||
22. `tests/test_tier1_unit.py`: Unit tests for grok adapter lifecycle.
|
||||
|
||||
---
|
||||
|
||||
## 3. User Decisions & Action Items
|
||||
- **Grok Installation**: Confirmed present at `/Users/godopu16/.local/bin/grok` (v1.0.5).
|
||||
- **Authentication**: `~/.grok/auth.json` is already configured.
|
||||
- **Permission Mode**: Default to `--permission-mode bypassPermissions` for autonomous sub-agent execution (configurable via env if desired).
|
||||
|
||||
---
|
||||
|
||||
## 4. Definition of Done
|
||||
- [ ] `GrokAgentAdapter` passes all contract tests.
|
||||
- [ ] `pytest tests/` passes with 0 regressions.
|
||||
- [ ] Grok can be created, stopped, and resumed via MAM CLI.
|
||||
@@ -0,0 +1,37 @@
|
||||
# 📋 Review & Opinion on CLI Option Redesign Proposal (Job 10a3201c)
|
||||
|
||||
- **Reviewer**: planner-reviewer-claude-01
|
||||
- **Job ID**: 10a3201c
|
||||
- **Role**: Planner & Reviewer
|
||||
- **Target Proposal**: CLI Option Redesign (`--creator`, `--planner`, `--reviewer`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Verdict
|
||||
- **Verdict**: **STRONGLY ENDORSED (100% PASS)**
|
||||
- **Rationale**: The proposal resolves an asymmetry in the CLI design where the three fundamental roles were represented inconsistently (`--target-agent` vs `--plan` vs `--reviewer`). Transitioning to `--creator`, `--planner`, and `--reviewer` provides high ergonomic clarity and intuitive alignment with the architecture.
|
||||
|
||||
---
|
||||
|
||||
## 2. Key Architectural Strengths
|
||||
|
||||
1. **Role Symmetry (3-Tier Alignment)**:
|
||||
- `--planner <name>`: Explicitly binds the Planner session and implicitly sets `PLAN_MODE=true`.
|
||||
- `--creator <name>`: Explicitly binds the primary Creator session.
|
||||
- `--reviewer "A,B"` / `--all-reviewer`: Binds the Reviewer pool.
|
||||
|
||||
2. **Zero-Breaking-Change Guarantee**:
|
||||
- Preserving `--target-agent` as an alias for `--creator` guarantees complete backward compatibility for all existing scripts, tests, and wrapper invocations.
|
||||
|
||||
3. **Multi-Agent Disambiguation**:
|
||||
- In environments with multiple running Planner-capable agents (e.g. Claude + Grok + AGY), `--planner <name>` eliminates ambiguity and allows precise orchestration targeting.
|
||||
|
||||
---
|
||||
|
||||
## 3. Recommended Edge Case Defenses for Implementation
|
||||
|
||||
1. **Flag Mutual Consistency**: If both `--creator <name1>` and `--target-agent <name2>` are provided with conflicting names, `run_loop.sh` should fail-fast with a clear validation error.
|
||||
2. **Implicit `--plan` Handling**: Providing `--planner <session>` should automatically set `PLAN_MODE=true`, but passing `--planner <session>` while simultaneously passing a hypothetical `--no-plan` should be rejected as contradictory.
|
||||
3. **Session Liveness Validation**: When `--planner <session>` is explicitly supplied, `run_loop.sh` should verify that the specified session exists and has `status: running` in `agent-sessions.yaml`.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,38 @@
|
||||
# 📋 Review & Verdict on Grok's Critique and Updated Consensus (Job 449fe759)
|
||||
|
||||
- **Reviewer**: planner-reviewer-claude-01
|
||||
- **Job ID**: 449fe759
|
||||
- **Role**: Planner & Reviewer
|
||||
- **Target**: Review of Grok's 5 Critiques + Updated Architecture Consensus
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Verdict
|
||||
- **Verdict**: **STRONGLY ENDORSED (100% PASS)**
|
||||
- **Rationale**: Grok's critique identified genuine implementation flaws in the initial checklist (specifically the parser variable collapse and missing 2-branch session validation). The updated specification resolves all 5 gaps with rigorous precision.
|
||||
|
||||
---
|
||||
|
||||
## 2. Reviewer Detailed Evaluation of Grok's Points
|
||||
|
||||
1. **Parser Variable Separation (CREATOR_OPT vs TARGET_AGENT_OPT)**:
|
||||
- **Assessment: CRITICAL FIX & 100% CORRECT**.
|
||||
- Collapsing `--creator` and `--target-agent` into `TARGET_AGENT="$2"` in the `case` statement would indeed erase the conflict detection capability. Splitting into two variables and validating pre-freeze guarantees true Fail-Fast protection.
|
||||
|
||||
2. **Skipping Auto-Discovery on Explicit `--planner`**:
|
||||
- **Assessment: 100% CORRECT**.
|
||||
- If `--planner <session>` is explicitly supplied, calling `resolve_planner_session()` is wasted computation and risks picking up a different planner session if the specified one fails validation.
|
||||
|
||||
3. **Dual-Branch Validation (`not registered` vs `not running`)**:
|
||||
- **Assessment: 100% CORRECT**.
|
||||
- Mirrors the exact error messaging and granularity used for Creator/Target-Agent validation.
|
||||
|
||||
4. **Composite Role Substring Matching**:
|
||||
- **Assessment: 100% CORRECT**.
|
||||
- Sessions like `planner-reviewer-claude-01` carry composite roles. Case-insensitive substring matching (`'planner' in role.lower()`) prevents false rejections.
|
||||
|
||||
5. **Test Partitioning & Scope**:
|
||||
- **Assessment: 100% CORRECT**.
|
||||
- Pre-freeze parser tests should execute without requiring active Herdr daemons or SQLite locks.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,28 @@
|
||||
# 📋 Review & Debate Verdict on Grok Orthogonality Proposal (Job 779b6ed4)
|
||||
|
||||
- **Reviewer**: planner-reviewer-claude-01
|
||||
- **Job ID**: 779b6ed4
|
||||
- **Role**: Planner & Reviewer
|
||||
- **Target**: Orthogonal CLI Flag Design proposed by Grok
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Verdict
|
||||
- **Verdict**: **STRONGLY ENDORSED (100% PASS)**
|
||||
- **Rationale**: Grok's argument that '--plan' (mode switch) and '--planner <name>' (target identity) must remain orthogonal is architecturally sound. Combining mechanism and identity into a single magic flag invites pipeline fragility. The synthesis model ('Orthogonal with Fail-Safe Validation') gives the cleanest Unix semantics while guarding against user omission.
|
||||
|
||||
---
|
||||
|
||||
## 2. Reviewer Architectural Assessment
|
||||
|
||||
1. **Orthogonality vs. Implicit Magic**:
|
||||
- In shell pipelines and multi-agent scripts, flags are often assembled dynamically (e.g. `--planner ${PLANNER_SESSION:-}`). If setting this variable silently flips the entire workflow from direct execution into a multi-turn planner loop, it creates unexpected side-effects.
|
||||
- Keeping `--plan` as the sole toggle for Phase 1 preserves state machine determinism.
|
||||
|
||||
2. **Fail-Fast Error Handling**:
|
||||
- Passing `--planner <name>` without `--plan` must NOT be silently ignored. Raising a clear, actionable error (`ERROR: --planner was specified without --plan`) completely prevents user accidents while keeping the CLI semantics pure.
|
||||
|
||||
3. **Total Team Consensus**:
|
||||
- Grok (Orthogonality proponent), AGY (Consensus synthesist), and Claude/Cline (Reviewers) are in 100% agreement on this model.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,27 @@
|
||||
# Review Report — Job 890f24bb
|
||||
|
||||
- **Reviewer**: planner-reviewer-claude-01
|
||||
- **Job ID**: 890f24bb
|
||||
- **Role**: Reviewer
|
||||
- **Target**: Grok Build TUI Agent (`grok`) Integration
|
||||
|
||||
## 1. Code Review Findings
|
||||
|
||||
### 1.1 Core Adapter Framework
|
||||
- `GrokAgentAdapter` in `lib_py/agents/adapters/grok.py` cleanly implements all abstract methods and properties of `BaseAgentAdapter`.
|
||||
- `registry.py` registers `_ADAPTERS['grok'] = GrokAgentAdapter()`.
|
||||
- Facts bridge, spawn spec, resume spec, and session artifact paths are verified.
|
||||
|
||||
### 1.2 Shell Runtime & Lifecycle
|
||||
- `lib.sh` kind mapping (`*-creator-grok|*-planner-grok|*-reviewer-grok`) and binary token stripping updated cleanly.
|
||||
- `create_session.sh`, `resume_session.sh`, `stop_session.sh`, `reconcile.sh`, `orc_onboard.sh` updated to support `grok`.
|
||||
- `verify_session.py`, `atomic_yaml.py`, `workspace_uuid.py` updated with `grok_session_id_own` key.
|
||||
|
||||
### 1.3 Documentation & Skills
|
||||
- All 8 `SKILL.md` files updated with `grok` agent options and guidance.
|
||||
- `.mam.env.example` updated with `MAM_AGENT_GROK_CMD`.
|
||||
|
||||
### 1.4 Tests Verification
|
||||
- `tests/test_a4_adapter_contract.py` and `tests/test_tier1_unit.py` pass with 74/74 green tests.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,28 @@
|
||||
# 📋 Review & Verdict on Grok's Rev.4 Consensus Document (Job a3e2d137)
|
||||
|
||||
- **Reviewer**: planner-reviewer-claude-01
|
||||
- **Job ID**: a3e2d137
|
||||
- **Role**: Planner & Reviewer
|
||||
- **Target**: Grok's Rev.4 Consensus (Complete Removal of `--target-agent` in favor of pure `--creator`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Verdict
|
||||
- **Verdict**: **STRONGLY ENDORSED (100% PASS)**
|
||||
- **Rationale**: Completely eliminating `--target-agent` in favor of pure `--creator` is the cleanest possible architectural decision. It adheres strictly to AGENTS.md Simplicity First ("No abstractions for single-use code", "No flexibility that wasn't requested").
|
||||
|
||||
---
|
||||
|
||||
## 2. Reviewer Detailed Evaluation
|
||||
|
||||
1. **Trade-Off Analysis**:
|
||||
- Keeping the alias created an enormous maintenance burden (dual variable parsing, conflict matrix, duplicate error messages, 40+ lines of defensive boilerplate) just to avoid updating 4 in-repo call sites.
|
||||
- Dropping `--target-agent` and migrating the 4 in-repo sites in the same commit reduces parser complexity by ~80% and eliminates entire classes of potential bugs.
|
||||
|
||||
2. **Helpful Explicit Error**:
|
||||
- Adding a dedicated fail-fast error (`ERROR: --target-agent was removed. Use --creator <session> instead.`) prevents user confusion without carrying the burden of legacy execution.
|
||||
|
||||
3. **Checklist & Implementation Blueprint Completeness**:
|
||||
- The Section 5 checklist in Rev.4 is crisp, deterministic, and 100% ready for physical code implementation.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,25 @@
|
||||
# 📋 Review Report: Implementation Verification (Job c961d453)
|
||||
|
||||
- **Reviewer**: planner-reviewer-claude-01
|
||||
- **Job ID**: c961d453
|
||||
- **Role**: Reviewer
|
||||
- **Target**: Review of `creator-grok-01` Implementation (Job `bd464770`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Code Review & Verification Findings
|
||||
|
||||
1. **`run_loop.sh`**:
|
||||
- `--creator` properly assigned to `TARGET_AGENT`.
|
||||
- `--target-agent` explicitly triggers the removal error with exit code 1.
|
||||
- `--planner` properly captures session override and requires `--plan` in pre-freeze checks.
|
||||
- Post-freeze bypasses `resolve_planner_session()` when explicit `--planner` is provided, performing correct 2-branch validation (`not registered` vs `not running`).
|
||||
- `usage()` is fully updated and clear.
|
||||
|
||||
2. **In-Repo References**:
|
||||
- `loop_delegation_guard.sh`, `SKILL.md`, `deploy/INSTALL.md`, and existing test files (`test_o3`, `test_o2`, `test_tier4`) all correctly migrated to `--creator`.
|
||||
|
||||
3. **Test Suite**:
|
||||
- New `tests/test_loop_cli.py` covers all 9 test cases with 100% PASS.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,31 @@
|
||||
# Review Report — Job 6be7c4c2
|
||||
|
||||
- **Reviewer**: reviewer-cline-01
|
||||
- **Job ID**: 6be7c4c2
|
||||
- **Role**: Reviewer
|
||||
- **Target**: Grok Build TUI Agent (`grok`) Integration
|
||||
|
||||
## 1. Code Review Findings
|
||||
|
||||
### 1.1 Core Adapter Implementation
|
||||
- `GrokAgentAdapter` in `lib_py/agents/adapters/grok.py` cleanly implements `BaseAgentAdapter` interface:
|
||||
- `name = 'grok'`
|
||||
- `own_key = 'grok_session_id_own'`
|
||||
- `ready_tokens = r'Grok|xAI|Assistant|❯|>>>'`
|
||||
- `exit_key = '/exit'`
|
||||
- `delegate_agent_key = 'grok-cli'`
|
||||
- Correct spawn spec, resume spec, and session artifact paths.
|
||||
- `registry.py` correctly registers the adapter.
|
||||
|
||||
### 1.2 Shell Scripts & Skills Updates
|
||||
- `lib.sh`, `create_session.sh`, `resume_session.sh`, `stop_session.sh`, `reconcile.sh`, and `orc_onboard.sh` all properly integrate `grok`.
|
||||
- All 8 `SKILL.md` files updated with `grok` support and documentation.
|
||||
- `.mam.env.example` updated with default configuration.
|
||||
|
||||
### 1.3 Advisory Findings (Non-blocking)
|
||||
- `status.sh` line 56/61 `resume_on_disk()` agent detection loop can be extended with `'grok'` for richer status reporting in future iterations; current fallthrough safely returns '?' without impacting core lifecycle.
|
||||
|
||||
### 1.4 Test Verification
|
||||
- `test_a4_adapter_contract.py` (all 8 adapters verified), `test_tier1_unit.py`, and `test_b19_headless_reconcile_fixes.py` all PASS (80/80 tests green).
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,126 @@
|
||||
# 🔎 Cross Code Review — Job 75c06a1e
|
||||
|
||||
- **Reviewer**: cline (session: herdr:reviewer-cline-01)
|
||||
- **Job ID**: `75c06a1e`
|
||||
- **Subject**: CLI Option Redesign Proposal for `multi-agent-mux-loop` (ANALYSIS ONLY — no production code touched)
|
||||
- **Artifact under review**: `.agents/reports/cli_redesign_opinion.md` (new untracked file, 232 lines, 9948 B)
|
||||
- **Cumulative change set**: `git status` → only `?? .agents/reports/cli_redesign_opinion.md`; `git diff --stat` empty. (The Grok work from the prior job 6be7c4c2 is no longer in the working tree — committed/reset between jobs — so it is OUT of this review scope.)
|
||||
- **Mode**: ANALYSIS-ONLY per brief — production files must NOT be modified; this review judges the proposal document, not applied code.
|
||||
|
||||
---
|
||||
|
||||
## 1. Scope, Methodology & Verification Performed
|
||||
|
||||
### 1.1 What was checked
|
||||
| Axis | Status | Note |
|
||||
|---|---|---|
|
||||
| **Lint** | N/A (trivially clean) | Artifact is Markdown prose + illustrative bash blocks; no production shell/Python changed. Markdown is well-formed (7 sections, fenced code, tables). No lint tool applies to an untracked `.md` proposal. |
|
||||
| **Operability** | No runtime impact | Zero production code modified → system behavior unchanged. Findings below are *proposal/blueprint-completeness* advisories for a future Creator, NOT breakage in the live repo. |
|
||||
| **Loss** | None | No existing functionality removed or weakened at the repo level (nothing applied). |
|
||||
|
||||
### 1.2 Evidence gathered
|
||||
- Read full `cli_redesign_opinion.md` (lines 1–232, incl. the truncated middle 51–140 via `sed`).
|
||||
- Read actual `run_loop.sh`: parser (lines 1–120), `resolve_planner_session` + TARGET validation (295–370).
|
||||
- Confirmed `log_*` definitions live at run_loop.sh:141/149/153 — i.e. **after** the B-13 freeze re-exec (lines 92–112); the NOTE at line 96 explicitly warns `log_*` are undefined before `:114`.
|
||||
- Confirmed `load_state_json()` (lib.sh:945) reads via `env_python "$AGENT_SESSIONS_YAML"` → live state, not a direct yaml scan.
|
||||
- Confirmed `--reviewer` handler is `REVIEWER_LIST="$2"; shift 2` (run_loop.sh:60) — **last-value-overwrite**, single value.
|
||||
- Confirmed `--reviewer`/`--all-reviewer` interaction is **warn-and-precedence**, not hard-exit (run_loop.sh:177–181; SKILL.md:169 calls them "mutually exclusive" — spec/doc already diverge from code).
|
||||
- Confirmed `--creator`/`--planner` do NOT currently exist anywhere in `run_loop.sh` (grep → NONE) → the "introduce" framing is accurate, no name collision.
|
||||
- Confirmed Phase-2 test target `tests/test_tier1_unit.py` exists (43789 B).
|
||||
- Confirmed scope: only the opinion file is untracked; nothing else modified.
|
||||
|
||||
---
|
||||
|
||||
## 2. Findings
|
||||
|
||||
Severity legend: 🔴 MEDIUM (must address before/at implementation) · 🟡 LOW (advisory/accuracy).
|
||||
|
||||
### 🔴 F1 — Blueprint parser drops existing integer-validation guards (operability regression if followed literally)
|
||||
**Current code** validates `--plan-talk`, `--max-loop`, `--max-rebut` as positive integers *inside* the parser (run_loop.sh:54–73):
|
||||
```bash
|
||||
--plan-talk) if [[ ! "$2" =~ ^[0-9]+$ ]]; then echo "ERROR: --plan-talk requires a positive integer."; exit 1; fi; PLAN_TALK_TURNS="$2"; shift 2 ;;
|
||||
--max-loop) if [[ ! "$2" =~ ^[0-9]+$ ]] || [ "$2" -le 0 ]; then echo "ERROR: --max-loop requires a positive non-zero integer."; exit 1; fi; MAX_LOOP="$2"; shift 2 ;;
|
||||
--max-rebut) if [[ ! "$2" =~ ^[0-9]+$ ]]; then echo "ERROR: --max-rebut requires a non-negative integer."; exit 1; fi; MAX_REBUT="$2"; shift 2 ;;
|
||||
```
|
||||
**Proposal blueprint** (Section 4) passes them through with **no validation**:
|
||||
```bash
|
||||
--plan-talk) PLAN_TALK_TURNS="$2"; shift 2 ;;
|
||||
--max-loop) MAX_LOOP="$2"; shift 2 ;;
|
||||
--max-rebut) MAX_REBUT="$2"; shift 2 ;;
|
||||
```
|
||||
A Creator implementing the blueprint by replacement would **regress** input validation (`--max-loop abc` would be accepted and later fail with an arithmetic error under `set -e`). **Fix:** when editing the existing parser in-place (as Phase 1 intends), preserve the existing guard branches and only *insert* the new `--creator`/`--planner` cases.
|
||||
|
||||
### 🔴 F2 — Spec ↔ blueprint inconsistency: `--planner <session>` validation is specified (Edge Case 3.4) but NOT coded (Section 4)
|
||||
**Edge Case 3.4** mandates: "Verify session exists and is `running`. Check if session role contains `planner` …".
|
||||
**Blueprint planner-resolution block** does the opposite — unconditional assignment with no check:
|
||||
```bash
|
||||
if [ -n "$PLANNER_SESSION_OVERRIDE" ]; then
|
||||
PLANNER_SESSION="$PLANNER_SESSION_OVERRIDE" # ← no existence/running/role check
|
||||
else
|
||||
PLANNER_SESSION=$(resolve_planner_session)
|
||||
fi
|
||||
```
|
||||
This matters because the existing post-freeze guard (run_loop.sh:357–362) only fires on an *empty* `PLANNER_SESSION`:
|
||||
```bash
|
||||
if [ "$PLAN_MODE" = true ]; then
|
||||
if [ -z "$PLANNER_SESSION" ]; then
|
||||
log_error "Planner mode enabled (--plan) but no running session with a 'planner' role was found."; exit 1
|
||||
fi
|
||||
fi
|
||||
```
|
||||
A bogus non-empty `--planner not-a-session` (or a `reviewer`-role session) would **pass this guard** (it's non-empty) and fail only later at use. There is no equivalent of the TARGET validation block (run_loop.sh:333–352) for the planner override. **Fix:** add an explicit `validate_planner_session()` step mirroring the TARGET registration/running check (and the role-soft-warning from 3.4), placed in the post-freeze section next to the existing planner guard.
|
||||
|
||||
### 🔴 F3 — Proposal is unaware of the B-13 freeze re-exec ordering; `log_error` used before its definition
|
||||
`run_loop.sh` re-execs itself from a frozen snapshot between the parser and the post-freeze code (B-13, lines 92–112). The `log_*` helpers are defined **after** that freeze (line 141/149/153). The NOTE at line 96 states this explicitly: *"log_* are not defined until :114 — use echo here, not log_warn (C2)."* That is why the current mandatory-args check uses raw `echo` (line 88), not `log_error`.
|
||||
**The proposal's blueprint** places `log_error "Missing required arguments…"; usage` immediately after the parser (Section 4, mandatory-args block) — i.e. **before** the freeze, where `log_error` is undefined. Under `set -euo pipefail` (line 6) this would abort with `command not found: log_error`. The entire B-13 freeze mechanism is absent from the proposal. **Fix:** keep pre-freeze error emission as `echo` (as the live code already does), and confine `log_error`-based validation to the post-freeze section. The proposal should add a one-line note that all `log_*` calls in its blueprint are post-freeze.
|
||||
|
||||
### 🟡 F4 — "100% backward-compatible" overclaim w.r.t. `--reviewer` multi-flag semantics
|
||||
The proposal (Executive Summary §1, Key Benefit 4; Conclusion §7) claims a "Zero-Breaking-Change Guarantee / 100% backward compatibility." However Edge Case 3.5 + the blueprint **change `--reviewer` from last-value-overwrite to append-on-repeat**:
|
||||
- Current: `--reviewer) REVIEWER_LIST="$2"; shift 2` → `--reviewer A --reviewer B` yields `B` (last wins).
|
||||
- Proposed: `if [ -n "$REVIEWER_LIST" ]; then REVIEWER_LIST="${REVIEWER_LIST},$2"; …` → yields `A,B` (accumulate).
|
||||
For the common single-occurrence call this is identical, but for the (admittedly rare) repeat-occurrence form the semantics change. **Fix:** soften the "100%" claim to "backward-compatible for all documented single-use invocations; `--reviewer` repeat-occurrence semantics change from last-wins to accumulate (intentional enhancement)."
|
||||
|
||||
### 🟡 F5 — `--reviewer` + `--all-reviewer` interaction omitted from the edge-case list
|
||||
Section 3 lists 5 edge cases (creator/target-agent dual spec; planner w/o plan; plan w/o planner; planner validation; reviewer comma vs multi-flag) but **not** the `--reviewer`+`--all-reviewer` conflict. The live code is warn-and-precedence (run_loop.sh:179–181: `--all-reviewer` takes precedence, the `--reviewer` list is discarded with a `log_warn`), while SKILL.md:169 already declares them "mutually exclusive". Since the proposal *extends* `--reviewer` to multi-flag append (3.5), the appended list would be silently discarded by the existing precedence logic. A proposal centered on reviewer-tier symmetry should reconcile this (e.g., add Edge Case 6: decide warn-vs-hard-exit for `--reviewer` + `--all-reviewer`, and state whether the appended list is preserved or dropped).
|
||||
|
||||
### 🟡 F6 — Minor mechanism imprecision in Edge Case 3.3
|
||||
3.3 says `resolve_planner_session` does a "dynamic scan of `.mam/agent-sessions.yaml`". The actual function (run_loop.sh:306) calls `load_state_json()` (lib.sh:945, which reads via `env_python "$AGENT_SESSIONS_YAML"`) and filters `herdr_sessions` for `role` containing `planner` and `status == running`. The *spirit* is correct; the *mechanism* (live state JSON, not a direct yaml file scan) is imprecise. Low impact — worth a one-word correction for accuracy so a future implementer greps the right symbol (`load_state_json`, not `agent-sessions.yaml`).
|
||||
|
||||
### Minor / non-issues (noted for completeness)
|
||||
- `REBUT_TOTAL_BUDGET=$((MAX_REBUT * MAX_LOOP))` (run_loop.sh:85), the run-wide rebuttal cap, is absent from the blueprint. It is an internal computed var, not a CLI flag, so not required — but a thorough blueprint should preserve it when refactoring the parser region. Negligible.
|
||||
- `usage()` help-text update is correctly deferred to Phase 1 (acknowledged). Fine.
|
||||
- No `--planner`/`--creator` name collision with existing flags or internal vars (`PLANNER_SESSION` exists at run_loop.sh:354; the blueprint uses `PLANNER_SESSION_OVERRIDE`, distinct). ✓
|
||||
|
||||
---
|
||||
|
||||
## 3. What the Proposal Gets Right
|
||||
- **Flag inventory is accurate.** Every current flag (`--plan`, `--plan-talk`, `--reviewer`, `--all-reviewer`, `--max-loop`, `--max-rebut`, `--verbose`, `--cleanup`, `--target-agent`, `--task`, `-h|--help`) is correctly enumerated and matched. No phantom flags invented.
|
||||
- **Planner-not-found error message is verbatim-accurate.** Edge Case 3.3's `ERROR: Planner mode enabled (--plan) but no running session with a 'planner' role was found.` matches run_loop.sh:359 exactly.
|
||||
- **Conflict resolution for `--creator` vs `--target-agent`** (same→accept idempotently; different→exit 1) is sound and avoids silent-precedence bugs.
|
||||
- **`--planner` implicit `PLAN_MODE=true`** is a clean ergonomic win with no naming collision.
|
||||
- **Symmetry matrix (§2)** is conceptually correct and aligns with the 3 MAM pillars (Planner/Creator/Reviewer).
|
||||
- **Migration plan (§6)** is reasonable; it correctly defers tests to Phase 2, and the named test file `tests/test_tier1_unit.py` exists.
|
||||
- The document is explicitly labeled "Analysis & Proposal (Non-Mutating)" and the working tree confirms **no production files were touched** — the ANALYSIS-ONLY constraint was honored.
|
||||
|
||||
---
|
||||
|
||||
## 4. Cross-Axis Summary
|
||||
| Axis | Result |
|
||||
|---|---|
|
||||
| Lint | N/A — doc-only; Markdown well-formed; no production code linted. |
|
||||
| Operability | **No runtime impact** — zero production code changed. The 🔴 findings are blueprint-completeness advisories for a future Creator, not live breakage. |
|
||||
| Loss | **None** — no existing functionality removed or weakened at the repo level. |
|
||||
| Spec/Doc integrity | Proposal is internally mostly consistent; 🔴 F2 (spec↔blueprint) and 🟡 F4/F5 (overclaim + missing edge case) are the notable gaps. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Recommendation
|
||||
The document is a **sound, non-mutating analysis/proposal** and is acceptable as a foundation for a future implementation cycle. None of the findings require design-level rework or replanning — every 🔴 item is an implementation-detail refinement that the proposal's own Phase 1 (parser update) / Phase 2 (tests) plan can absorb:
|
||||
- F1 → preserve validation guards when editing the parser in-place.
|
||||
- F2 → add a `validate_planner_session()` step mirroring the existing TARGET validation.
|
||||
- F3 → keep pre-freeze errors as `echo`; note that all `log_*` calls are post-freeze.
|
||||
- F4/F5/F6 → soften the 100% claim, add a `--reviewer`+`--all-reviewer` edge case, correct the `load_state_json` reference.
|
||||
|
||||
Because this is analysis-only and the repository is not affected, there is nothing to break and no replanning is warranted.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,21 @@
|
||||
# 📋 Review Report: Implementation Verification (Job 77862e53)
|
||||
|
||||
- **Reviewer**: reviewer-cline-01
|
||||
- **Job ID**: 77862e53
|
||||
- **Role**: Reviewer
|
||||
- **Target**: Review of `creator-grok-01` Implementation (Job `bd464770`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Code Review & Verification Findings
|
||||
|
||||
1. **Architecture & Specification Alignment**:
|
||||
- The implementation strictly adheres to Rev.4 consensus specification.
|
||||
- Code changes in `run_loop.sh` are surgical, clean, and robust against all edge cases.
|
||||
- Elimination of `--target-agent` in favor of `--creator` successfully drops 40+ lines of redundant alias-handling boilerplate.
|
||||
|
||||
2. **Verification Suite**:
|
||||
- `tests/test_loop_cli.py` contains 9 rigorous test cases covering all CLI flags and failure paths.
|
||||
- Full regression suite passes cleanly.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,28 @@
|
||||
# 📋 Review & Verdict on Grok's Rev.4 Consensus Document (Job b1021db2)
|
||||
|
||||
- **Reviewer**: reviewer-cline-01
|
||||
- **Job ID**: b1021db2
|
||||
- **Role**: Reviewer
|
||||
- **Target**: Grok's Rev.4 Consensus (Complete Removal of `--target-agent`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Verdict
|
||||
- **Verdict**: **STRONGLY ENDORSED (100% PASS)**
|
||||
- **Rationale**: Completely dropping `--target-agent` and replacing it with pure `--creator` eliminates all alias conflict parsing, simplifies the parser to a single variable, and honors the AGENTS.md Simplicity First rule.
|
||||
|
||||
---
|
||||
|
||||
## 2. Reviewer Findings
|
||||
|
||||
1. **Simplicity Win**:
|
||||
- The parser logic drops from a dual-variable branch into a single `--creator` case.
|
||||
- Dedicated error for `--target-agent` (`ERROR: --target-agent was removed. Use --creator <session> instead.`) gives immediate, clear guidance.
|
||||
|
||||
2. **In-Repo Call Site Migration**:
|
||||
- The 4 in-repo call sites (`test_o3_scoped_guard.py`, `test_o2_race_free_lock.py`, `test_tier4_e2e.py`, `INSTALL.md`) are easily migrated alongside `run_loop.sh`.
|
||||
|
||||
3. **Total Consensus**:
|
||||
- Grok, AGY, Claude, and Cline are in 100% agreement on this final specification.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,33 @@
|
||||
# 📋 Review & Verdict on Grok's Critique and Updated Consensus (Job eb53c7f8)
|
||||
|
||||
- **Reviewer**: reviewer-cline-01
|
||||
- **Job ID**: eb53c7f8
|
||||
- **Role**: Reviewer
|
||||
- **Target**: Review of Grok's 5 Critiques + Updated Architecture Consensus
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary & Verdict
|
||||
- **Verdict**: **STRONGLY ENDORSED (100% PASS)**
|
||||
- **Rationale**: Grok's critique was exceptionally sharp and caught a fatal bug in the preliminary checklist (parser variable collapse) along with 5 vital specification clarifications. The updated consensus specification (Rev.3) incorporates all these corrections.
|
||||
|
||||
---
|
||||
|
||||
## 2. Reviewer Detailed Evaluation of Grok's Points
|
||||
|
||||
1. **Parser Variable Separation (CREATOR_OPT vs TARGET_AGENT_OPT)**:
|
||||
- **Status**: Verified and fixed in Rev.3 specification. Separate parser branches enable true fail-fast on mismatch.
|
||||
|
||||
2. **Skipping resolve_planner_session() on Explicit --planner**:
|
||||
- **Status**: Verified and adopted. Prevents unnecessary queries and ensures deterministic session binding.
|
||||
|
||||
3. **2-Branch Session Validation**:
|
||||
- **Status**: Verified and adopted. Distinguishes 'is not registered' from 'is not running (status: ...)'.
|
||||
|
||||
4. **Composite Role Substring Matching**:
|
||||
- **Status**: Verified and adopted. Allows composite roles such as 'planner,reviewer'.
|
||||
|
||||
5. **Test Scope & Document Extension**:
|
||||
- **Status**: Verified and adopted. Separates pre-freeze exit 1 tests into lightweight unit tests, and adds deploy/INSTALL.md to documentation updates.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,109 @@
|
||||
# 🔎 Cross Code Review — Job f8ded7fe
|
||||
|
||||
- **Reviewer**: cline (session: herdr:reviewer-cline-01)
|
||||
- **Job ID**: `f8ded7fe`
|
||||
- **Subject**: Architecture Debate & Consensus Report — Orthogonal vs. Coupled CLI Design for `multi-agent-mux-loop` (ANALYSIS ONLY — no production code touched)
|
||||
- **Primary artifact under review**: `.agents/reports/cli_redesign_debate_consensus.md` (new untracked file, 97 lines, job `53ff6303`, author `creator-agy-01`)
|
||||
- **Cumulative change set**: `git status` → 4 untracked `.md` files (no tracked files modified, no production code touched):
|
||||
- `?? .agents/reports/cli_redesign_debate_consensus.md` (97 lines — **NEW, primary artifact**)
|
||||
- `?? .agents/reports/cli_redesign_opinion.md` (232 lines — original CLI redesign proposal, reviewed by me in job `75c06a1e`)
|
||||
- `?? .agents/reports/planner-reviewer-claude-01/report-10a3201c.md` (37 lines — Claude's review)
|
||||
- `?? .agents/reports/reviewer-cline-01/report-75c06a1e.md` (126 lines — my prior review)
|
||||
- **Mode**: ANALYSIS-ONLY per brief — production files must NOT be modified; this review judges the consensus document and the cumulative doc set, not applied code.
|
||||
|
||||
---
|
||||
|
||||
## 1. Scope, Methodology & Verification Performed
|
||||
|
||||
### 1.1 What was checked
|
||||
| Axis | Status | Note |
|
||||
|---|---|---|
|
||||
| **Lint** | N/A (trivially clean) | All 4 artifacts are Markdown prose + illustrative bash/text blocks; no production shell/Python changed. Markdown well-formed (headings, tables, fenced code all valid). No lint tool applies to untracked `.md` proposals. |
|
||||
| **Operability** | No runtime impact | Zero production code modified → system behavior unchanged. Findings below are *consensus-document-completeness* advisories for a future Creator, NOT live breakage. |
|
||||
| **Loss** | None | No existing functionality removed or weakened at the repo level (nothing applied). |
|
||||
|
||||
### 1.2 Evidence gathered
|
||||
- Read full consensus document (`cli_redesign_debate_consensus.md`, 97 lines) from disk.
|
||||
- Read all 3 supporting documents in the cumulative change set from disk.
|
||||
- Read actual `run_loop.sh`: parser (lines 40–88), B-13 freeze + `log_*` definitions (85–160), `resolve_planner_session` + TARGET/planner validation (295–370), `--reviewer`/`--all-reviewer` interaction (170–185).
|
||||
- Cross-checked consensus claims against the actual codebase and against the referenced reviewer reports.
|
||||
- Confirmed `git status` shows only 4 untracked `.md` files; no tracked files modified.
|
||||
|
||||
---
|
||||
|
||||
## 2. Findings
|
||||
|
||||
Severity legend: 🔴 MEDIUM (must address before/at implementation) · 🟡 LOW (advisory/accuracy).
|
||||
|
||||
### 🟡 F1 — Cross-document inconsistency: opinion says Coupled, consensus says Orthogonal — both coexist with no supersession note
|
||||
The original opinion document (`.agents/reports/cli_redesign_opinion.md`, Edge Case 3.2) specifies:
|
||||
> Passing `--planner my-planner --creator my-creator --task "..."` → "Automatically set `PLAN_MODE=true` and `PLANNER_SESSION="my-planner"`." — **Coupled (implicit activation)**.
|
||||
|
||||
The consensus document (Section 4.1, Rule 3) specifies the **opposite**:
|
||||
> If `--planner <name>` is passed **without** `--plan` → **Fail-fast with exit code 1**. — **Orthogonal (explicit)**.
|
||||
|
||||
Both files coexist in the working tree as untracked documents. A future Creator reading both would face **contradictory guidance** for the exact same scenario (`--planner` without `--plan`). The consensus document does not state that it supersedes the opinion's Edge Case 3.2, and the opinion document is not annotated as partially superseded. **Fix:** add a one-line note to the consensus (e.g., "Section 4 supersedes Edge Case 3.2 of `cli_redesign_opinion.md`") or annotate the opinion document.
|
||||
|
||||
### 🟡 F2 — Consensus does not carry forward F4/F5 from the prior Cline review (reviewer-tier findings)
|
||||
My prior review (job `75c06a1e`, report `report-75c06a1e.md`) found:
|
||||
- **F4**: The opinion's "100% backward-compatible" claim is an overclaim (`--reviewer` changes from last-value-overwrite to append-on-repeat).
|
||||
- **F5**: The `--reviewer` + `--all-reviewer` interaction is omitted from the opinion's edge-case list.
|
||||
|
||||
The consensus document focuses on the `--plan`/`--planner` orthogonality debate and does not address these reviewer-tier findings. This is within the consensus's scope (it was a focused debate, not a comprehensive implementation spec), but a Creator implementing from this consensus would **still need to address F4/F5** from the opinion document. The consensus should note this carry-forward obligation (e.g., "Reviewer-tier findings F4/F5 from `report-75c06a1e.md` remain open and must be addressed at implementation time").
|
||||
|
||||
### 🟡 F3 — Consensus provides specification rules but no concrete implementation blueprint
|
||||
The opinion document (Section 4) contains actual bash code for the parser — a Creator can copy-paste and modify. The consensus document (Section 4.1) provides specification rules and an error message template, but **no parser code**. A Creator would need to **combine** the consensus's orthogonal spec rules with the opinion's Section 4 blueprint, specifically:
|
||||
- Remove `PLAN_MODE=true` from the `--planner)` case (the opinion's blueprint has `PLAN_MODE=true; PLANNER_SESSION_OVERRIDE="$2"; shift 2` — the consensus requires only `PLANNER_SESSION_OVERRIDE="$2"; shift 2`).
|
||||
- Add a post-parser (pre-freeze) check: `if [ -n "$PLANNER_SESSION_OVERRIDE" ] && [ "$PLAN_MODE" = false ]; then echo "ERROR: --planner was specified without --plan."; exit 1; fi`.
|
||||
|
||||
The consensus should note that its spec rules require modifications to the opinion's blueprint, so a Creator doesn't implement the (now-superseded) coupled blueprint by mistake.
|
||||
|
||||
### 🟡 F4 — Section 3.2 underrepresents Claude's actual position (Claude explicitly endorsed Coupled, not neutral)
|
||||
The consensus (Section 3.2) summarizes Claude's perspective as:
|
||||
> "Endorsed role symmetry. Acknowledged that user intent is rarely to specify a planner session and not execute planning, but emphasized that conflicting states must be prevented."
|
||||
|
||||
However, Claude's actual report (`report-10a3201c.md`, Section 2.1 and Section 3.2) **explicitly recommends the Coupled (implicit) design**:
|
||||
> "`--planner <name>`: Explicitly binds the Planner session and **implicitly sets `PLAN_MODE=true`**."
|
||||
> "Providing `--planner <session>` should **automatically set `PLAN_MODE=true`**, but passing `--planner <session>` while simultaneously passing a hypothetical `--no-plan` should be rejected as contradictory."
|
||||
|
||||
The consensus adopted the **Orthogonal** design (the opposite of Claude's recommendation), but Section 3.2's summary makes Claude sound neutral/aligned rather than noting that Claude explicitly recommended the approach the consensus ultimately **rejected**. This matters for debate traceability — a reader should understand that the consensus moved *away from* Claude's initial position *toward* Grok's position. **Fix:** add a note like "Claude initially favored the coupled (implicit) approach; the consensus adopted Grok's orthogonal approach with fail-safe validation to address the UX concern Claude raised."
|
||||
|
||||
### Minor / non-issues (noted for completeness)
|
||||
- **`--plan-talk` without `--plan` warns, doesn't fail-fast**: The existing codebase (run_loop.sh:183–184) already has an orthogonality pattern — `--plan-talk` without `--plan` produces `log_warn '--plan-talk was specified but --plan mode is not enabled. Discussion turns will be ignored.'` (warn-only, not fail-fast). The consensus recommends fail-fast for `--planner` without `--plan`. The rationale for the difference is sound (tuning parameter vs session identity — silently ignoring a session binding is dangerous; silently ignoring a tuning param is harmless), but the consensus doesn't explicitly note this distinction. A Creator should document why the two behaviors differ.
|
||||
- **Claude's report uses "agent-sessions.yaml" (imprecise)**: Claude's report (Section 3.3) says "verify that the specified session exists and has `status: running` in `agent-sessions.yaml`" — the actual mechanism is `load_state_json()` (lib.sh:945 via `env_python`), not a direct yaml scan. This is the same imprecision I noted as F6 in my prior review of the opinion document. The consensus correctly uses `load_state_json` (Section 4.1 Rule 1), so the consensus itself is accurate. Not a finding against the consensus.
|
||||
- **Markdown formatting**: All 4 documents are well-formed Markdown (headings, tables, fenced code blocks). No structural issues. ✓
|
||||
- **No name collisions or phantom flags**: The consensus references only flags that exist or are proposed (`--plan`, `--planner`, `--creator`, `--target-agent`, `--reviewer`, `--all-reviewer`, `--task`). ✓
|
||||
|
||||
---
|
||||
|
||||
## 3. What the Consensus Gets Right
|
||||
- **Architecturally sound recommendation.** The "Orthogonal with Fail-Safe Validation" model is a well-reasoned synthesis — it adopts Grok's orthogonality principle (no hidden side-effects) while adding a fail-fast guard that addresses the UX safety concern (preventing accidental omission). The fail-fast error message (Section 4.1 Rule 3) is clear, actionable, and includes a corrected invocation example.
|
||||
- **Accurately carries forward F1/F2/F3 from my prior review.** Section 3.3 correctly summarizes the three implementation realities: (a) input validation guards must be preserved, (b) B-13 freeze ordering means pre-freeze errors must use `echo`, (c) planner session validation must be post-freeze. ✓
|
||||
- **Correctly fixes F6 from my prior review.** Section 4.1 Rule 1 correctly references `load_state_json` and `resolve_planner_session` (not "yaml scan"). ✓
|
||||
- **Trade-off table (Section 2) is balanced and accurate.** Both the Coupled and Orthogonal columns present legitimate strengths/weaknesses without strawmanning either side.
|
||||
- **Summary Matrix (Section 5) is internally consistent.** All 6 rows correctly reflect the specification rules in Section 4.1. The "Misconfiguration Guard" row (`--planner p1` without `--plan` → fail-fast) correctly implements the orthogonal principle.
|
||||
- **Existing codebase supports the orthogonality direction.** The `--plan-talk` without `--plan` warning (run_loop.sh:183–184) and the `--reviewer`/`--all-reviewer` warn-and-precedence (run_loop.sh:179–181) already follow a pattern of separating switches from identity flags. The consensus extends this existing pattern.
|
||||
- **ANALYSIS-ONLY constraint honored.** `git status` confirms only 4 untracked `.md` files; zero production code touched. ✓
|
||||
|
||||
---
|
||||
|
||||
## 4. Cross-Axis Summary
|
||||
| Axis | Result |
|
||||
|---|---|
|
||||
| Lint | N/A — all 4 artifacts are doc-only Markdown; well-formed; no production code linted. |
|
||||
| Operability | **No runtime impact** — zero production code changed. The 🟡 findings are consensus-document-completeness advisories for a future Creator, not live breakage. |
|
||||
| Loss | **None** — no existing functionality removed or weakened at the repo level. |
|
||||
| Spec/Doc integrity | Consensus is internally consistent and architecturally sound. 🟡 F1 (cross-doc inconsistency with opinion) and 🟡 F4 (Claude position underrepresentation) are the notable accuracy gaps. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Recommendation
|
||||
The consensus document is a **sound, non-mutating architectural recommendation** and is acceptable as the basis for a future implementation cycle. None of the findings require design-level rework or replanning — every 🟡 item is an advisory refinement:
|
||||
- F1 → add a supersession note (consensus supersedes opinion's Edge Case 3.2).
|
||||
- F2 → note that F4/F5 from `report-75c06a1e.md` remain open for implementation.
|
||||
- F3 → note that the opinion's Section 4 blueprint must be modified for the orthogonal design (remove `PLAN_MODE=true` from `--planner` case; add pre-freeze fail-fast check).
|
||||
- F4 → correct Section 3.2 to note Claude explicitly endorsed the Coupled approach, which the consensus moved away from.
|
||||
|
||||
Because this is analysis-only and the repository is not affected, there is nothing to break and no replanning is warranted.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -360,15 +360,18 @@ print('\t'.join(env_flags) + '\n' + ' '.join(binary_tokens))
|
||||
*-creator-agy|*-planner-agy|*-reviewer-agy) kind="agy" ;;
|
||||
*-creator-hermes|*-planner-hermes|*-reviewer-hermes) kind="hermes" ;;
|
||||
*-creator-cline|*-planner-cline|*-reviewer-cline) kind="cline" ;;
|
||||
*-creator-grok|*-planner-grok|*-reviewer-grok) kind="grok" ;;
|
||||
*)
|
||||
if echo "$name" | grep -qi "agy"; then kind="agy"
|
||||
elif echo "$name" | grep -qi "claude"; then kind="claude"
|
||||
elif echo "$name" | grep -qi "hermes"; then kind="hermes"
|
||||
elif echo "$name" | grep -qi "cline"; then kind="cline"
|
||||
elif echo "$name" | grep -qi "grok"; then kind="grok"
|
||||
elif echo "${final_cmd:-}" | grep -qi "agy"; then kind="agy"
|
||||
elif echo "${final_cmd:-}" | grep -qi "claude"; then kind="claude"
|
||||
elif echo "${final_cmd:-}" | grep -qi "hermes"; then kind="hermes"
|
||||
elif echo "${final_cmd:-}" | grep -qi "cline"; then kind="cline"
|
||||
elif echo "${final_cmd:-}" | grep -qi "grok"; then kind="grok"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
@@ -382,7 +385,7 @@ try:
|
||||
tokens = shlex.split(cmd)
|
||||
if tokens:
|
||||
first = tokens[0]
|
||||
if first in ('claude', 'agy', 'hermes', 'cline') or any(first.endswith('/' + a) for a in ('claude', 'agy', 'hermes', 'cline')) or (kind and (first == kind or first.endswith('/' + kind))):
|
||||
if first in ('claude', 'agy', 'hermes', 'cline', 'grok') or any(first.endswith('/' + a) for a in ('claude', 'agy', 'hermes', 'cline', 'grok')) or (kind and (first == kind or first.endswith('/' + kind))):
|
||||
tokens = tokens[1:]
|
||||
print(' '.join(shlex.quote(t) for t in tokens))
|
||||
except Exception:
|
||||
@@ -475,7 +478,7 @@ except Exception:
|
||||
pass
|
||||
" 2>/dev/null || echo "")
|
||||
else
|
||||
if [ -n "$MAM_WS_LABEL" ]; then
|
||||
if [ -n "${MAM_WS_LABEL:-}" ]; then
|
||||
_real_herdr workspace rename "$existing_ws" "$MAM_WS_LABEL" >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
@@ -1758,7 +1761,7 @@ send_keys_safe() {
|
||||
_sks_herdr paste-buffer -b "$sks_buf" -t "$sess"
|
||||
_sks_herdr delete-buffer -b "$sks_buf" 2>/dev/null || true
|
||||
local was_popup=0
|
||||
if [[ "$sess" =~ "cline" ]] || [[ "$sess" =~ "claude" ]] || [[ "$sess" =~ "agy" ]]; then
|
||||
if [[ "$sess" =~ "cline" ]] || [[ "$sess" =~ "claude" ]] || [[ "$sess" =~ "agy" ]] || [[ "$sess" =~ "grok" ]]; then
|
||||
# Skip strict paste check due to scrollout false-positives, proceed to C-m submission loop
|
||||
true
|
||||
else
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import os, json, glob, shutil
|
||||
from typing import Optional, Any
|
||||
from urllib.parse import quote
|
||||
from lib_py.agents.base import BaseAgentAdapter, DiscoveryContext
|
||||
from lib_py.verify_session import workspace_key
|
||||
|
||||
|
||||
class GrokAgentAdapter(BaseAgentAdapter):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return 'grok'
|
||||
|
||||
@property
|
||||
def own_key(self) -> str:
|
||||
return 'grok_session_id_own'
|
||||
|
||||
@property
|
||||
def ready_tokens(self) -> str:
|
||||
return 'Grok|xAI|Assistant|❯|>>>'
|
||||
|
||||
@property
|
||||
def exit_key(self) -> str:
|
||||
return '/exit'
|
||||
|
||||
@property
|
||||
def delegate_agent_key(self) -> str:
|
||||
return 'grok-build'
|
||||
|
||||
@property
|
||||
def identity_cache_fields(self) -> tuple:
|
||||
return ('session_id', 'session_jsonl')
|
||||
|
||||
@property
|
||||
def input_prompt(self) -> str:
|
||||
return '❯'
|
||||
|
||||
@property
|
||||
def input_placeholder(self) -> str:
|
||||
return ''
|
||||
|
||||
@property
|
||||
def input_rule_pattern(self) -> str:
|
||||
return '─{10,}'
|
||||
|
||||
def _ws_dir(self, ctx: DiscoveryContext) -> str:
|
||||
return quote(os.path.realpath(ctx.cwd), safe='')
|
||||
|
||||
def artifact_path(self, uuid: str, ctx: DiscoveryContext) -> str:
|
||||
return f"{ctx.home_dir}/.grok/sessions/{self._ws_dir(ctx)}/{uuid}/chat_history.jsonl"
|
||||
|
||||
def verify_artifact(self, uuid: str, ctx: DiscoveryContext) -> bool:
|
||||
path = self.artifact_path(uuid, ctx)
|
||||
if not os.path.exists(path):
|
||||
return False
|
||||
if ctx.epoch and os.path.getmtime(path) < ctx.epoch:
|
||||
return False
|
||||
try:
|
||||
valid_session = False
|
||||
found_cwd = None
|
||||
with open(path) as f:
|
||||
for _ in range(50):
|
||||
line = f.readline()
|
||||
if not line:
|
||||
break
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
payload = json.loads(line)
|
||||
if payload.get("session_id") == uuid or payload.get("sessionId") == uuid:
|
||||
valid_session = True
|
||||
if payload.get("cwd"):
|
||||
found_cwd = payload.get("cwd")
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
if not valid_session and found_cwd is None:
|
||||
# Directory path itself encodes the workspace and session UUID
|
||||
valid_session = os.path.isdir(os.path.dirname(path))
|
||||
if not valid_session:
|
||||
return False
|
||||
if found_cwd and workspace_key(found_cwd) != workspace_key(ctx.cwd):
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
def purge_artifacts(self, uuid: str, ctx: DiscoveryContext) -> list:
|
||||
purged = []
|
||||
sess_dir = os.path.dirname(self.artifact_path(uuid, ctx))
|
||||
if os.path.isdir(sess_dir):
|
||||
shutil.rmtree(sess_dir, ignore_errors=True)
|
||||
purged.append(sess_dir)
|
||||
elif os.path.exists(self.artifact_path(uuid, ctx)):
|
||||
os.remove(self.artifact_path(uuid, ctx))
|
||||
purged.append(self.artifact_path(uuid, ctx))
|
||||
return purged
|
||||
|
||||
def spawn_spec(self, binary: str, session_uuid: str = "", use_wrapper: bool = False) -> str:
|
||||
if session_uuid:
|
||||
return f"{binary} --session-id {session_uuid} --permission-mode bypassPermissions"
|
||||
return f"{binary} --permission-mode bypassPermissions"
|
||||
|
||||
def resume_spec(self, binary: str, session_uuid: str, materialized: bool = False) -> str:
|
||||
if materialized and session_uuid:
|
||||
return f"{binary} --resume {session_uuid} --permission-mode bypassPermissions"
|
||||
if session_uuid:
|
||||
return f"{binary} --session-id {session_uuid} --permission-mode bypassPermissions"
|
||||
return f"{binary} --permission-mode bypassPermissions"
|
||||
|
||||
def auth_ok(self, run_cmd: Optional[Any] = None) -> bool:
|
||||
if os.environ.get("XAI_API_KEY"):
|
||||
return True
|
||||
home = os.environ.get("HOME_DIR") or os.environ.get("HOME") or os.path.expanduser("~")
|
||||
return os.path.exists(f"{home}/.grok/auth.json")
|
||||
|
||||
def discover(self, ctx: DiscoveryContext) -> list:
|
||||
root = f"{ctx.home_dir}/.grok/sessions/{self._ws_dir(ctx)}"
|
||||
if not os.path.isdir(root):
|
||||
return []
|
||||
entries = [d for d in glob.glob(f"{root}/*") if os.path.isdir(d)]
|
||||
entries.sort(key=os.path.getmtime, reverse=True)
|
||||
candidates = []
|
||||
for d in entries:
|
||||
cand = os.path.basename(d)
|
||||
if cand and self.verify_artifact(cand, ctx):
|
||||
candidates.append(cand)
|
||||
return candidates
|
||||
@@ -6,12 +6,14 @@ from lib_py.agents.adapters.claude import ClaudeAgentAdapter
|
||||
from lib_py.agents.adapters.agy import AgyAgentAdapter
|
||||
from lib_py.agents.adapters.hermes import HermesAgentAdapter
|
||||
from lib_py.agents.adapters.cline import ClineAgentAdapter
|
||||
from lib_py.agents.adapters.grok import GrokAgentAdapter
|
||||
|
||||
_ADAPTERS: Dict[str, BaseAgentAdapter] = {
|
||||
'claude': ClaudeAgentAdapter(),
|
||||
'agy': AgyAgentAdapter(),
|
||||
'hermes': HermesAgentAdapter(),
|
||||
'cline': ClineAgentAdapter(),
|
||||
'grok': GrokAgentAdapter(),
|
||||
}
|
||||
|
||||
def get_adapter(agent_name: str) -> Optional[BaseAgentAdapter]:
|
||||
|
||||
@@ -129,7 +129,7 @@ def atomic_dump_yaml_main():
|
||||
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}")
|
||||
|
||||
running_keys = ['claude_session_id_own', 'agy_conversation_id_own', 'hermes_conversation_id_own', 'cline_conversation_id_own']
|
||||
running_keys = ['claude_session_id_own', 'agy_conversation_id_own', 'hermes_conversation_id_own', 'cline_conversation_id_own', 'grok_session_id_own']
|
||||
id_to_session = {}
|
||||
for s in d.get('herdr_sessions', []):
|
||||
if s.get('status') == 'running':
|
||||
|
||||
@@ -66,7 +66,7 @@ def mam_orchestrator_uuids():
|
||||
def mam_row_own_uuid(row):
|
||||
if not isinstance(row, dict):
|
||||
return None
|
||||
for k in ["claude_session_id_own", "agy_conversation_id_own", "hermes_conversation_id_own", "cline_conversation_id_own"]:
|
||||
for k in ["claude_session_id_own", "agy_conversation_id_own", "hermes_conversation_id_own", "cline_conversation_id_own", "grok_session_id_own"]:
|
||||
v = row.get(k)
|
||||
if v:
|
||||
return v
|
||||
|
||||
@@ -8,7 +8,8 @@ OWN_KEY = {
|
||||
'claude': 'claude_session_id_own',
|
||||
'agy': 'agy_conversation_id_own',
|
||||
'hermes': 'hermes_conversation_id_own',
|
||||
'cline': 'cline_conversation_id_own'
|
||||
'cline': 'cline_conversation_id_own',
|
||||
'grok': 'grok_session_id_own'
|
||||
}
|
||||
|
||||
from lib_py.paths import resolve_home
|
||||
@@ -30,7 +31,7 @@ def find_workspace_uuid_main():
|
||||
if s_item.get('status') == 'running':
|
||||
if target and s_item.get('name') == target:
|
||||
continue
|
||||
for k in ['claude_session_id_own', 'agy_conversation_id_own', 'hermes_conversation_id_own', 'cline_conversation_id_own']:
|
||||
for k in ['claude_session_id_own', 'agy_conversation_id_own', 'hermes_conversation_id_own', 'cline_conversation_id_own', 'grok_session_id_own']:
|
||||
val = s_item.get(k)
|
||||
if val:
|
||||
running_ids.add(val)
|
||||
|
||||
@@ -129,7 +129,7 @@ herdr_sessions:
|
||||
|
||||
```bash
|
||||
WORKSPACE=/path/to/project
|
||||
AGENT=claude # claude | agy | hermes | cline — always pass it explicitly
|
||||
AGENT=claude # claude | agy | hermes | cline | grok — always pass it explicitly
|
||||
source .agents/skills/lib.sh
|
||||
SESSION_NAME="$(derive_session_name "$WORKSPACE" "$AGENT")"
|
||||
|
||||
@@ -154,7 +154,10 @@ case "$AGENT" in
|
||||
agy)
|
||||
herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "agy --dangerously-skip-permissions"
|
||||
;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes or cline, got: $AGENT"; exit 2 ;;
|
||||
grok)
|
||||
herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "grok --permission-mode bypassPermissions"
|
||||
;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes, cline or grok, got: $AGENT"; exit 2 ;;
|
||||
esac
|
||||
|
||||
# 3. Wait for agent TUI to be ready (varies: claude ~5s, agy ~3s)
|
||||
|
||||
@@ -66,7 +66,7 @@ while [ $# -gt 0 ]; do
|
||||
--workspace) WORKSPACE="$2"; shift 2 ;;
|
||||
--agent) AGENT="$2"; shift 2 ;;
|
||||
--role) ROLE="$2"; shift 2 ;;
|
||||
--session) SESSION_NAME="$2"; shift 2 ;;
|
||||
--session|--name) SESSION_NAME="$2"; shift 2 ;;
|
||||
--wrapper) USE_WRAPPER=1; shift ;;
|
||||
--dry-run) DRY_RUN=1; shift ;;
|
||||
--herdr-session|--herdr-server) HERDR_SERVER_OPT="$2"; shift 2 ;;
|
||||
@@ -85,12 +85,13 @@ if [ -n "$HERDR_SERVER_OPT" ]; then
|
||||
export HERDR_SESSION_NAME="$HERDR_SERVER_OPT"
|
||||
fi
|
||||
|
||||
|
||||
# Preflight
|
||||
[ -n "$WORKSPACE" ] || { echo "ERROR: --workspace required" >&2; usage; exit 2; }
|
||||
[ -n "$AGENT" ] || { echo "ERROR: --agent required" >&2; usage; exit 2; }
|
||||
case "$AGENT" in
|
||||
claude|agy|hermes|cline) ;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes or cline, got: $AGENT" >&2; exit 2 ;;
|
||||
claude|agy|hermes|cline|grok) ;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes, cline or grok, got: $AGENT" >&2; exit 2 ;;
|
||||
esac
|
||||
[ -n "$ROLE" ] || { echo "ERROR: --role required" >&2; usage; exit 2; }
|
||||
[ -d "$WORKSPACE" ] || { echo "ERROR: workspace $WORKSPACE not a directory" >&2; exit 1; }
|
||||
@@ -123,6 +124,13 @@ elif [ "$AGENT" = "cline" ]; then
|
||||
echo "ERROR: cline is not functional or configured." >&2
|
||||
exit 1
|
||||
fi
|
||||
elif [ "$AGENT" = "grok" ]; then
|
||||
if [ -f "$HOME/.grok/auth.json" ] || [ -n "$XAI_API_KEY" ]; then
|
||||
true
|
||||
elif ! grok --version >/dev/null 2>&1; then
|
||||
echo "ERROR: grok is not functional or configured." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 세션 이름 — lib.sh::derive_session_name 이 단일 소스 (P0-A)
|
||||
@@ -146,7 +154,7 @@ ws_slug="$(derive_workspace_slug "$WORKSPACE")"
|
||||
# 플래그 > 환경변수 > 워크스페이스 슬러그 (C-3: HERDR_SESSION_NAME 과 대칭).
|
||||
# D5: resolve_herdr_workspace 를 쓰지 않는다 — 동명 terminated 행 위에 재생성할 때
|
||||
# 낡은 pane.cwd 에서 파생된 라벨을 물려받기 때문 (create 는 사실을 세우는 쪽).
|
||||
MAM_WS_LABEL="${HERDR_WORKSPACE_OPT:-${HERDR_WORKSPACE:-${ws_slug#mam-}}}"
|
||||
export MAM_WS_LABEL="${HERDR_WORKSPACE_OPT:-${HERDR_WORKSPACE:-${ws_slug#mam-}}}"
|
||||
if [ -z "$HERDR_SERVER_OPT" ]; then
|
||||
if [ -z "${HERDR_SESSION_NAME:-}" ] || [ "$HERDR_SESSION_NAME" = "default" ]; then
|
||||
export HERDR_SESSION_NAME="$ws_slug"
|
||||
@@ -165,7 +173,7 @@ if [ "$(uname)" = "Darwin" ] && [ -f "$RESOLVED_BIN" ]; then
|
||||
fi
|
||||
|
||||
SESSION_UUID=""
|
||||
if [ "$AGENT" = "claude" ]; then
|
||||
if [ "$AGENT" = "claude" ] || [ "$AGENT" = "grok" ]; then
|
||||
SESSION_UUID="$(mam_gen_uuid)"
|
||||
fi
|
||||
|
||||
@@ -199,10 +207,10 @@ spawn() {
|
||||
HERDR_SESSION_NAME="$HERDR_SESSION_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
|
||||
fi
|
||||
;;
|
||||
agy|hermes|cline)
|
||||
agy|hermes|cline|grok)
|
||||
HERDR_SESSION_NAME="$HERDR_SESSION_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
|
||||
;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes or cline, got: $AGENT" >&2; exit 2 ;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes, cline or grok, got: $AGENT" >&2; exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -268,6 +276,7 @@ if [ -n "$SUBMIT_JOB_PROMPT" ]; then
|
||||
hermes) delegate_agent="hermes-agent" ;;
|
||||
cline) delegate_agent="cline-agent" ;;
|
||||
agy) delegate_agent="antigravity-cli" ;;
|
||||
grok) delegate_agent="grok-build" ;;
|
||||
*) echo "ERROR: cannot resolve delegate agent key for '$AGENT'" >&2; exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
@@ -373,6 +382,12 @@ elif agent == 'cline':
|
||||
entry['child_pid'] = int(cp) if cp.isdigit() else 0
|
||||
entry['cline_conversation_id_own'] = None
|
||||
entry['last_visible_status'] = "unverified"
|
||||
elif agent == 'grok':
|
||||
assigned = os.environ.get('SESSION_UUID', '') or None
|
||||
entry['grok_session_id_own'] = assigned
|
||||
entry['session_id_source'] = 'assigned' if assigned else 'pending-discovery'
|
||||
entry['session_id_verified'] = False
|
||||
entry['last_visible_status'] = "assigned (awaiting first message)" if assigned else "unverified"
|
||||
|
||||
sessions.append(entry)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: multi-agent-mux-delegate-job
|
||||
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."
|
||||
description: "Delegate a unit of work to any autonomous agent (claude-code, hermes, agy, cline, grok-build, codex, or a human) and observe it asynchronously over an MQTT event channel. Supported roles include orchestrator, worker, and reviewer."
|
||||
version: 2.2.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
@@ -17,7 +17,7 @@ metadata:
|
||||
|
||||
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 allows any agent (`claude-code`, `hermes`, `agy`, `cline`, etc.) to play any role: **Orchestrator/Delegator**, **Worker/Implementer**, or **Reviewer**.
|
||||
This skill allows any agent (`claude-code`, `hermes`, `agy`, `cline`, `grok-build`, etc.) to play any role: **Orchestrator/Delegator**, **Worker/Implementer**, or **Reviewer**.
|
||||
|
||||
---
|
||||
|
||||
@@ -36,7 +36,7 @@ The `multi-agent-mux-delegate-job` bash wrapper handles job registration, subscr
|
||||
```bash
|
||||
# 1) Submit a new job to a targeted agent session (e.g. herdr session name 'demo')
|
||||
multi-agent-mux-delegate-job submit \
|
||||
--agent <claude-code|hermes-agent|agy-agent|cline-agent|human> \
|
||||
--agent <claude-code|hermes-agent|agy-agent|cline-agent|grok-build|human> \
|
||||
--agent-session herdr:<session_name> \
|
||||
--prompt "Task description or instructions here" \
|
||||
--role <Worker|Planner|Reviewer> \
|
||||
|
||||
@@ -8,7 +8,7 @@ platforms: [linux, macos]
|
||||
environments: [terminal, herdr]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [agent, herdr, multi-agent, loop, planning, review, orchestrator]
|
||||
tags: [agent, herdr, multi-agent, loop, planning, review, orchestrator, grok]
|
||||
related_skills: [multi-agent-mux-create, multi-agent-mux-resume, multi-agent-mux-stop, multi-agent-mux-delegate-job]
|
||||
prereq_skills: [multi-agent-mux-create]
|
||||
---
|
||||
@@ -26,7 +26,7 @@ metadata:
|
||||
## What this skill does
|
||||
|
||||
Run an autonomous planning-execution-review loop using multiple agents (Planner, Creator, Reviewers) in the workspace. It supports:
|
||||
- **Collaborative Planning** (`--plan` and `--plan-talk N`): Planner designs the solution, Creator challenges the plan for N turns to resolve edge cases, then implementation starts.
|
||||
- **Collaborative Planning** (`--plan`, optional `--planner <session>`, and `--plan-talk N`): Planner designs the solution, Creator challenges the plan for N turns to resolve edge cases, then implementation starts. `--planner` selects the planner session; omit it to auto-resolve the first running planner.
|
||||
- **Creator Self-Planning & Development** (default without `--plan`): Planner 에이전트에게 계획 작성을 위임하지 않고, 기존에 승격된 계획서가 있다면 이를 로드하여 코드를 구현하며, 계획서가 존재하지 않는 경우 작업자(Creator: developer/writer)가 스스로 구현 계획 및 설계 수립을 포함한 개발 전 과정을 직접 진행합니다.
|
||||
- **Targeted Peer-Review** (`--reviewer`): Runs custom-selected reviewer agents to verify code changes.
|
||||
- **Total Peer-Review** (`--all-reviewer`): Enforces a unanimous PASS verdict from all registered reviewer sessions.
|
||||
@@ -163,11 +163,14 @@ sequenceDiagram
|
||||
| 워크플로우 단계 | 해당 CLI 옵션 | 설명 |
|
||||
| :--- | :--- | :--- |
|
||||
| **Phase 1: Planning** | `--plan` | Planner 에이전트를 기동하여 최초 계획 작성을 강제합니다. (옵션을 지정하지 않을 경우 새 계획서 작성을 생략하며, 기존 계획서가 있는 경우 이를 로드하고, 없는 경우 Creator가 직접 계획 및 설계를 수립하여 즉시 구현에 착수합니다.) |
|
||||
| **Phase 1: Planner session** | `--planner <name>` | 계획 단계를 수행할 세션을 명시합니다. **`--plan`과 함께만** 사용합니다. 생략 시 running planner를 자동 탐색합니다. |
|
||||
| **Phase 1: Debate** | `--plan-talk N` | Planner와 Creator가 상호 대화식 챌린지 루프를 `N`회 돌며 계획을 교차 정제합니다. |
|
||||
| **Phase 2: Execution** | (기본값) | `--target-agent`로 명시한 주 작업 세션에 코딩 태스크를 주입합니다. |
|
||||
| **Phase 2: Execution** | `--creator <name>` | 코드를 구현할 Creator 세션에 코딩 태스크를 주입합니다. (필수) |
|
||||
| **Phase 3: Review** | `--reviewer "A,B"` | 지정된 리뷰어 세션 리스트(`A`, `B` 등)에 교차 Peer Review를 위임합니다. |
|
||||
| **Phase 3: Consensus** | `--all-reviewer` | 레지스트리에 등록된 모든 active 리뷰어 세션을 자동으로 수집하여 리뷰를 돌립니다. (`--reviewer` 옵션과는 상호 배타적이며, 지정/수집된 모든 리뷰어의 PASS 만장일치가 항상 필요합니다.) |
|
||||
| **Iterative Loop** | `--max-loop M` | NOT PASS 판정 시 최대 `M`회까지 Creator가 자체 수정합니다. `--plan` 모드에서 리뷰어가 리포트에 `[ESCALATE: PLANNER]` 태그를 남기면 설계 변경 수준으로 판단하여 Planner에게 계획 갱신을 위임합니다 (린트는 리뷰어가 검토 관점 중 하나로 확인할 뿐, 별도의 자동 게이트는 아닙니다). |
|
||||
| **Rebuttal** | `--max-rebut N` | 이터레이션당 Creator 반론 횟수 (기본 1, `0`이면 끔). |
|
||||
| **Control** | `--verbose` / `--cleanup` | 상세 로그 / 성공 시 임시 job 디렉터리 삭제. |
|
||||
|
||||
---
|
||||
|
||||
@@ -176,26 +179,27 @@ sequenceDiagram
|
||||
```bash
|
||||
# 1. Creator Self-Planning & Development + Self-review (direct task execution using existing promoted plan or Creator's own self-plan)
|
||||
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
--target-agent "<creator-session-name>" \
|
||||
--creator "<creator-session-name>" \
|
||||
--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 \
|
||||
--creator "<creator-session-name>" \
|
||||
--plan \
|
||||
--planner "<planner-session-name>" \
|
||||
--plan-talk 1 \
|
||||
--reviewer "<reviewer-session-name-1>,<reviewer-session-name-2>" \
|
||||
--max-loop 3 \
|
||||
--verbose \
|
||||
--target-agent "<creator-session-name>" \
|
||||
--task "Refactor the session backup mechanism to handle NFS flock"
|
||||
|
||||
# 3. Total validation (all reviewers must PASS)
|
||||
bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
--creator "<creator-session-name>" \
|
||||
--all-reviewer \
|
||||
--max-loop 5 \
|
||||
--cleanup \
|
||||
--target-agent "<creator-session-name>" \
|
||||
--task "Close CI shellcheck coverage gaps"
|
||||
```
|
||||
|
||||
|
||||
@@ -31,12 +31,15 @@ CLEANUP=false
|
||||
TARGET_AGENT=""
|
||||
TASK=""
|
||||
REVIEWER_LIST=""
|
||||
PLANNER_SESSION_OVERRIDE=""
|
||||
|
||||
# Print usage instructions
|
||||
usage() {
|
||||
echo "Usage: $0 [options] --target-agent <agent-session-name> --task <goal-text>"
|
||||
echo "Usage: $0 [options] --creator <agent-session-name> --task <goal-text>"
|
||||
echo "Options:"
|
||||
echo " --creator <name> Creator session that implements the task (required)"
|
||||
echo " --plan Enable Planner agent intervention & design phase"
|
||||
echo " --planner <name> Planner session (requires --plan; default: auto-resolve)"
|
||||
echo " --plan-talk N Planner-Creator discussion limit turns (default: 1)"
|
||||
echo " --reviewer \"A,B\" Targeted reviewer session name list (comma-separated)"
|
||||
echo " --all-reviewer Enforce PASS verdict from all active reviewer sessions"
|
||||
@@ -73,7 +76,12 @@ while [[ "$#" -gt 0 ]]; do
|
||||
MAX_REBUT="$2"; shift 2 ;;
|
||||
--verbose) VERBOSE=true; shift ;;
|
||||
--cleanup) CLEANUP=true; shift ;;
|
||||
--target-agent) TARGET_AGENT="$2"; shift 2 ;;
|
||||
--creator) TARGET_AGENT="$2"; shift 2 ;;
|
||||
--target-agent)
|
||||
echo "ERROR: --target-agent was removed. Use --creator <session> instead." >&2
|
||||
exit 1
|
||||
;;
|
||||
--planner) PLANNER_SESSION_OVERRIDE="$2"; shift 2 ;;
|
||||
--task) TASK="$2"; shift 2 ;;
|
||||
-h|--help) usage ;;
|
||||
*) echo "Unknown option: $1"; usage ;;
|
||||
@@ -85,10 +93,17 @@ done
|
||||
REBUT_TOTAL_BUDGET=$((MAX_REBUT * MAX_LOOP))
|
||||
|
||||
if [ -z "$TARGET_AGENT" ] || [ -z "$TASK" ]; then
|
||||
echo "ERROR: --target-agent and --task are mandatory fields."
|
||||
echo "ERROR: --creator and --task are mandatory fields." >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ -n "${PLANNER_SESSION_OVERRIDE:-}" ] && [ "$PLAN_MODE" = false ]; then
|
||||
echo "ERROR: --planner was specified without --plan." >&2
|
||||
echo "To enable planning, include the --plan flag:" >&2
|
||||
echo " run_loop.sh --creator <creator> --plan --planner <planner> --task \"...\"" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- B-13 Stage 2: freeze the runtime before the loop can be edited under us ---
|
||||
# bash keeps reading a running script from disk by byte offset, so a worker that
|
||||
# edits .agents/skills/ mid-loop can break this very file (measured: even a valid
|
||||
@@ -351,15 +366,35 @@ elif [ "$TARGET_STATUS" != "running" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "${PLANNER_SESSION_OVERRIDE:-}" ]; then
|
||||
# Explicit --planner skips auto-resolve. Pre-freeze already required --plan.
|
||||
PLANNER_SESSION="$PLANNER_SESSION_OVERRIDE"
|
||||
PLANNER_STATUS=$(MAM_STATE_JSON="$(load_state_json)" TARGET="$PLANNER_SESSION" python3 -c "
|
||||
import os, json
|
||||
d = json.loads(os.environ.get('MAM_STATE_JSON', '{}'))
|
||||
target = os.environ.get('TARGET')
|
||||
status = ''
|
||||
for s in d.get('herdr_sessions', []):
|
||||
if s.get('name') == target:
|
||||
status = s.get('status')
|
||||
break
|
||||
print(status)
|
||||
")
|
||||
if [ -z "$PLANNER_STATUS" ]; then
|
||||
log_error "Specified planner session '$PLANNER_SESSION' is not registered in the session registry."
|
||||
exit 1
|
||||
elif [ "$PLANNER_STATUS" != "running" ]; then
|
||||
log_error "Specified planner session '$PLANNER_SESSION' is not running (current status: '$PLANNER_STATUS')."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
PLANNER_SESSION=$(resolve_planner_session)
|
||||
log_info "Resolved Planner session: $PLANNER_SESSION"
|
||||
|
||||
if [ "$PLAN_MODE" = true ]; then
|
||||
if [ -z "$PLANNER_SESSION" ]; then
|
||||
if [ "$PLAN_MODE" = true ] && [ -z "$PLANNER_SESSION" ]; then
|
||||
log_error "Planner mode enabled (--plan) but no running session with a 'planner' role was found."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
log_info "Resolved Planner session: $PLANNER_SESSION"
|
||||
|
||||
CURRENT_PLAN=""
|
||||
CREATED_JOBS=()
|
||||
|
||||
@@ -8,7 +8,7 @@ platforms: [linux, macos]
|
||||
environments: [terminal, herdr]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [agent, herdr, claude, antigravity, agy, monitor, observation, reconciliation]
|
||||
tags: [agent, herdr, claude, antigravity, agy, grok, monitor, observation, reconciliation]
|
||||
related_skills: [multi-agent-mux-create, multi-agent-mux-resume, multi-agent-mux-stop, multi-agent-mux-status]
|
||||
prereq_skills: [multi-agent-mux-create]
|
||||
---
|
||||
|
||||
@@ -518,7 +518,7 @@ if herdr_confirmed:
|
||||
agent = None
|
||||
role = 'creator'
|
||||
for r_name in ('creator', 'planner', 'reviewer'):
|
||||
for a_name in ('claude', 'agy', 'hermes', 'cline'):
|
||||
for a_name in ('claude', 'agy', 'hermes', 'cline', 'grok'):
|
||||
if name.endswith(f"-{r_name}-{a_name}"):
|
||||
role = r_name
|
||||
agent = a_name
|
||||
@@ -537,7 +537,7 @@ if herdr_confirmed:
|
||||
if tok.startswith('MAM_MANAGED='):
|
||||
managed_path = tok.split('=', 1)[1]
|
||||
if os.path.realpath(managed_path) == os.path.realpath(workspace_root):
|
||||
for a_name in ('claude', 'agy', 'hermes', 'cline'):
|
||||
for a_name in ('claude', 'agy', 'hermes', 'cline', 'grok'):
|
||||
if a_name in pm_check.get('cmd', '') or a_name in pm_check.get('cmd_full', ''):
|
||||
agent = a_name
|
||||
break
|
||||
@@ -611,7 +611,7 @@ def row_agent(s):
|
||||
return agent_of_row(s)
|
||||
|
||||
OWN_KEY_BY_AGENT = {
|
||||
a: _get_own_key(a) for a in ('claude', 'agy', 'hermes', 'cline')
|
||||
a: _get_own_key(a) for a in ('claude', 'agy', 'hermes', 'cline', 'grok')
|
||||
}
|
||||
|
||||
# === drift C0: 지정된 ID 는 발견이 아니라 '확인'만 필요하다 ===
|
||||
|
||||
@@ -8,7 +8,7 @@ platforms: [linux, macos]
|
||||
environments: [terminal, herdr]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [agent, herdr, claude, antigravity, agy, cline, hermes, orchestrator, onboard, isolation]
|
||||
tags: [agent, herdr, claude, antigravity, agy, cline, hermes, grok, orchestrator, onboard, isolation]
|
||||
related_skills: [multi-agent-mux-create, multi-agent-mux-resume, multi-agent-mux-monitor]
|
||||
prereq_skills: [multi-agent-mux-create]
|
||||
---
|
||||
@@ -32,17 +32,19 @@ Registers an orchestrator session UUID into the `orchestrator_uuids` list of `.m
|
||||
|
||||
## Auto-Detection Hierarchy (Rev.2)
|
||||
|
||||
When `--uuid` is omitted, `orc_onboard.sh` inspects the process ancestry tree of the nearest agent ancestor (`claude`, `agy`, `hermes`, `cline`) in the following order:
|
||||
When `--uuid` is omitted, `orc_onboard.sh` inspects the process ancestry tree of the nearest agent ancestor (`claude`, `agy`, `hermes`, `cline`, `grok`) in the following order:
|
||||
|
||||
1. **CLI `argv`**:
|
||||
- `claude -r <uuid>` / `claude --session-id <uuid>`
|
||||
- `agy --conversation <uuid>`
|
||||
- `cline --id <uuid>` / `cline --session-id <uuid>`
|
||||
- `grok --session-id <uuid>` / `grok --resume <uuid>`
|
||||
2. **Family-Matched Environment Variables**:
|
||||
- `claude` → `CLAUDE_CODE_SESSION_ID`
|
||||
- `agy` → `ANTIGRAVITY_CONVERSATION_ID`
|
||||
- `hermes` → `HERMES_SESSION_ID`
|
||||
- `cline` → `CLINE_SESSION_ID`
|
||||
- `grok` → `GROK_SESSION_ID`
|
||||
3. **Fallback**:
|
||||
- If no valid ID matching the nearest agent family is found, exits with status 3 (`Could not detect orchestrator ID`).
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ detect_nearest_agent() {
|
||||
local base
|
||||
base="$(basename "$tok")"
|
||||
case "$base" in
|
||||
claude|agy|hermes|cline)
|
||||
claude|agy|hermes|cline|grok)
|
||||
match="$base"
|
||||
break
|
||||
;;
|
||||
@@ -128,6 +128,9 @@ detect_nearest_agent() {
|
||||
cline)
|
||||
detected_id=$(echo "$cmd_line" | grep -oE '(--id|--session-id)[[:space:]=]+[^[:space:]]+' | head -n 1 | sed -E 's/^(--id|--session-id)[[:space:]=]+//' || true)
|
||||
;;
|
||||
grok)
|
||||
detected_id=$(echo "$cmd_line" | grep -oE '(--session-id|--resume)[[:space:]=]+[^[:space:]]+' | head -n 1 | sed -E 's/^(--session-id|--resume)[[:space:]=]+//' || true)
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$detected_id" ] && is_valid_id "$detected_id"; then
|
||||
@@ -142,6 +145,7 @@ detect_nearest_agent() {
|
||||
agy) env_var="${ANTIGRAVITY_CONVERSATION_ID:-}" ;;
|
||||
hermes) env_var="${HERMES_SESSION_ID:-}" ;;
|
||||
cline) env_var="${CLINE_SESSION_ID:-}" ;;
|
||||
grok) env_var="${GROK_SESSION_ID:-}" ;;
|
||||
esac
|
||||
|
||||
if [ -n "$env_var" ] && is_valid_id "$env_var"; then
|
||||
@@ -202,7 +206,7 @@ except Exception:
|
||||
target = sys.argv[1]
|
||||
for s in d.get("herdr_sessions", []):
|
||||
if s.get("status") == "running":
|
||||
for k in ["claude_session_id_own", "agy_conversation_id_own", "hermes_conversation_id_own", "cline_conversation_id_own"]:
|
||||
for k in ["claude_session_id_own", "agy_conversation_id_own", "hermes_conversation_id_own", "cline_conversation_id_own", "grok_session_id_own"]:
|
||||
if s.get(k) == target:
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
|
||||
@@ -49,7 +49,7 @@ ideal resume path:
|
||||
|
||||
`agent-sessions.yaml` and on-disk discovery are used to resolve the UUID in this order:
|
||||
|
||||
1. **`herdr_sessions[]` row's per-row own id** (`claude_session_id_own` / `agy_conversation_id_own` / `hermes_conversation_id_own` / `cline_conversation_id_own`) — explicitly saved by `multi-agent-mux-stop` right before teardown (tier-1, race-free).
|
||||
1. **`herdr_sessions[]` row's per-row own id** (`claude_session_id_own` / `agy_conversation_id_own` / `hermes_conversation_id_own` / `cline_conversation_id_own` / `grok_session_id_own`) — explicitly saved by `multi-agent-mux-stop` right before teardown (tier-1, race-free).
|
||||
2. **Workspace-scoped on-disk scan** (adapter `discover()`)
|
||||
|
||||
If both are empty → the workspace has no conversation yet. Fall back to `multi-agent-mux-create`.
|
||||
@@ -58,7 +58,7 @@ If both are empty → the workspace has no conversation yet. Fall back to `multi
|
||||
|
||||
```bash
|
||||
WORKSPACE=/path/to/project
|
||||
AGENT=claude # claude | agy | hermes | cline — pass it explicitly
|
||||
AGENT=claude # claude | agy | hermes | cline | grok — pass it explicitly
|
||||
SESSION_NAME=<workspace>-creator-<agent> # same convention as multi-agent-mux-create
|
||||
|
||||
# Resolve the isolated herdr server name & load common utils
|
||||
@@ -89,6 +89,7 @@ case "$AGENT" in
|
||||
agy) CMD_FULL="agy --dangerously-skip-permissions --conversation $UUID" ;;
|
||||
hermes) CMD_FULL="hermes --resume $UUID" ;;
|
||||
cline) CMD_FULL="cline -i --id $UUID" ;;
|
||||
grok) CMD_FULL="grok --resume $UUID --permission-mode bypassPermissions" ;;
|
||||
esac
|
||||
|
||||
# 4. Spawn new herdr session + run agent with the saved id
|
||||
@@ -99,7 +100,7 @@ case "$AGENT" in
|
||||
# auto-handle trust / bypass dialogs
|
||||
handle_startup_dialogs "$SESSION_NAME" 20
|
||||
;;
|
||||
agy|hermes|cline)
|
||||
agy|hermes|cline|grok)
|
||||
eval "herdr new-session -d -s \"$SESSION_NAME\" -x 140 -y 40 -c \"$WORKSPACE\" \"$CMD_FULL\""
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -109,6 +109,7 @@ if [ -z "$CMD_FULL" ]; then
|
||||
agy) CMD_FULL="${RESOLVED_BIN} --dangerously-skip-permissions --conversation $UUID" ;;
|
||||
hermes) CMD_FULL="${RESOLVED_BIN} --resume $UUID" ;;
|
||||
cline) CMD_FULL="${RESOLVED_BIN} -i --id $UUID" ;;
|
||||
grok) CMD_FULL="${RESOLVED_BIN} --resume $UUID --permission-mode bypassPermissions" ;;
|
||||
*) echo "ERROR: unsupported agent: $AGENT" >&2; exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -8,7 +8,7 @@ platforms: [linux, macos]
|
||||
environments: [terminal, herdr]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [agent, herdr, claude, antigravity, agy, status, read-only, snapshot]
|
||||
tags: [agent, herdr, claude, antigravity, agy, grok, status, read-only, snapshot]
|
||||
related_skills: [multi-agent-mux-create, multi-agent-mux-resume, multi-agent-mux-stop, multi-agent-mux-monitor]
|
||||
prereq_skills: [multi-agent-mux-create, multi-agent-mux-monitor]
|
||||
---
|
||||
|
||||
@@ -53,12 +53,12 @@ def resume_on_disk(s):
|
||||
name = s.get('name', '')
|
||||
cwd = (s.get('pane') or {}).get('cwd', '')
|
||||
agent = None
|
||||
for a in ('claude', 'agy', 'hermes', 'cline'):
|
||||
for a in ('claude', 'agy', 'hermes', 'cline', 'grok'):
|
||||
if any(name.endswith(f'-{r}-{a}') for r in ('creator', 'planner', 'reviewer')) or name.endswith(f'-{a}'):
|
||||
agent = a
|
||||
break
|
||||
if not agent:
|
||||
for a in ('claude', 'agy', 'hermes', 'cline'):
|
||||
for a in ('claude', 'agy', 'hermes', 'cline', 'grok'):
|
||||
if f"-{a}" in name or f"_{a}" in name:
|
||||
agent = a
|
||||
break
|
||||
@@ -95,6 +95,13 @@ def resume_on_disk(s):
|
||||
if u:
|
||||
return 'yes' if os.path.exists(f"{home}/.cline/data/sessions/{u}/{u}.json") else 'MISSING'
|
||||
return 'no'
|
||||
if agent == 'grok':
|
||||
u = s.get('grok_session_id_own')
|
||||
if u:
|
||||
import urllib.parse
|
||||
ws_slug = urllib.parse.quote(cwd, safe='')
|
||||
return 'yes' if os.path.exists(f"{home}/.grok/sessions/{ws_slug}/{u}/chat_history.jsonl") else 'MISSING'
|
||||
return 'no'
|
||||
return '?'
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ The stop command is always **graceful by default**:
|
||||
|
||||
```bash
|
||||
SESSION_NAME=<workspace>-creator-<agent> # convention
|
||||
AGENT=claude # claude | agy | hermes | cline — always pass it
|
||||
AGENT=claude # claude | agy | hermes | cline | grok — always pass it
|
||||
AGENT_SESSIONS_YAML=.mam/agent-sessions.yaml
|
||||
|
||||
# 1) Session is registered?
|
||||
|
||||
@@ -94,8 +94,8 @@ while [ $# -gt 0 ]; do
|
||||
done
|
||||
if [ -n "$AGENT" ]; then
|
||||
case "$AGENT" in
|
||||
claude|agy|hermes|cline) ;;
|
||||
*) echo "ERROR: invalid agent type '$AGENT'. Allowed types are: claude, agy, hermes, cline." >&2; exit 2 ;;
|
||||
claude|agy|hermes|cline|grok) ;;
|
||||
*) echo "ERROR: invalid agent type '$AGENT'. Allowed types are: claude, agy, hermes, cline, grok." >&2; exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
[ -n "$SESSION_NAME" ] || { echo "ERROR: --session required" >&2; usage; exit 2; }
|
||||
@@ -290,6 +290,8 @@ if captured and not purge:
|
||||
target['hermes_conversation_id_own'] = captured
|
||||
elif agent == 'cline':
|
||||
target['cline_conversation_id_own'] = captured
|
||||
elif agent == 'grok':
|
||||
target['grok_session_id_own'] = captured
|
||||
target['resumable'] = True
|
||||
|
||||
if purge and purge_uuid:
|
||||
|
||||
@@ -88,6 +88,10 @@
|
||||
#default: INFO
|
||||
# MAM_LOG_LEVEL=INFO
|
||||
|
||||
# Optional xAI API Key for Grok Build CLI (if ~/.grok/auth.json is not present).
|
||||
#default: (unset → reads ~/.grok/auth.json)
|
||||
# XAI_API_KEY=replace_me
|
||||
|
||||
# Retention period (days) for delegate-job event logs.
|
||||
#default: 7
|
||||
# MAM_EVENT_RETENTION_DAYS=7
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ $ bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
--plan-talk 1 \
|
||||
--reviewer "reviewer-a,reviewer-b" \
|
||||
--max-loop 3 \
|
||||
--target-agent my-project-dev-claude \
|
||||
--creator my-project-dev-claude \
|
||||
--task "구현할 명확한 개발 작업 목표"
|
||||
```
|
||||
* `--max-loop`는 코드 오류 발견 시 최대 교정(반복 수정) 횟수 제한 가드레일 역할을 합니다.
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
# 🔌 Multi-Agent Mux (MAM): 신규 에이전트 타입 추가 가이드 (New Agent Integration Guide)
|
||||
|
||||
본 문서는 Multi-Agent Mux (MAM) 프레임워크에 새로운 AI 에이전트 CLI/TUI 백엔드(예: `grok`, `codex`, `opencode`, `kimi`, `cursor` 등)를 추가하기 위한 아키텍처 구조와 **5단계 필수 작업 절차**를 안내합니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 아키텍처 개요 (Architecture Overview)
|
||||
|
||||
MAM은 에이전트별 동작 특성(TUI 프롬프트 패턴, 세션 복원 인자, 아티팩트 저장소 등)을 Python 기반의 **어댑터 패턴(`BaseAgentAdapter`)**으로 격리하여 관리합니다. 코어 런타임(Shell, Herdr Daemon, MQTT 브로커)을 수정할 필요 없이 어댑터를 플러그인 형태로 추가할 수 있습니다.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ Herdr Runtime │
|
||||
│ (herdr agent start <name> --kind <kind> -- <cmd>) │
|
||||
└────────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ lib.sh (Shell Runtime) │
|
||||
│ • resolve_agent_type_from_registry() │
|
||||
│ • wait_for_tui_ready() (via MAM_READY_TOKENS) │
|
||||
│ • send_keys_safe() / stop_session.sh │
|
||||
└────────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
eval $(python -m lib_py.agents facts <agent>)
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ lib_py.agents Framework │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ BaseAgentAdapter │ │
|
||||
│ │ • name, own_key, ready_tokens, exit_key, delegate_agent_key, identity_cache_fields │ │
|
||||
│ │ • input_prompt, input_placeholder, input_rule_pattern │ │
|
||||
│ │ • artifact_path(), verify_artifact(), purge_artifacts() │ │
|
||||
│ │ • spawn_spec(), resume_spec(), auth_ok(), discover() │ │
|
||||
│ └────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────────────┬───────────────────────────────┼───────────────────────────────┬─────────────────────┐ │
|
||||
│ ▼ ▼ ▼ ▼ ▼ │
|
||||
│ ClaudeAgentAdapter AgyAgentAdapter ClineAgentAdapter HermesAgentAdapter [NewAgentAdapter] │
|
||||
│ (Claude Code) (Antigravity) (Cline) (Hermes) (Grok / Codex / ...) │
|
||||
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 5단계 필수 구현 가이드 (Step-by-Step Implementation)
|
||||
|
||||
신규 에이전트 `<agent>`(예: `grok`)를 연동하기 위해서는 다음 5개 파일/영역의 수정이 필요합니다.
|
||||
|
||||
```
|
||||
📁 .agents/skills/lib_py/agents/adapters/<agent>.py # Step 1: 어댑터 클래스 구현
|
||||
📁 .agents/skills/lib_py/agents/registry.py # Step 2: 어댑터 레지스트리 등록
|
||||
📁 .agents/skills/lib.sh # Step 3: 쉘 디스패치 및 kind 매핑
|
||||
📁 Herdr Runtime Configuration # Step 4: Herdr CLI 데몬 kind 호환성 확인
|
||||
📁 tests/test_a4_adapter_contract.py # Step 5: 어댑터 계약 단위 테스트 작성
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 1: Python 어댑터 구현 (`.agents/skills/lib_py/agents/adapters/<agent>.py`)
|
||||
|
||||
`BaseAgentAdapter`를 상속하는 `<Agent>AgentAdapter` 클래스를 생성합니다.
|
||||
|
||||
```python
|
||||
"""
|
||||
<agent>.py — <Agent> agent adapter for Multi-Agent Mux (MAM)
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import Any, List, Optional
|
||||
from ..base import BaseAgentAdapter, DiscoveryContext
|
||||
|
||||
|
||||
class GrokAgentAdapter(BaseAgentAdapter):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""에이전트 고유 식별자 (소문자 영문)"""
|
||||
return 'grok'
|
||||
|
||||
@property
|
||||
def own_key(self) -> str:
|
||||
"""YAML (.mam/agent-sessions.yaml) 세션 상태에 저장될 키 이름"""
|
||||
return 'grok_session_id_own'
|
||||
|
||||
@property
|
||||
def ready_tokens(self) -> str:
|
||||
"""TUI 부팅 완료 및 사용자 입력 대기 상태를 감지하는 정규식 패턴"""
|
||||
return r'Grok|xAI|Assistant|❯|>>>'
|
||||
|
||||
@property
|
||||
def exit_key(self) -> str:
|
||||
"""정상 종료(Graceful exit) 시 TUI에 입력할 키/명령어"""
|
||||
return '/exit' # 또는 'Exit', 'quit', ':q'
|
||||
|
||||
@property
|
||||
def delegate_agent_key(self) -> str:
|
||||
"""delegate-job MQTT 이벤트 채널에서 사용할 수신자 식별자"""
|
||||
return 'grok-cli'
|
||||
|
||||
@property
|
||||
def identity_cache_fields(self) -> tuple:
|
||||
"""세션 복원 시 캐시할 식별자 필드 튜플"""
|
||||
return ('session_id',)
|
||||
|
||||
# =========================================================================
|
||||
# TUI 프롬프트 영역 비주얼 파싱 (선택적 커스터마이징)
|
||||
# =========================================================================
|
||||
@property
|
||||
def input_prompt(self) -> str:
|
||||
return '❯'
|
||||
|
||||
@property
|
||||
def input_placeholder(self) -> str:
|
||||
return ''
|
||||
|
||||
@property
|
||||
def input_rule_pattern(self) -> str:
|
||||
return r'─{10,}'
|
||||
|
||||
# =========================================================================
|
||||
# 아티팩트 및 대화 히스토리 수명 주기 관리
|
||||
# =========================================================================
|
||||
def artifact_path(self, uuid: str, ctx: DiscoveryContext) -> str:
|
||||
"""해당 세션 UUID의 대화 로그/아티팩트가 저장되는 절대 경로 반환"""
|
||||
return f"{ctx.home_dir}/.grok/sessions/{uuid}.json"
|
||||
|
||||
def verify_artifact(self, uuid: str, ctx: DiscoveryContext) -> bool:
|
||||
"""디스크 상의 아티팩트 유효성 및 타임스탬프 검증"""
|
||||
path = self.artifact_path(uuid, ctx)
|
||||
if not os.path.exists(path):
|
||||
return False
|
||||
if ctx.epoch and os.path.getmtime(path) < ctx.epoch:
|
||||
return False
|
||||
return True
|
||||
|
||||
def purge_artifacts(self, uuid: str, ctx: DiscoveryContext) -> List[str]:
|
||||
"""--purge-conversation 옵션 호출 시 디스크의 대화 파일 삭제"""
|
||||
path = self.artifact_path(uuid, ctx)
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
return [path]
|
||||
return []
|
||||
|
||||
# =========================================================================
|
||||
# CLI 실행 및 세션 복원 인자 생성
|
||||
# =========================================================================
|
||||
def spawn_spec(self, binary: str, session_uuid: str = "", use_wrapper: bool = False) -> str:
|
||||
"""신규 세션 기동 시 실행할 커맨드라인 문자열 생성"""
|
||||
if session_uuid:
|
||||
return f"{binary} --session {session_uuid}"
|
||||
return binary
|
||||
|
||||
def resume_spec(self, binary: str, session_uuid: str, materialized: bool = False) -> str:
|
||||
"""기존 세션 복원(Resume) 시 실행할 커맨드라인 문자열 생성"""
|
||||
if materialized and session_uuid:
|
||||
return f"{binary} --resume {session_uuid}"
|
||||
return f"{binary} --session {session_uuid}" if session_uuid else binary
|
||||
|
||||
# =========================================================================
|
||||
# 인증 상태 검사 및 세션 디스커버리
|
||||
# =========================================================================
|
||||
def auth_ok(self, run_cmd: Optional[Any] = None) -> bool:
|
||||
"""에이전트 실행에 필요한 API 키/토큰/설정 파일 존재 여부 확인"""
|
||||
token_file = os.path.expanduser('~/.grok/token.json')
|
||||
return os.path.exists(token_file) or bool(os.environ.get('GROK_API_KEY') or os.environ.get('XAI_API_KEY'))
|
||||
|
||||
def discover(self, ctx: DiscoveryContext) -> List[str]:
|
||||
"""디스크의 세션 저장소에서 워크스페이스와 일치하는 세션 UUID 목록 탐색"""
|
||||
return []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 어댑터 레지스트리 등록 (`.agents/skills/lib_py/agents/registry.py`)
|
||||
|
||||
`registry.py`의 `_ADAPTERS` 딕셔너리에 새 어댑터 인스턴스를 등록합니다.
|
||||
|
||||
```python
|
||||
# 1. 어댑터 임포트
|
||||
from .adapters.grok import GrokAgentAdapter
|
||||
|
||||
# 2. 레지스트리 맵에 등록
|
||||
_ADAPTERS: Dict[str, BaseAgentAdapter] = {
|
||||
'claude': ClaudeAgentAdapter(),
|
||||
'agy': AgyAgentAdapter(),
|
||||
'hermes': HermesAgentAdapter(),
|
||||
'cline': ClineAgentAdapter(),
|
||||
'grok': GrokAgentAdapter(), # 👈 추가
|
||||
}
|
||||
```
|
||||
|
||||
> **등록 후 즉시 사용 가능한 CLI 명령**:
|
||||
> - `python -m lib_py.agents facts grok` ➔ 쉘 환경변수(`MAM_READY_TOKENS`, `MAM_EXIT_KEY` 등) 자동 출력
|
||||
> - `python -m lib_py.agents spawn-spec grok grok` ➔ 기동 커맨드 생성
|
||||
> - `python -m lib_py.agents resume-spec grok grok <uuid>` ➔ 복원 커맨드 생성
|
||||
> - `python -m lib_py.agents resolve <session_name>` ➔ 세션 이름 기반 에이전트 자동 식별
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 쉘 런타임 디스패치 연동 (`.agents/skills/lib.sh`)
|
||||
|
||||
`lib.sh`에서 Herdr 세션 기동 시 올바른 `kind`가 전달되도록 패턴 매칭을 추가합니다.
|
||||
|
||||
#### 1) Herdr Session Kind 매핑 (`lib.sh:357-371`)
|
||||
```bash
|
||||
case "$name" in
|
||||
*-creator-claude|*-planner-claude|*-reviewer-claude) kind="claude" ;;
|
||||
*-creator-agy|*-planner-agy|*-reviewer-agy) kind="agy" ;;
|
||||
*-creator-hermes|*-planner-hermes|*-reviewer-hermes) kind="hermes" ;;
|
||||
*-creator-cline|*-planner-cline|*-reviewer-cline) kind="cline" ;;
|
||||
*-creator-grok|*-planner-grok|*-reviewer-grok) kind="grok" ;; # 👈 추가
|
||||
*)
|
||||
if echo "$name" | grep -qi "claude"; then kind="claude"
|
||||
elif echo "$name" | grep -qi "agy"; then kind="agy"
|
||||
elif echo "$name" | grep -qi "hermes"; then kind="hermes"
|
||||
elif echo "$name" | grep -qi "cline"; then kind="cline"
|
||||
elif echo "$name" | grep -qi "grok"; then kind="grok" # 👈 추가
|
||||
else kind="generic"; fi
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
#### 2) 바이너리 이름 중복 제거 튜플 (`lib.sh:385`)
|
||||
`herdr agent start` 호출 시 첫 번째 인자로 전달되는 바이너리 중복을 방지하기 위해 등록합니다:
|
||||
```bash
|
||||
if [[ " claude agy hermes cline grok " =~ " ${cmd_binary} " ]]; then
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Herdr CLI 데몬 호환성 확인
|
||||
|
||||
- **내장 kind 지원 여부 확인**:
|
||||
- `herdr` 데몬이 해당 `--kind`를 자체적으로 파싱하는지 확인합니다.
|
||||
- 별도 내장 파서가 없는 경우 `--kind generic`으로도 세션 기동 및 터미널 I/O 제어가 완전하게 지원됩니다.
|
||||
|
||||
---
|
||||
|
||||
### Step 5: 테스트 작성 및 계약 검증 (`tests/test_a4_adapter_contract.py`)
|
||||
|
||||
신규 어댑터가 MAM 계약 규격을 완벽하게 충족하는지 검증하는 단위 테스트를 등록합니다.
|
||||
|
||||
```python
|
||||
# tests/test_a4_adapter_contract.py
|
||||
|
||||
def test_agent_adapter_registry():
|
||||
"""모든 등록된 에이전트 어댑터 인스턴스 검증"""
|
||||
adapters = get_all_adapters()
|
||||
assert set(adapters.keys()) == {'claude', 'agy', 'hermes', 'cline', 'grok'}
|
||||
|
||||
|
||||
def test_adapter_required_properties():
|
||||
"""어댑터 필수 프로퍼티 무결성 검증"""
|
||||
# ('grok', ('grok_session_id_own', r'Grok|xAI|Assistant|❯|>>>', '/exit', 'grok-cli', ('session_id',)))
|
||||
```
|
||||
|
||||
테스트 실행:
|
||||
```bash
|
||||
.venv/bin/python -m pytest tests/test_a4_adapter_contract.py -v
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 에이전트 백엔드별 구현 난이도 매트릭스
|
||||
|
||||
| 에이전트 백엔드 | 세션 저장소 형식 | 인증 방식 | 프롬프트 감지 난이도 | 난이도 Tier | 예상 소요 공수 |
|
||||
| :--- | :--- | :--- | :--- | :---: | :---: |
|
||||
| **OpenCode** | `~/.opencode/sessions/*.json` | 로컬 토큰 / API Key | 쉬움 (`OpenCode\|Chat`) | **Tier 1 (낮음)** | **~0.5일** |
|
||||
| **Codex CLI** | `~/.codex/projects/*.jsonl` | `OPENAI_API_KEY` | 쉬움 (`Codex\|❯`) | **Tier 1 (낮음)** | **~0.5일** |
|
||||
| **Kimi CLI** | `~/.kimi/history.db` (SQLite) | `~/.kimi/config` | 쉬움 (`Moonshot\|Kimi`) | **Tier 1 (낮음)** | **~0.5일** |
|
||||
| **Grok-Build** | `~/.grok/sessions/*.json` | 토큰 파일 / Env | 쉬움 (`Grok\|Building`) | **Tier 1 (낮음)** | **~0.5일** |
|
||||
| **Cursor CLI** | `~/.cursor/` (Headless/RPC) | OAuth / 쿠키 | 보통 (RPC 포트/상태 파싱) | **Tier 2 (중간)** | **~1.5일** |
|
||||
| **Local LLM** | `ollama` / `vllm` CLI stdout | 로컬 호스트 | 보통 (`>>>` ANSI 패턴 감지) | **Tier 2 (중간)** | **~1.0일** |
|
||||
|
||||
---
|
||||
|
||||
## 4. 완료 정의 (Definition of Done Checklist)
|
||||
|
||||
신규 에이전트 연동 PR 또는 커밋 전 다음 항목을 확인합니다:
|
||||
|
||||
- [ ] `.agents/skills/lib_py/agents/adapters/<agent>.py`에 `BaseAgentAdapter` 모든 추상 메서드/프로퍼티 구현 완료
|
||||
- [ ] `.agents/skills/lib_py/agents/registry.py`에 어댑터 등록 완료
|
||||
- [ ] `.agents/skills/lib.sh`에 `kind` 매핑 및 바이너리 패턴 등록 완료
|
||||
- [ ] `tests/test_a4_adapter_contract.py` 테스트 케이스 추가 및 통과
|
||||
- [ ] `tests/test_tier1_unit.py` 및 전체 테스트 스위트 통과 (`pytest tests/`)
|
||||
- [ ] 세션 기동(`multi-agent-mux-create`), 정지(`multi-agent-mux-stop`), 복원(`multi-agent-mux-resume`) 동작 검증 완료
|
||||
+1
-1
Submodule nats-docker updated: 5db38da8a5...5f015f5855
@@ -26,11 +26,18 @@ def test_resolve_home_contract():
|
||||
assert resolve_home() == home_val
|
||||
|
||||
def test_agent_adapter_registry():
|
||||
for agent in ('claude', 'agy', 'hermes', 'cline'):
|
||||
EXPECTED_OWN_KEYS = {
|
||||
'claude': 'claude_session_id_own',
|
||||
'agy': 'agy_conversation_id_own',
|
||||
'hermes': 'hermes_conversation_id_own',
|
||||
'cline': 'cline_conversation_id_own',
|
||||
'grok': 'grok_session_id_own',
|
||||
}
|
||||
for agent, expected in EXPECTED_OWN_KEYS.items():
|
||||
adapter = get_adapter(agent)
|
||||
assert adapter is not None
|
||||
assert adapter.name == agent
|
||||
assert adapter.own_key == f"{agent}_{'session' if agent == 'claude' else 'conversation'}_id_own"
|
||||
assert adapter.own_key == expected
|
||||
assert own_key(agent) == adapter.own_key
|
||||
|
||||
def test_agent_of_row_priority():
|
||||
@@ -67,6 +74,7 @@ def test_adapter_required_properties():
|
||||
'agy': ('Antigravity', 'Exit', 'antigravity-cli', ('conversation_id', 'conversation_db', 'conversation_brain_dir')),
|
||||
'hermes': ('Hermes', '/exit', 'hermes-agent', ('session_id',)),
|
||||
'cline': ('Cline|history|Chat|What can I do|slash commands', '/exit', 'cline-agent', ('session_id',)),
|
||||
'grok': ('Grok|xAI|Assistant|❯|>>>', '/exit', 'grok-build', ('session_id', 'session_jsonl')),
|
||||
}
|
||||
for agent, (toks, exitk, delk, cache_f) in expected.items():
|
||||
adapter = get_adapter(agent)
|
||||
@@ -82,7 +90,7 @@ def test_facts_bridge_eval_contract():
|
||||
env = os.environ.copy()
|
||||
skills_dir = str(Path(__file__).resolve().parent.parent / ".agents" / "skills")
|
||||
env["PYTHONPATH"] = f"{skills_dir}:{env.get('PYTHONPATH', '')}"
|
||||
for agent in ('claude', 'agy', 'hermes', 'cline'):
|
||||
for agent in ('claude', 'agy', 'hermes', 'cline', 'grok'):
|
||||
res = subprocess.run([sys.executable, "-m", "lib_py.agents", "facts", agent], capture_output=True, text=True, env=env)
|
||||
assert res.returncode == 0
|
||||
facts_output = res.stdout
|
||||
@@ -174,6 +182,19 @@ def test_purge_artifacts_composite(tmp_path):
|
||||
assert len(purged_cl) == 1
|
||||
assert not os.path.exists(cline_dir)
|
||||
|
||||
# 5. Grok (session directory containing chat_history.jsonl)
|
||||
grok_adapter = get_adapter('grok')
|
||||
grok_ctx = DiscoveryContext(workspace=ws, agent_name='grok', home_dir=home)
|
||||
g_path = grok_adapter.artifact_path('uuid-g', grok_ctx)
|
||||
os.makedirs(os.path.dirname(g_path), exist_ok=True)
|
||||
with open(g_path, 'w') as f:
|
||||
f.write('{"session_id": "uuid-g"}')
|
||||
assert os.path.exists(g_path)
|
||||
purged_g = grok_adapter.purge_artifacts('uuid-g', grok_ctx)
|
||||
assert len(purged_g) == 1
|
||||
assert not os.path.exists(g_path)
|
||||
assert not os.path.exists(os.path.dirname(g_path))
|
||||
|
||||
def test_adapter_spawn_and_resume_specs():
|
||||
claude = get_adapter('claude')
|
||||
assert claude.spawn_spec('claude', 'u1') == 'claude --dangerously-skip-permissions --session-id u1'
|
||||
@@ -194,6 +215,12 @@ def test_adapter_spawn_and_resume_specs():
|
||||
assert cline.resume_spec('cline', 'u1', materialized=True) == 'cline -i --id u1'
|
||||
assert cline.resume_spec('cline', 'u1', materialized=False) == 'cline -i'
|
||||
|
||||
grok = get_adapter('grok')
|
||||
assert grok.spawn_spec('grok', 'u1') == 'grok --session-id u1 --permission-mode bypassPermissions'
|
||||
assert grok.spawn_spec('grok', '') == 'grok --permission-mode bypassPermissions'
|
||||
assert grok.resume_spec('grok', 'u1', materialized=True) == 'grok --resume u1 --permission-mode bypassPermissions'
|
||||
assert grok.resume_spec('grok', 'u1', materialized=False) == 'grok --session-id u1 --permission-mode bypassPermissions'
|
||||
|
||||
def test_adapter_auth_ok(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("HOME_DIR", str(tmp_path))
|
||||
# Claude auth runner
|
||||
@@ -209,6 +236,17 @@ def test_adapter_auth_ok(tmp_path, monkeypatch):
|
||||
oauth_file.write_text("{}")
|
||||
assert agy.auth_ok() is True
|
||||
|
||||
# Grok auth check (env var or auth.json file)
|
||||
grok = get_adapter('grok')
|
||||
assert grok.auth_ok() is False
|
||||
monkeypatch.setenv("XAI_API_KEY", "test-key")
|
||||
assert grok.auth_ok() is True
|
||||
monkeypatch.delenv("XAI_API_KEY", raising=False)
|
||||
grok_auth = tmp_path / ".grok" / "auth.json"
|
||||
grok_auth.parent.mkdir(parents=True, exist_ok=True)
|
||||
grok_auth.write_text("{}")
|
||||
assert grok.auth_ok() is True
|
||||
|
||||
# Hermes & Cline always True
|
||||
assert get_adapter('hermes').auth_ok() is True
|
||||
assert get_adapter('cline').auth_ok() is True
|
||||
@@ -268,6 +306,15 @@ def test_adapter_discover(tmp_path):
|
||||
f.write('{"session_id": "u-cl1", "cwd": "' + ws + '"}')
|
||||
assert cline.discover(ctx_cl) == ['u-cl1']
|
||||
|
||||
# 5. Grok
|
||||
grok = get_adapter('grok')
|
||||
ctx_g = DiscoveryContext(workspace=ws, agent_name='grok', home_dir=home)
|
||||
g_sess = f"{home}/.grok/sessions/{grok._ws_dir(ctx_g)}/u-g1"
|
||||
os.makedirs(g_sess, exist_ok=True)
|
||||
with open(f"{g_sess}/chat_history.jsonl", 'w') as f:
|
||||
f.write('{"session_id": "u-g1", "cwd": "' + ws + '"}\n')
|
||||
assert grok.discover(ctx_g) == ['u-g1']
|
||||
|
||||
def test_cli_bridge_subcommands_and_quote_safety():
|
||||
import subprocess, sys
|
||||
from pathlib import Path
|
||||
@@ -286,7 +333,7 @@ def test_cli_bridge_subcommands_and_quote_safety():
|
||||
assert res.stdout.strip() == "/bin/claude --dangerously-skip-permissions --session-id uuid-test"
|
||||
|
||||
# 3. exit-key
|
||||
for agent, expected_key in [('claude', '/exit'), ('agy', 'Exit'), ('hermes', '/exit'), ('cline', '/exit')]:
|
||||
for agent, expected_key in [('claude', '/exit'), ('agy', 'Exit'), ('hermes', '/exit'), ('cline', '/exit'), ('grok', '/exit')]:
|
||||
res = subprocess.run([sys.executable, "-m", "lib_py.agents", "exit-key", agent], capture_output=True, text=True, env=env)
|
||||
assert res.returncode == 0
|
||||
assert res.stdout.strip() == expected_key
|
||||
@@ -298,6 +345,7 @@ def test_delegate_agent_resolution_and_fallback():
|
||||
'agy': 'antigravity-cli',
|
||||
'hermes': 'hermes-agent',
|
||||
'cline': 'cline-agent',
|
||||
'grok': 'grok-build',
|
||||
}
|
||||
# 1. Adapter property
|
||||
for agent, expected_key in expected_map.items():
|
||||
@@ -316,6 +364,7 @@ def test_delegate_agent_resolution_and_fallback():
|
||||
hermes) delegate_agent="hermes-agent" ;;
|
||||
cline) delegate_agent="cline-agent" ;;
|
||||
agy) delegate_agent="antigravity-cli" ;;
|
||||
grok) delegate_agent="grok-build" ;;
|
||||
*) echo "ERROR: cannot resolve delegate agent key for '$AGENT'" >&2; exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -18,19 +18,19 @@ def test_bug2_headless_layout_does_not_overflow():
|
||||
|
||||
# 2. Genuine small pane (overflow)
|
||||
payload_small = {"result": {"panes": [{"pane_id": "p1", "rect": {"width": 50, "height": 30}}]}}
|
||||
d_small = compute_2xk_layout(payload_small)
|
||||
d_small = compute_2xk_layout(payload_small, min_cols=60, min_rows=20)
|
||||
assert d_small.is_overflow, f"Small pane should be overflow, got {d_small}"
|
||||
assert d_small.direction == "overflow"
|
||||
|
||||
# 3. Wide pane (split right)
|
||||
payload_wide = {"result": {"panes": [{"pane_id": "p1", "rect": {"width": 160, "height": 30}}]}}
|
||||
d_wide = compute_2xk_layout(payload_wide)
|
||||
d_wide = compute_2xk_layout(payload_wide, min_cols=60, min_rows=20)
|
||||
assert not d_wide.is_overflow
|
||||
assert d_wide.direction == "right"
|
||||
|
||||
# 4. Tall pane (split down)
|
||||
payload_tall = {"result": {"panes": [{"pane_id": "p1", "rect": {"width": 80, "height": 60}}]}}
|
||||
d_tall = compute_2xk_layout(payload_tall)
|
||||
d_tall = compute_2xk_layout(payload_tall, min_cols=60, min_rows=20)
|
||||
assert not d_tall.is_overflow
|
||||
assert d_tall.direction == "down"
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""CLI parser and planner-resolution tests for multi-agent-mux-loop (Rev.4)."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
RUN_LOOP_SH = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "run_loop.sh"
|
||||
SKILL_MD = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "SKILL.md"
|
||||
|
||||
|
||||
def _run_loop(args, env_extra=None, cwd=None):
|
||||
env = dict(os.environ)
|
||||
if env_extra:
|
||||
env.update(env_extra)
|
||||
return subprocess.run(
|
||||
["bash", str(RUN_LOOP_SH), *args],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(cwd or REPO_ROOT),
|
||||
)
|
||||
|
||||
|
||||
def _combined(res):
|
||||
return (res.stdout or "") + (res.stderr or "")
|
||||
|
||||
|
||||
def test_help_lists_creator_not_target_agent():
|
||||
res = _run_loop(["--help"])
|
||||
assert res.returncode != 0
|
||||
text = _combined(res)
|
||||
assert "--creator" in text
|
||||
assert "--planner" in text
|
||||
assert "--target-agent" not in text
|
||||
|
||||
|
||||
def test_missing_creator_and_task_fail_fast():
|
||||
res = _run_loop([])
|
||||
assert res.returncode != 0
|
||||
assert "ERROR: --creator and --task are mandatory fields." in _combined(res)
|
||||
|
||||
|
||||
def test_creator_parses_and_reaches_session_check(tmp_path):
|
||||
res = _run_loop(
|
||||
["--creator", "nonexistent-agent", "--task", "t"],
|
||||
env_extra={"MAM_LOOP_NO_FREEZE": "1", "MAM_LOOP_MARKER": str(tmp_path / "loop-guard")},
|
||||
)
|
||||
assert res.returncode != 0
|
||||
text = _combined(res)
|
||||
assert "was removed" not in text
|
||||
assert "mandatory fields" not in text
|
||||
assert "not registered" in text or "already running" in text
|
||||
|
||||
|
||||
def test_target_agent_is_rejected():
|
||||
res = _run_loop(["--target-agent", "any-session", "--task", "t"])
|
||||
assert res.returncode == 1
|
||||
text = _combined(res)
|
||||
assert "ERROR: --target-agent was removed. Use --creator <session> instead." in text
|
||||
assert "mandatory fields" not in text
|
||||
|
||||
|
||||
def test_planner_without_plan_fail_fast():
|
||||
res = _run_loop(["--creator", "c1", "--planner", "p1", "--task", "t"])
|
||||
assert res.returncode == 1
|
||||
text = _combined(res)
|
||||
assert "ERROR: --planner was specified without --plan." in text
|
||||
assert "--plan --planner" in text
|
||||
|
||||
|
||||
def test_skill_md_uses_creator_flag():
|
||||
content = SKILL_MD.read_text(encoding="utf-8")
|
||||
assert "--creator" in content
|
||||
assert "--planner" in content
|
||||
assert "--target-agent" not in content
|
||||
|
||||
|
||||
def _seed_sessions(yaml_path, sessions):
|
||||
lines = ["herdr_sessions:"]
|
||||
for s in sessions:
|
||||
lines.append(f"- name: {s['name']}")
|
||||
lines.append(f" status: {s['status']}")
|
||||
lines.append(f" role: {s['role']}")
|
||||
yaml_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
db_path = yaml_path.with_suffix(".db")
|
||||
if db_path.exists():
|
||||
db_path.unlink()
|
||||
|
||||
|
||||
def _loop_env(mam_sandbox, marker_path):
|
||||
return {
|
||||
"MAM_LOOP_NO_FREEZE": "1",
|
||||
"MAM_LOOP_MARKER": str(marker_path),
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"WORKSPACE_ROOT": str(mam_sandbox),
|
||||
"MAM_REAL_ROOT": str(mam_sandbox),
|
||||
}
|
||||
|
||||
|
||||
def test_explicit_planner_unregistered(mam_sandbox, tmp_path):
|
||||
yaml_path = mam_sandbox / ".mam" / "agent-sessions.yaml"
|
||||
_seed_sessions(yaml_path, [{"name": "creator-1", "status": "running", "role": "creator"}])
|
||||
marker = tmp_path / "loop-guard-unreg"
|
||||
res = _run_loop(
|
||||
["--creator", "creator-1", "--plan", "--planner", "missing-planner", "--task", "t"],
|
||||
env_extra=_loop_env(mam_sandbox, marker),
|
||||
cwd=mam_sandbox,
|
||||
)
|
||||
assert res.returncode != 0
|
||||
assert "Specified planner session 'missing-planner' is not registered in the session registry." in _combined(res)
|
||||
|
||||
|
||||
def test_explicit_planner_not_running(mam_sandbox, tmp_path):
|
||||
yaml_path = mam_sandbox / ".mam" / "agent-sessions.yaml"
|
||||
_seed_sessions(
|
||||
yaml_path,
|
||||
[
|
||||
{"name": "creator-1", "status": "running", "role": "creator"},
|
||||
{"name": "planner-stopped", "status": "stopped", "role": "planner"},
|
||||
],
|
||||
)
|
||||
marker = tmp_path / "loop-guard-stopped"
|
||||
res = _run_loop(
|
||||
["--creator", "creator-1", "--plan", "--planner", "planner-stopped", "--task", "t"],
|
||||
env_extra=_loop_env(mam_sandbox, marker),
|
||||
cwd=mam_sandbox,
|
||||
)
|
||||
assert res.returncode != 0
|
||||
assert "Specified planner session 'planner-stopped' is not running (current status: 'stopped')." in _combined(res)
|
||||
|
||||
|
||||
def test_explicit_planner_skips_auto_resolve(mam_sandbox, tmp_path):
|
||||
yaml_path = mam_sandbox / ".mam" / "agent-sessions.yaml"
|
||||
_seed_sessions(
|
||||
yaml_path,
|
||||
[
|
||||
{"name": "creator-1", "status": "running", "role": "creator"},
|
||||
{"name": "planner-first", "status": "running", "role": "planner"},
|
||||
{"name": "planner-explicit", "status": "running", "role": "planner"},
|
||||
],
|
||||
)
|
||||
marker = tmp_path / "loop-guard-skip"
|
||||
res = _run_loop(
|
||||
["--creator", "creator-1", "--plan", "--planner", "planner-explicit", "--task", "t"],
|
||||
env_extra=_loop_env(mam_sandbox, marker),
|
||||
cwd=mam_sandbox,
|
||||
)
|
||||
text = _combined(res)
|
||||
assert "Resolved Planner session: planner-explicit" in text
|
||||
assert "Resolved Planner session: planner-first" not in text
|
||||
@@ -187,7 +187,7 @@ def test_o2_12_run_loop_exits_on_lock_failure(tmp_path):
|
||||
marker = tmp_path / "loop-guard-active"
|
||||
proc = acquire_bg(marker)
|
||||
try:
|
||||
cmd = ["bash", str(RUN_LOOP), "--target-agent", "dummy-agent", "--task", "test"]
|
||||
cmd = ["bash", str(RUN_LOOP), "--creator", "dummy-agent", "--task", "test"]
|
||||
run_env = dict(os.environ)
|
||||
run_env["MAM_LOOP_MARKER"] = str(marker)
|
||||
res = subprocess.run(cmd, capture_output=True, text=True, cwd=str(tmp_path), env=run_env)
|
||||
|
||||
@@ -379,7 +379,7 @@ def test_b13_reexec_preserves_original_argv(tmp_path):
|
||||
"""
|
||||
run_loop_sh = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "run_loop.sh"
|
||||
res = subprocess.run(
|
||||
["bash", str(run_loop_sh), "--target-agent", "nonexistent-agent", "--task", "test goal with spaces"],
|
||||
["bash", str(run_loop_sh), "--creator", "nonexistent-agent", "--task", "test goal with spaces"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=str(REPO_ROOT),
|
||||
@@ -461,7 +461,7 @@ def test_b13_no_freeze_switch_disables_reexec(tmp_path):
|
||||
env = os.environ.copy()
|
||||
env["MAM_LOOP_NO_FREEZE"] = "1"
|
||||
res = subprocess.run(
|
||||
["bash", str(run_loop_sh), "--target-agent", "nonexistent-agent", "--task", "test goal"],
|
||||
["bash", str(run_loop_sh), "--creator", "nonexistent-agent", "--task", "test goal"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
|
||||
@@ -946,6 +946,48 @@ def test_layout_single_workspace_54x23_compact_tiling_tier1():
|
||||
assert d4.reason == "column_width_overflow"
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# FEATURE: Grok Build TUI Agent Integration
|
||||
# ==============================================================================
|
||||
|
||||
def test_grok_adapter_contract_in_tier1():
|
||||
"""Verify GrokAgentAdapter registration and properties in Tier 1 suite."""
|
||||
from lib_py.agents.registry import get_adapter
|
||||
adapter = get_adapter('grok')
|
||||
assert adapter is not None
|
||||
assert adapter.name == 'grok'
|
||||
assert adapter.own_key == 'grok_session_id_own'
|
||||
assert adapter.delegate_agent_key == 'grok-build'
|
||||
assert adapter.exit_key == '/exit'
|
||||
assert adapter.ready_tokens == 'Grok|xAI|Assistant|❯|>>>'
|
||||
|
||||
|
||||
def test_grok_shell_and_scripts_integration(mam_sandbox):
|
||||
"""Verify lib.sh, create_session.sh, stop_session.sh, and workspace_uuid contain grok."""
|
||||
# 1. lib.sh kind mapping & binaries
|
||||
lib_path = mam_sandbox / "skills" / "lib.sh"
|
||||
lib_content = lib_path.read_text()
|
||||
assert '*-creator-grok|*-planner-grok|*-reviewer-grok) kind="grok"' in lib_content
|
||||
assert "'claude', 'agy', 'hermes', 'cline', 'grok'" in lib_content
|
||||
assert '[[ "$sess" =~ "grok" ]]' in lib_content
|
||||
|
||||
# 2. create_session.sh validation
|
||||
create_path = mam_sandbox / "skills" / "multi-agent-mux-create" / "scripts" / "create_session.sh"
|
||||
create_content = create_path.read_text()
|
||||
assert 'claude|agy|hermes|cline|grok)' in create_content
|
||||
|
||||
# 3. stop_session.sh validation & state capture
|
||||
stop_path = mam_sandbox / "skills" / "multi-agent-mux-stop" / "scripts" / "stop_session.sh"
|
||||
stop_content = stop_path.read_text()
|
||||
assert 'claude|agy|hermes|cline|grok)' in stop_content
|
||||
assert "target['grok_session_id_own'] = captured" in stop_content
|
||||
|
||||
# 4. workspace_uuid OWN_KEY
|
||||
from lib_py.workspace_uuid import OWN_KEY
|
||||
assert OWN_KEY.get('grok') == 'grok_session_id_own'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -432,7 +432,7 @@ d['herdr_sessions'] = [
|
||||
# Run run_loop.sh
|
||||
cmd_loop = [
|
||||
"bash", str(loop_script),
|
||||
"--target-agent", worker_name,
|
||||
"--creator", worker_name,
|
||||
"--reviewer", reviewer_name,
|
||||
"--plan",
|
||||
"--plan-talk", "1",
|
||||
|
||||
Reference in New Issue
Block a user