release(v4.7.1): bump framework and eight skills to 4.7.1 in lockstep; archive PR #32 merge-side reviews
Issue #25: a promoted plan is reused only with --plan-file; the implicit newest-plan load is gone in both runners, and an empty planner report no longer passes silently. VERSIONS.md carries the entry; the four merge-side review reports and their identity sidecars (base6fc9194-> head1e82651, evidence review-tree) are archived under .agents/reports/<session>/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
1 parent
1e826515cc
commit
6b7198f73c
24 files changed
+441
-26
No files matched your search
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"loop_id": "loop-20260921T150212Z-97d845",
|
||||
"base_sha": "6fc9194f896eb2e13e123b8ebe7a9089b629764d",
|
||||
"head_sha": "1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd",
|
||||
"tree_sha": "4fd82ad1c2682d12c3f5114818f97291af28a032",
|
||||
"review_commit": "1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd",
|
||||
"reviewer": "planner-reviewer-claude-01",
|
||||
"job_id": "293e905f",
|
||||
"verdict": "PASS",
|
||||
"transport": "herdr",
|
||||
"evidence": "review-tree",
|
||||
"isolation": "worktree",
|
||||
"verified_at": "2026-09-21T15:25:44Z"
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
# 🔍 Review Report — PR #32 병합 전 독립 리뷰 (job `293e905f`)
|
||||
|
||||
- **Reviewer**: `planner-reviewer-claude-01` (role: planner,reviewer) · 2026-09-22
|
||||
- **대상**: PR #32 `fix/issue-25` **@ `1e82651`**, base `main 6fc9194` (v4.7.0). 5커밋, 4파일 `+486/−31`.
|
||||
- **검증 경로**: `/var/folders/.../T/mam-review/loop-20260921T150212Z-97d845/1` (브리프가 지정한 리뷰 워크트리). 실제로 거기서만 검증했다.
|
||||
- **identity 확인**: 검증 경로의 `HEAD = 1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd`, `HEAD^{tree} = 4fd82ad1c2682d12c3f5114818f97291af28a032` — 브리프의 `head`·`tree` 와 **일치**한다(항목 5 identity 가 실제로 작동함을 이 잡 자체가 보여준다).
|
||||
- **안전 규칙 준수**: 검증 경로·소스 워크트리(`multi-agent-mux-wt/pr-32`)·본 체크아웃(`main 6fc9194`) 모두 수정·커밋·checkout 없음. 실험은 전부 scratchpad 임시 디렉터리(uuid4 이름)에서 했고, pytest 는 `--basetemp` 을 검증 경로 밖으로 주었다. 실제 `.mam`·데몬·세션 조작 없음, push·merge·PR 상태 변경 없음, purge·scrub 실행 없음.
|
||||
|
||||
## 결론 요약
|
||||
|
||||
**병합을 막을 결함은 없다.** 핵심 변경(암묵적 계획서 로드 제거, `--plan-file` 명시 재사용, 네 지점 공통 계획 텍스트 검사)은 코드·문서·테스트가 일치하며, 새 테스트 17건 중 **13건이 수정 전 소스에서는 실패**한다(직접 확인). 비차단 지적 3건(E1–E3)을 남긴다.
|
||||
|
||||
## 1. 암묵적 로드 제거 (항목 1)
|
||||
|
||||
| 확인 | 결과 |
|
||||
|---|---|
|
||||
| Python 러너 | `runner.py` 에서 `glob` import 가 사라졌고, 남은 `glob` 사용처는 `find_report` 의 `report-final.md` 탐색(`:568`) 하나뿐이다. `.agents/reports/<planner>/plan-*.md` 를 읽는 코드가 없다 |
|
||||
| 레거시 셸 러너 | `run_loop.sh` 의 `EXISTING_PLAN_FILE`/`ls -t .../plan-*.md` 블록이 통째로 사라지고 `log_info "No plan loaded; the Creator plans by itself."` 만 남았다 |
|
||||
| 다른 경로 | 저장소 전체에서 `plan-*.md` 를 읽는 코드는 없다(승격 쪽 **쓰기**만 `runner.py:1368-1372` 에 남는다 — 정상) |
|
||||
| planner 자동 선택 | `resolve_planner_session`(`run_loop.sh:386`)·`resolve_planner_session` 파이썬 대응은 남아 있으나, 그 결과는 **plan 모드의 위임 대상과 승격 경로**에만 쓰인다. 자기계획 모드에서 planner 이름으로 파일을 찾는 코드는 없다 |
|
||||
|
||||
## 2. `--plan-file` (항목 2) — 실측
|
||||
|
||||
검증 경로의 러너를 임시 제어 루트(`WORKSPACE_ROOT=<tmp>/ctl`)로 직접 돌려 확인했다.
|
||||
|
||||
| 입력 | 결과 |
|
||||
|---|---|
|
||||
| 없는 파일 | `[✗] --plan-file <절대경로>: not a readable file with plan text in it.` · **exit 1** |
|
||||
| 빈 파일 / 공백만 있는 파일 | 같은 메시지 · exit 1 |
|
||||
| 디렉터리 | 같은 메시지 · exit 1 (`is_file()` 로 걸린다) |
|
||||
| 읽기 권한 없음(`chmod 000`) | 같은 메시지 · exit 1 (`OSError` → `None`) |
|
||||
| `--plan` 과 함께 | `ERROR: --plan-file cannot be combined with --plan.` + 사용 예 2줄 · exit 1 |
|
||||
| 상대경로 | **제어 루트 기준**. 호출 cwd(`<tmp>/elsewhere`)에 `plans/p.md` 가 실제로 있어도 러너는 `<ctl>/plans/p.md` 를 찾고 실패한다 — #25 의 "다른 디렉터리의 동명 파일을 집어오는" 사고를 구조적으로 막는다 |
|
||||
| 공백 있는 경로(`my plans/p.md`, 상대) | 정상 로드: `Using the plan given with --plan-file: <ctl>/my plans/p.md (modified ... UTC)` (셸을 거치지 않으므로 인용 문제 없음) |
|
||||
| 절대경로·`~` | `Path.__truediv__` 가 절대경로를 그대로 두고 `expanduser()` 가 앞서 적용된다(P3d 가 고정) |
|
||||
| `--review-only` 와 함께 | 경고 후 무시(`log_warn`), `current_plan` 을 채우지 않는다 |
|
||||
| 레거시 셸(`MAM_LOOP_PY=0`) | `ERROR: --worktree/--base/--review-only/--review-isolation/--remote-review/--plan-file require the Python loop (MAM_LOOP_PY=1)` · exit 1 — **동등 구현 대신 명시적 거부**이며, 기본 경로(`MAM_LOOP_PY=1`)는 `exec "$_py" -m lib_py.loop "${MAM_LOOP_ARGV[@]}"`(`run_loop.sh:262`)로 원본 argv 를 그대로 넘기므로 공백 경로도 안전하다 |
|
||||
|
||||
읽기가 **잡을 하나도 만들기 전**(parse_args 단계)에 끝나는 것도 확인했다 — 실패 시 레지스트리에 아무 흔적이 없다.
|
||||
|
||||
## 3. 계획 텍스트 검사 (항목 3)
|
||||
|
||||
`_plan_text()`(`runner.py:303-317`)가 "읽을 수 있는 일반 파일 + 공백 제외 내용 있음"을 단일 판정으로 만들고, **파이썬 러너의 네 지점이 모두 이를 쓴다**: `--plan-file`(`:265`), `--plan` 초기 계획서(`:1025`), 논의 턴 refine(`:1054`), 리뷰 에스컬레이션 refine(`:1284`).
|
||||
|
||||
- 빈 초기 계획서 → `log_error` 후 **return 1**. 이때 이미 만든 잡은 `release_guard`/정리 경로가 담당하고, 자기계획으로의 조용한 격하가 사라졌다.
|
||||
- 빈 refine → 경고 + **이전 계획 유지**. 그래서 "갱신된 다음 계획서에 입각하여…" 프롬프트가 빈 계획을 싣는 일이 없다.
|
||||
- 에스컬레이션 경로(`[ESCALATE: PLANNER]`)는 `if self.plan_mode and complex_fix`(`:1275`)로 **plan 모드에만** 걸린다. `--plan-file` 로 계획을 실은 실행에서 ESCALATE 판정이 나오면 재계획 없이 일반 수정 라운드로 간다 — 샌드박스에서 직접 구동해 확인했다(Planner 위임 0건, 수정 프롬프트는 "리뷰어들이 지적한 다음 피드백…"). SKILL.md:31 의 "수정 라운드에는 리뷰 피드백만" 서술과 **일치**한다. 다만 이 동작 자체는 E2 로 남긴다.
|
||||
|
||||
## 4. 체크포인트 (항목 4)
|
||||
|
||||
`.mam/loop-checkpoint.json` 의 `current_plan` 은 **쓰기 전용**이다. `checkpoint.load_checkpoint()` 는 정의되어 있으나 `runner.py` 어디에서도 호출하지 않는다(`grep 'checkpoint\.'` → `save_checkpoint`·`remove_checkpoint` 뿐). 따라서 본 체크아웃 체크포인트에 남아 있는 Issue #6 계획 텍스트가 다음 루프나 재개에서 되살아날 경로는 없다. 계획은 매 실행마다 `--plan-file` 또는 `--plan` 으로만 들어온다.
|
||||
|
||||
## 5. 옵션 없을 때의 동작 변화 (항목 5)
|
||||
|
||||
- 자기계획 프롬프트(`runner.py:1095`)와 승격 경로(`:1367` `if self.plan_mode and self.current_plan and self.plan_job_id`)는 그대로다. 즉 `--plan-file` 로 실은 계획서는 **승격되지 않는다**(승격은 plan 모드의 planner 산출물만) — 옳은 구분이다.
|
||||
- 바뀐 것은 "planner 리포트 디렉터리에서 최신 plan-*.md 를 끌어오던" 동작 하나뿐이다. 그 외 프롬프트 문구·옵션 기본값·종료 코드는 동일하다.
|
||||
- 기존 스위트 정합은 §6 에서 실측했다.
|
||||
|
||||
## 6. 테스트 (항목 6)
|
||||
|
||||
### 6-1. 검증 경로에서의 실행
|
||||
|
||||
```
|
||||
cd <검증 경로> && PYTHONDONTWRITEBYTECODE=1 <제어 루트 .venv/bin/python> -m pytest -p no:cacheprovider -q -rfEs \
|
||||
--basetemp=<검증 경로 밖> tests/test_issue25_plan_reuse.py tests/test_loop_cli.py tests/test_issue5_phase3_loop.py \
|
||||
tests/test_issue21_loop_b.py tests/test_issue21_phase2_loop_a.py tests/test_issue21_acceptance.py
|
||||
→ 2 failed, 108 passed in 220.98s (skipped 0)
|
||||
```
|
||||
|
||||
**실패 2건은 PR 회귀가 아니라 리뷰 워크트리에 `.venv` 가 없어서 생긴 환경 문제다.**
|
||||
|
||||
| 실패 | 원인 | 확인 |
|
||||
|---|---|---|
|
||||
| `test_loop_cli.py::test_creator_parses_and_reaches_session_check` | `run_loop.sh` 를 하위 프로세스로 띄우는데, `REPO_ROOT`(= 검증 경로)에 `.venv` 가 없어 `_py` 가 시스템 python 으로 떨어지고 `RuntimeError: Failed to eager load lib_py.loop.autocomplete: No module named 'paho'` 로 죽는다(`run_loop.sh:253`) | `AGENT_PYTHON_BIN=<제어 루트 .venv/bin/python>` 만 주고 둘을 다시 돌리면 **2 passed in 0.34s** |
|
||||
| `test_issue5_phase3_loop.py::test_p3_02_run_loop_wrapper_delegates_to_python_default` | 동일 | 동일 |
|
||||
|
||||
두 시험 모두 PR 이 건드리지 않은 파일이며, 실패 메시지에 계획서·`--plan-file` 은 등장하지 않는다.
|
||||
|
||||
### 6-2. 수정 없이는 실패하는가 — 확인함
|
||||
|
||||
검증 경로 사본을 만들고 `runner.py`·`run_loop.sh`·`SKILL.md` **세 파일만 base `6fc9194` 판으로 되돌려** 새 테스트를 돌렸다(검증 경로 자체는 건드리지 않았다).
|
||||
|
||||
```
|
||||
13 failed, 4 passed in 1.51s
|
||||
FAILED P1, P2, P3b, P3c, P3d, P5, P9[empty], P9[whitespace], P10, P11, P7, P7b, P8
|
||||
```
|
||||
|
||||
통과한 4건(P3·P4·P5b·P6)은 옵션 없는 일반 CLI 오류·plan 모드 정상 경로라 수정과 무관한 것들이다. 즉 새 테스트는 이 변경을 정확히 겨냥한다.
|
||||
|
||||
### 6-3. 격리
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| 실제 `.mam/jobs` 전후 | **차이 0** |
|
||||
| 실제 `.agents/reports` | 새 항목 없음(테스트는 각자 `tmp_path` 안에 제어 루트를 만든다 — `ctl` 픽스처) |
|
||||
| 검증 경로 tracked 파일 | 변화 없음(`git status --short` 클린, HEAD `1e82651` 유지) |
|
||||
| 검증 경로 ignored | 내 실행이 `.mam/`(무시됨)을 만들었다. tracked 파일·커밋에는 영향이 없으며, 지우면 리뷰 트리를 수정하는 셈이라 그대로 두었다 → E3 |
|
||||
| 실행 시간 | 새 파일 `test_issue25_plan_reuse.py` 는 전체 17건이 **1.5 s** 안에 끝난다(브로커·herdr 의존 없음, 모두 stub) |
|
||||
|
||||
## 7. 문서 (항목 7)
|
||||
|
||||
- 옛 서술("기존에 승격된 계획서가 있다면 이를 로드")은 `.agents`·`docs` 어디에도 남아 있지 않다(전수 grep).
|
||||
- SKILL.md 의 사용법 문자열·CLI 표·Workflow 예시·Mermaid 다이어그램이 모두 `--plan-file` 기준으로 맞춰졌고, "계획서가 비어 있을 때" 절이 네 지점의 판정을 정확히 설명한다.
|
||||
- 러너 usage 와 셸 usage 문자열이 동일하다(P7 이 이를 고정한다).
|
||||
- 배포 사본: 저장소의 `.claude/skills/*` 는 `../../.agents/skills/*` **심링크**라 자동으로 동기화된다. 사용자 홈 `~/.claude/skills` 에는 mux 스킬 사본이 없다. 옛 문구를 가진 사본은 발견되지 않았다.
|
||||
|
||||
## 8. 병합 차단 여부 (항목 8)
|
||||
|
||||
버전 락스텝 bump 부재(`lib.sh:32`·`eager.py:16`·loop SKILL.md 모두 `4.7.0`, 오케스트레이터가 4.7.1 로 처리 예정) 외에 **병합을 막을 결함은 없다**.
|
||||
|
||||
| # | 비차단 지적 | 근거 |
|
||||
|---|---|---|
|
||||
| E1 | 레거시 셸 러너(`MAM_LOOP_PY=0`)의 plan 모드 읽기에는 빈 계획서 검사가 없다: `CURRENT_PLAN=$(cat "$PLAN_FILE")`(`run_loop.sh:501`). D13/D14 계약은 파이썬 러너에만 성립하는데 SKILL.md 의 "계획서가 비어 있을 때" 절은 이를 명시하지 않는다 | 후속: 한 줄 거부 또는 문서에 "Python 러너 기준" 명시 |
|
||||
| E2 | `--plan-file` 로 계획을 실은 실행에서 `[ESCALATE: PLANNER]` 는 재계획 없이 일반 수정 라운드로 내려간다(`runner.py:1275` 가 `plan_mode` 로 게이트). 샌드박스 구동으로 확인(Planner 위임 0건). 이 게이트는 PR 이전부터 있던 것이고 SKILL.md 서술과도 모순되지 않지만, "계획서를 갖고 도는 루프인데 에스컬레이션이 무력화된다"는 점은 문서에 없다 | 문서 보강 권고 |
|
||||
| E3 | 리뷰 격리 워크트리에는 `.venv` 가 없어, `run_loop.sh` 를 하위 프로세스로 띄우는 스위트는 `AGENT_PYTHON_BIN` 없이는 반드시 실패한다(§6-1). 또한 그 워크트리에서 pytest 를 돌리면 무시되는 `.mam/` 이 생긴다. **PR #32 가 아니라 Issue #21 항목 5 운영 쪽 후속**이다 | 리뷰 브리프의 테스트 명령에 `AGENT_PYTHON_BIN=<제어 루트 .venv/bin/python>` 를 포함하는 것을 권고 |
|
||||
| E4 | `runner.py:1070` 의 `os.path.getmtime(self.plan_file)` 는 파싱 시점과 Phase 1 사이에 파일이 사라지면 예외로 죽는다(메시지 없는 트레이스백). 극히 좁은 경합 | 사소 |
|
||||
|
||||
## 9. 교차검증 가이드
|
||||
|
||||
```bash
|
||||
RP=/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260921T150212Z-97d845/1
|
||||
PY=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python
|
||||
git -C "$RP" rev-parse HEAD HEAD^{tree} # 1e8265... / 4fd82a... = 브리프의 head·tree
|
||||
# --plan-file 실패 경로 (임시 제어 루트에서만)
|
||||
C=$(mktemp -d); mkdir -p "$C/ctl" "$C/elsewhere/plans"; : > "$C/ctl/empty.md"; echo x > "$C/elsewhere/plans/p.md"
|
||||
(cd "$C/elsewhere" && PYTHONPATH="$RP/.agents/skills" WORKSPACE_ROOT="$C/ctl" MAM_STATE_JSON='{"herdr_sessions":[]}' \
|
||||
"$PY" -m lib_py.loop --creator c --task t --plan-file plans/p.md; echo "rc=$?") # ctl 기준 경로로 실패, rc 1
|
||||
# 수정 없이는 실패하는지 (사본에서만)
|
||||
S=$(mktemp -d); rsync -a --exclude .venv --exclude .git --exclude .mam --exclude nats-docker "$RP/" "$S/"
|
||||
for f in .agents/skills/lib_py/loop/runner.py .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
|
||||
.agents/skills/multi-agent-mux-loop/SKILL.md; do git -C "$RP" show 6fc9194:"$f" > "$S/$f"; done
|
||||
(cd "$S" && PYTHONDONTWRITEBYTECODE=1 "$PY" -m pytest -q -p no:cacheprovider --basetemp=/tmp/bt tests/test_issue25_plan_reuse.py) # 13 failed
|
||||
# 실패 2건이 환경 문제임을 확인
|
||||
(cd "$RP" && AGENT_PYTHON_BIN="$PY" PYTHONDONTWRITEBYTECODE=1 "$PY" -m pytest -q -p no:cacheprovider --basetemp=/tmp/bt2 \
|
||||
tests/test_loop_cli.py::test_creator_parses_and_reaches_session_check \
|
||||
tests/test_issue5_phase3_loop.py::test_p3_02_run_loop_wrapper_delegates_to_python_default) # 2 passed
|
||||
```
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"loop_id": "loop-20260921T150212Z-97d845",
|
||||
"base_sha": "6fc9194f896eb2e13e123b8ebe7a9089b629764d",
|
||||
"head_sha": "1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd",
|
||||
"tree_sha": "4fd82ad1c2682d12c3f5114818f97291af28a032",
|
||||
"review_commit": "1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd",
|
||||
"reviewer": "planner-reviewer-codex-01",
|
||||
"job_id": "08808795",
|
||||
"verdict": "PASS",
|
||||
"transport": "herdr",
|
||||
"evidence": "review-tree",
|
||||
"isolation": "worktree",
|
||||
"verified_at": "2026-09-21T15:25:44Z"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# PR #32 independent review
|
||||
|
||||
Reviewer: planner-reviewer-codex-01. Job: 08808795.
|
||||
|
||||
Reviewed the exact detached review-identity path specified by the brief:
|
||||
`/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260921T150212Z-97d845/1`, at `1e826515`, for the five commits after base `6fc9194`. No checkout, source file, session, monitor, or real registry state was modified.
|
||||
|
||||
## Findings
|
||||
|
||||
The explicit plan-reuse change is coherent and closes the implicit-load defect.
|
||||
|
||||
- With neither `--plan` nor `--plan-file`, Python self-planning logs that no plan is loaded and does not glob `.agents/reports/<planner>/plan-*.md`. The legacy shell runner does the same. `resolve_planner_session` may still identify a planner for other loop bookkeeping, but it no longer supplies a plan through that path.
|
||||
- `--plan-file` is parsed by both wrappers. Python resolves relative paths against the control root, preserves absolute and `~` paths, requires a readable regular file with non-whitespace content, and rejects bad inputs before registering jobs. It is mutually exclusive with `--plan`; `--planner` still requires `--plan`; review-only accepts a valid file but warns and ignores it because no implementation prompt is produced. The legacy shell runner rejects the option explicitly when `MAM_LOOP_PY=0`, before the loop lock.
|
||||
- `_plan_text()` is the shared check for explicit files, initial planner reports, discussion refinements, and escalation refinements. An empty initial planner report stops the run rather than silently falling back to self-planning. An empty refine report preserves the existing plan. Correction prompts therefore never claim to carry an empty plan.
|
||||
- Checkpoint `current_plan` is not a plan-discovery source. It is only recorded for the current run; a later no-option run starts planless, and a named `--plan-file` is read explicitly. Promotion still writes the plan only for an actual `--plan` run.
|
||||
- Relative plan paths with spaces, missing paths, directories, empty/whitespace files, unreadable files, `--plan` conflicts, review-only handling, and legacy-shell rejection are covered by P1–P11. The tests run in temporary control roots and use isolated reports; they do not write the real `.mam` or `.agents/reports`.
|
||||
- The loop skill’s old implicit-reuse wording is removed. The CLI usage, workflow examples, option table, and explicit-reuse description agree. No `.claude/skills` deployment copy exists in this checkout, so there is no second copy to synchronize.
|
||||
|
||||
The known release prerequisite remains outside this PR: the orchestrator must perform the 4.7.1 lockstep version bump and release entry before merge.
|
||||
|
||||
## Verification
|
||||
|
||||
All tests used `PYTHONDONTWRITEBYTECODE=1`, the interpreter from the brief, pytest cache disabled, and a basetemp outside the review path.
|
||||
|
||||
- `tests/test_issue25_plan_reuse.py`: **17 passed** in 1.70 s.
|
||||
- `tests/test_issue25_plan_reuse.py`, `tests/test_loop_cli.py`, `tests/test_issue5_phase3_loop.py`, and `tests/test_version_consistency.py`: **39 passed, 2 failed** in 49.25 s when run exactly from the identity path. Both failures occurred before their assertions because `run_loop.sh` could not find `.venv` in the detached snapshot and fell back to system Python, which lacked `paho` (`RuntimeError: Failed to eager load lib_py.loop.autocomplete: No module named 'paho'`). This is a snapshot/setup issue, not a change in the plan-reuse code.
|
||||
- Re-running the two failed compatibility cases with `AGENT_PYTHON_BIN` pinned to the brief’s main interpreter: **2 passed** in 0.32 s. This reaches and verifies the expected session-registration checks.
|
||||
- `git diff --check 6fc9194...HEAD`: passed.
|
||||
|
||||
The focused plan tests passed without modification, and the only broader failures disappeared when the required interpreter was explicitly supplied. I found no additional merge-blocking defect beyond the known version gate.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"loop_id": "loop-20260921T150212Z-97d845",
|
||||
"base_sha": "6fc9194f896eb2e13e123b8ebe7a9089b629764d",
|
||||
"head_sha": "1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd",
|
||||
"tree_sha": "4fd82ad1c2682d12c3f5114818f97291af28a032",
|
||||
"review_commit": "1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd",
|
||||
"reviewer": "reviewer-agy-01",
|
||||
"job_id": "41f07cde",
|
||||
"verdict": "PASS",
|
||||
"transport": "herdr",
|
||||
"evidence": "review-tree",
|
||||
"isolation": "worktree",
|
||||
"verified_at": "2026-09-21T15:25:44Z"
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
# Pre-Merge Independent Review Report: PR #32 (`fix/issue-25`)
|
||||
|
||||
- **Job ID**: `41f07cde`
|
||||
- **Target Agent**: `agy` (Session: `herdr:reviewer-agy-01`)
|
||||
- **Role**: Reviewer
|
||||
- **Target Worktree Context**:
|
||||
- **Loop ID**: `loop-20260921T150212Z-97d845`
|
||||
- **Base Commit**: `6fc9194f896eb2e13e123b8ebe7a9089b629764d` (v4.7.0)
|
||||
- **Head Commit**: `1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd`
|
||||
- **Tree SHA**: `4fd82ad1c2682d12c3f5114818f97291af28a032`
|
||||
- **Verification Path**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260921T150212Z-97d845/1`
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
PR #32 resolves Issue #25 ("승격된 계획서 재사용을 --plan-file 로만 허용 — 암묵적 자동 로드 제거") across 5 commits and 4 files (+486 / −31):
|
||||
- `1e82651` fix(issue-25): a relative --plan-file is taken against the control root, as its usage line says
|
||||
- `5fa5923` docs(issue-25): the loop skill documents --plan-file and the empty-report handling
|
||||
- `a334083` test(issue-25): plan reuse is explicit only and empty plan reports are not silent (P1-P11)
|
||||
- `e551e3b` fix(issue-25): one plan-text check for every load; an empty planner report stops the loop, an empty refine keeps the plan
|
||||
- `a28711c` fix(issue-25): reuse a promoted plan only with --plan-file; drop the implicit newest-plan load
|
||||
|
||||
All checklist criteria have been verified in the isolated verification path. All 17 new tests in `test_issue25_plan_reuse.py` and all 115 baseline regression tests passed with zero regressions.
|
||||
|
||||
---
|
||||
|
||||
## 2. Detailed Verification Against Checklist Items
|
||||
|
||||
### (1) Removal of Implicit Plan Loading
|
||||
- **Python Runner (`lib_py/loop/runner.py`)**:
|
||||
- Removed `import glob` and the directory scan `glob.glob(str(self.workspace_root / ".agents" / "reports" / planner_session / "plan-*.md"))`.
|
||||
- In Phase 1 Self-Planning mode, if `--plan-file` was not supplied, `self.current_plan` remains empty and the runner logs: `"No plan loaded; the Creator plans by itself (reuse one with --plan-file <path>)."`.
|
||||
- **Legacy Shell Runner (`scripts/run_loop.sh`)**:
|
||||
- Removed `EXISTING_PLAN_FILE=$(ls -t ".agents/reports/$PLANNER_SESSION"/plan-*.md 2>/dev/null | head -n 1 || true)`.
|
||||
- In Self-Planning mode, logs `"No plan loaded; the Creator plans by itself."`.
|
||||
- **Parity & Search Check**:
|
||||
- Test `test_p8_neither_loop_implementation_globs_for_a_plan_any_more` confirms the string pattern `plan-*.md` does not appear anywhere in `runner.py` or `run_loop.sh`.
|
||||
- Automatic planner resolution (`resolve_planner_session`) is not called or consulted to retrieve stored plans in Phase 1 self-planning mode.
|
||||
|
||||
### (2) `--plan-file <path>` CLI Option Semantics
|
||||
- **Control Root Resolution**:
|
||||
- Relative paths are anchored against `self.workspace_root` via `(self.workspace_root / Path(self.plan_file).expanduser()).resolve()`. This guarantees identical path resolution whether the command is run from the repository root, a subdirectory, or an isolated worktree (`test_p3c`).
|
||||
- Absolute paths and user home expansion (`~`) remain untouched and work as expected (`test_p3d`).
|
||||
- **Fail-Closed Early Validation**:
|
||||
- Evaluated in `parse_args()` before any agent delegation or job submission occurs (`test_p3`, `test_p3b`).
|
||||
- If the path is non-existent, a directory, unreadable, or contains whitespace-only text, the runner logs an error naming the resolved path and immediately exits with rc=1.
|
||||
- **Mutual Exclusion & Option Combinations**:
|
||||
- Combining `--plan-file` with `--plan` exits 1 with a clear mutual exclusion error (`test_p4`).
|
||||
- Specifying `--plan-file` without an argument exits 1 (`test_p4`).
|
||||
- When combined with `--review-only`, the runner logs a warning and ignores the plan (`self.current_plan = ""`), because Phase 2 implementation is skipped (`test_p5`, `test_p5b`).
|
||||
- **Legacy Shell Rejection**:
|
||||
- When invoked with `MAM_LOOP_PY=0`, `run_loop.sh` explicitly detects `--plan-file` and terminates with code 1 before the loop marker/guard is created (`test_p7b`).
|
||||
|
||||
### (3) Centralized Plan Text Validation (`e551e3b`)
|
||||
- **Shared Validation Method**:
|
||||
- `LoopRunner._plan_text(path: Path) -> Optional[str]` enforces:
|
||||
1. Regular file check (`path.is_file()`).
|
||||
2. Safe decoding with error replacement (`read_text(encoding="utf-8", errors="replace")`).
|
||||
3. Stripped text check (`text if text.strip() else None`).
|
||||
- **Initial Plan Failure**:
|
||||
- In `--plan` mode, if the initial planner report is empty or whitespace-only, the runner logs `the planner's report has no plan text: <path>` and exits with code 1 (`test_p9`). This eliminates the regression where an empty plan silently demoted the run to self-planning.
|
||||
- **Refinement Retention (Debate & Review Escalation)**:
|
||||
- In Phase 1 interactive debate (`--plan-talk`) and Phase 3 review escalation (`[ESCALATE: PLANNER]`), an empty refinement report logs a warning and preserves the previously valid plan (`test_p10`, `test_p11`).
|
||||
- This prevents prompt corruption in `correction_prompt = f"갱신된 다음 계획서에 입각하여 ... 계획서:\n{self.current_plan}"`.
|
||||
|
||||
### (4) Checkpoint Integrity
|
||||
- `LoopRunner` persists `current_plan` to `.mam/loop-checkpoint.json` for diagnostic and monitoring visibility during active loops.
|
||||
- `LoopRunner` never reads `.mam/loop-checkpoint.json` at startup to populate `self.current_plan`. `self.current_plan` is always initialized cleanly (`self.current_plan = ""`).
|
||||
- Existing checkpoint files in the workspace (such as unrelated historical plan text from prior issues) cannot contaminate new loop runs.
|
||||
|
||||
### (5) Baseline Behavior & Creator Prompts
|
||||
- In self-planning mode without `--plan-file`, the initial Creator prompt retains the exact contract:
|
||||
`"계획서가 존재하지 않으므로, 작업자(Creator)의 판단하에 스스로 구현 계획 및 설계를 수립한 뒤, 이를 바탕으로 코드를 구현하고 다음 작업 목표를 완성해주세요. 작업 목표: {self.task}"`.
|
||||
- With `--plan-file <path>`, the prompt receives:
|
||||
`"이미 수립된 다음 계획서에 입각하여 작업자의 판단하에 코드를 구현하고 작업 목표를 완성해주세요. 계획서:\n{self.current_plan}\n\n작업 목표: {self.task}"`.
|
||||
- Subsequent review corrective rounds strictly provide reviewer feedback without repeating the initial plan (`test_p2`).
|
||||
- Promotion of newly planned tasks via `--plan` to `.agents/reports/<planner>/plan-<id>.md` remains operational and protected (`test_p6`).
|
||||
|
||||
### (6) Test Suite Execution & Isolation (P1–P11)
|
||||
- All 17 test cases in `tests/test_issue25_plan_reuse.py` operate strictly within isolated `tmp_path` fixtures with stubbed delegations.
|
||||
- Real `.mam/jobs`, live daemon processes, and global agent sessions remain completely untouched.
|
||||
- Execution timing: 17 tests passed in 2.02 seconds.
|
||||
|
||||
### (7) Documentation Integrity
|
||||
- `.agents/skills/multi-agent-mux-loop/SKILL.md` was thoroughly updated:
|
||||
- Completely purged the stale statement ("기존에 승격된 계획서가 있다면 이를 로드").
|
||||
- Updated Sequence Diagram and Phase 1 documentation to reflect explicit `--plan-file` reuse.
|
||||
- Added a dedicated section: `### 계획서가 비어 있을 때 (Empty plan reports)`.
|
||||
- Updated CLI Option mapping table and Workflow examples.
|
||||
- No dangling references to the old implicit loading mechanism exist across the repository.
|
||||
|
||||
### (8) Versioning & Merge Readiness
|
||||
- Version lockstep bump to `v4.7.1` is reserved for the orchestrator release commit in accordance with release procedures.
|
||||
- Formatting and git diff checks (`git diff 6fc9194...HEAD --check`) reported 0 issues.
|
||||
- No merge blockers, regression risks, or side-effects detected.
|
||||
|
||||
---
|
||||
|
||||
## 3. Test Execution Summary
|
||||
|
||||
| Test Suite | Result | Duration | Notes |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `tests/test_issue25_plan_reuse.py` | **17 / 17 PASS** | 2.02s | P1–P11 test matrix covering all reuse/empty-plan conditions |
|
||||
| `tests/test_loop_cli.py` | **9 / 9 PASS** | 38.97s | CLI interface and argument validation regressions |
|
||||
| `tests/test_issue5_phase2a_loop.py` | **91 / 91 PASS** | 2.91s | Core loop state machine, dispatching, and verdict handling |
|
||||
| `tests/test_issue5_phase3_loop.py` | **12 / 12 PASS** | 0.54s | Runner orchestration, timeouts, and signal handling |
|
||||
| `tests/test_version_consistency.py` | **3 / 3 PASS** | 0.21s | Version consistency checks |
|
||||
| `git diff 6fc9194...HEAD --check` | **0 errors** | 0.05s | Zero trailing whitespace / formatting errors |
|
||||
|
||||
---
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"loop_id": "loop-20260921T150212Z-97d845",
|
||||
"base_sha": "6fc9194f896eb2e13e123b8ebe7a9089b629764d",
|
||||
"head_sha": "1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd",
|
||||
"tree_sha": "4fd82ad1c2682d12c3f5114818f97291af28a032",
|
||||
"review_commit": "1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd",
|
||||
"reviewer": "reviewer-grok-01",
|
||||
"job_id": "06fb4a9e",
|
||||
"verdict": "PASS",
|
||||
"transport": "herdr",
|
||||
"evidence": "review-tree",
|
||||
"isolation": "worktree",
|
||||
"verified_at": "2026-09-21T15:25:44Z"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# 🔍 Review Report — Job 06fb4a9e (PR #32 @ `1e82651`)
|
||||
|
||||
- **Reviewer**: `reviewer-grok-01` · 2026-09-22
|
||||
- **검증 경로**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260921T150212Z-97d845/1`
|
||||
- **identity**: base `6fc9194` head `1e82651` tree `4fd82ad` (일치)
|
||||
- **소스 워크트리·본 체크아웃**: 읽기 전용. 본 체크아웃 `main` `6fc9194` 유지.
|
||||
|
||||
`git diff --check 6fc9194...HEAD` 클린. 5커밋.
|
||||
|
||||
## (1) 암묵적 로드 제거
|
||||
|
||||
Python: Phase 1 else 가 glob/`plan-*.md` 를 쓰지 않음 (`runner.py:1064-1073`). `import glob` 삭제. `resolve_planner_session` (`:990`) 은 세션 이름만 로그하고 파일을 읽지 않음. `--planner` 는 `--plan` 없이 exit 1 (`:244-248`).
|
||||
|
||||
레거시 셸 (`MAM_LOOP_PY=0`): `ls -t .agents/reports/.../plan-*.md` 제거 (`run_loop.sh` Phase 1 else). `plan-*.md` 문자열이 두 구현에 없음 (`test_p8`).
|
||||
|
||||
## (2) `--plan-file`
|
||||
|
||||
상대경로는 `workspace_root / path` (`runner.py:264`); POSIX 에서 절대경로는 `/` 로 치환되어 그대로. `~` 는 `expanduser`. 없는/빈/공백뿐/디렉터리/읽기 실패 → `_plan_text` None → parse_args `sys.exit(1)`, 잡 없음. `--plan` 과 상호 배타. `--review-only` 는 파일 검사 후 경고·무시(`current_plan` 비움). `--planner` 는 `--plan` 필수라 `--plan-file` 과 함께면 먼저 `--planner` 규칙에 걸린다.
|
||||
|
||||
레거시 셸은 `--plan-file` 을 Python 전용으로 **명시 거부** exit 1, 루프 락 전 (`run_loop.sh:127-134`, `test_p7b`). 공백 경로는 pathlib 가 처리; 전용 테스트는 없음(비차단).
|
||||
|
||||
## (3) `_plan_text` (e551e3b)
|
||||
|
||||
한 함수 (`:304-316`)가 `--plan-file`, 초기 planner 리포트, 논의 refine, 에스컬레이션 refine 에 사용. 빈 초기 계획 → `return 1`, Worker 없음 (`test_p9`). 빈 refine → 경고·이전 계획 유지 (`test_p10`, `test_p11` ESCALATE). 수정 프롬프트가 빈 계획을 싣지 않음.
|
||||
|
||||
## (4) 체크포인트
|
||||
|
||||
`current_plan` 은 기록만 (`_record_checkpoint`). `load_checkpoint` 를 runner 가 호출하지 않음. 새 실행은 `current_plan=""` 에서 시작. 본 체크아웃 `.mam/loop-checkpoint.json` 의 옛 계획은 우회가 아님.
|
||||
|
||||
## (5) 옵션 없음
|
||||
|
||||
의도된 변화만: 자기계획 프롬프트. 승격은 `--plan` 성공 시 `plan-{id}.md` 기록 유지 (`test_p6`). 기존 테스트는 검증 경로에서 실행.
|
||||
|
||||
## (6) 테스트 P1–P11
|
||||
|
||||
전부 `tmp_path`. 검증 경로:
|
||||
|
||||
```
|
||||
PYTHONDONTWRITEBYTECODE=1 <control .venv python> -m pytest -p no:cacheprovider --basetemp=/tmp/mam-review-basetemp-...
|
||||
tests/test_issue25_plan_reuse.py
|
||||
```
|
||||
|
||||
**17 passed** (P1–P11 + p3b/c/d, p5b, p7/p7b, p8). 수정 전 P1 은 옛 glob 을 단언하므로 실패했을 테스트.
|
||||
|
||||
같은 인터프리터로 `test_loop_cli`·`test_issue5_phase3`·`test_issue21_worktree_prereqs`·`test_version_consistency` 포함 시 래퍼 서브프로세스가 검증 트리에 `.venv` 가 없어 `paho` 를 못 찾는 실패 2건이 났고, `DELEGATE_JOB_PYTHON` 을 컨트롤 `.venv` 로 주면 **19 passed**. 제품 회귀가 아니라 스냅샷에 venv 가 없는 환경.
|
||||
|
||||
## (7) 문서
|
||||
|
||||
SKILL.md 에서 "기존에 승격된 계획서가 있다면 이를 로드" 잔존 없음. usage·CLI 표·Workflow 예시가 `--plan-file` 과 일치. `.claude/skills` 는 이 스냅샷에 없음.
|
||||
|
||||
## (8) 락스텝
|
||||
|
||||
`MAM_VERSION=4.7.0`. 4.7.1 bump 는 오케스트레이터 예정. 병합 차단 아님.
|
||||
|
||||
## 종합
|
||||
|
||||
암묵적 최신 계획 로드가 Python·레거시 양쪽에서 제거됐고, 재사용은 `--plan-file` 뿐이다. 빈 리포트는 조용히 계획을 지우지 않는다. 설계 재작업 불필요.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -29,7 +29,7 @@ export WORKSPACE_ROOT
|
||||
# Framework semantic version. Single runtime source of truth; kept in lockstep
|
||||
# with VERSIONS.md and the 8 SKILL.md frontmatters by tests/test_version_consistency.py.
|
||||
# NOTE: unlike other MAM_* variables this one is intentionally NOT env-overridable.
|
||||
MAM_VERSION="4.7.0"
|
||||
MAM_VERSION="4.7.1"
|
||||
export MAM_VERSION
|
||||
AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}"
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import sys
|
||||
from typing import List, Sequence
|
||||
|
||||
# Framework version locked with framework constants
|
||||
FRAMEWORK_VERSION = "4.7.0"
|
||||
FRAMEWORK_VERSION = "4.7.1"
|
||||
|
||||
CORE_MODULES: Sequence[str] = (
|
||||
# Standard library modules
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-create
|
||||
description: "Create a new agent session (claude, antigravity/agy) in a dedicated herdr session for context-preserving long-running work. Always creates a herdr session — never backgrounds with nohup/disown. Writes the new session to .mam/agent-sessions.yaml. Use when you want to start a fresh agent (no prior UUID) for a new project workspace."
|
||||
version: 4.7.0
|
||||
version: 4.7.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-delegate-job
|
||||
description: "Delegate a unit of work to any autonomous agent (claude-code, hermes, agy, grok-build, codex, or a human) and observe it asynchronously over an MQTT event channel. Supported roles include orchestrator, worker, and reviewer."
|
||||
version: 4.7.0
|
||||
version: 4.7.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos, windows]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-loop
|
||||
description: "Run an autonomous planning-execution-review loop using multiple agents (Planner, Creator, Reviewers) in the workspace. Automatically orchestrates plan discussion, code changes, and peer reviews until a unanimous PASS is achieved or the maximum iteration limit is reached."
|
||||
version: 4.7.0
|
||||
version: 4.7.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-monitor
|
||||
description: "Run a long-lived reconciler that watches .mam/agent-sessions.yaml against the actual herdr/agent runtime state and reconciles them. Use when you want live visibility into which agent sessions are running, which are dead, which have stale YAML entries, and which have new session ids that haven't been recorded yet. Runs as a persistent loop (`reconcile.sh --subscribe`) that keeps going until it times out, idles out, or is interrupted."
|
||||
version: 4.7.0
|
||||
version: 4.7.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-orc-onboard
|
||||
description: "Register current or specified orchestrator session UUID into agent-sessions.yaml orchestrator_uuids list to prevent sub-agent discovery capture."
|
||||
version: 4.7.0
|
||||
version: 4.7.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-resume
|
||||
description: "Resume an existing agent (claude, antigravity/agy) conversation by UUID into a herdr session. Reads .mam/agent-sessions.yaml for the saved session/conversation id, spawns (or reuses) a herdr session of the matching name, and runs `claude -r <id>` or `agy --conversation <id>` inside. Use when you want to reattach to a previous session's context, or revive a session whose herdr died but the agent's conversation is still on disk."
|
||||
version: 4.7.0
|
||||
version: 4.7.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-status
|
||||
description: "Read-only instant snapshot of all agent herdr sessions — name, YAML status, herdr alive, pane cmd/cwd, resume UUID on disk, and any drift. No mutation. Reuses reconcile.sh --dry-run for the diff logic. Use when you want to know 'what's running RIGHT NOW' without spinning up the monitor loop."
|
||||
version: 4.7.0
|
||||
version: 4.7.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-stop
|
||||
description: "Stop an agent herdr session (claude, antigravity/agy) and update .mam/agent-sessions.yaml. Default stops gracefully and marks status=stopped with conversation preserved for resume. Does NOT delete on-disk conversation artifacts (jsonl/db) — those are preserved unless --purge-conversation is passed. Use when ending a work session, switching to a different one, or cleaning up before a fresh start."
|
||||
version: 4.7.0
|
||||
version: 4.7.1
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
Reference in new issue
Block a user