release(v4.8.9): tests no longer take the checkout's loop lock - archive the review reports and bump the lockstep version

Merge-workspace review loop (--all-reviewer): round 1 (@a93c9bf) 4/4
PASS. Full suite at a93c9bf on macOS / Python 3.9 / herdr 0.9.3 with
WORKSPACE_ROOT set to an empty sentinel: 1752 passed / 0 failed / 38
skipped; the sentinel stayed empty, the review worktree had no .mam, and
the main checkout's shim was not touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-09 10:59:38 +09:00
1 parent a93c9bf292
commit ca688dcef4
24 files changed
+468 -25

No files matched your search

@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261008T234013Z-4f5d21",
"base_sha": "97c2946ee0e11eabcecafe6407755c33c0289218",
"head_sha": "a93c9bf29279f65ce0b9c9fb2c1aeea90a659420",
"tree_sha": "741c19ba4c17601768b06684d5843d218a72beb1",
"review_commit": "a93c9bf29279f65ce0b9c9fb2c1aeea90a659420",
"reviewer": "planner-reviewer-claude-01",
"job_id": "616578e7",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-09T00:45:03Z"
}
@@ -0,0 +1,47 @@
# 리뷰 리포트 — Job 616578e7 (loop-lock: 테스트가 체크아웃의 루프 잠금을 잡지 않게, 97c2946..a93c9bf)
- 리뷰어: planner-reviewer-claude-01 (claude)
- 리뷰 identity: loop-20261008T234013Z-4f5d21 / base 97c2946 / head a93c9bf / tree 741c19b — 검증 경로에서 `git rev-parse HEAD 'HEAD^{tree}'` 일치, 미커밋 변경 없음
- 범위: 커밋 1개(+55줄), 테스트 3개 파일: test_o3_scoped_guard.py 의 b13 두 곳에 `MAM_LOOP_MARKER=tmp_path/...`, 새 tests/test_loop_marker_isolation.py(L1), test_issue17_workspace_root_isolation.py W1 에 `.mam` 부재 단언 1줄. 제품 코드(.agents/skills)·버전·VERSIONS 변경 없음(브리프대로).
## 결론 요약
할 일 1~4 가 요구대로이고, 내 독립 조사(grep + 사본 트리 실행)로도 누락된 실행 지점이 없다. 잠금 동작을 시험하는 테스트들의 marker 경로는 바뀌지 않았다. 차단 결함 없음.
## 1. 조사(1) — 직접 grep 한 결과 (run_loop.sh 실행 지점 전수)
run_loop.sh 는 :15 `MAM_REAL_ROOT=${MAM_REAL_ROOT:-$REPO_ROOT}`, :110 `--help` 와 :66-141 인자 오류는 :181 의 marker 결정·:248 `mam_acquire_loop_lock` **이전**에 종료, loop_lock.sh:66 이 부모 디렉터리를 mkdir -p. Python `LoopRunner` 는 잠금을 잡지 않는다(runner.py:1224-1226 은 release 뿐, acquire 는 run_loop.sh:248 에만 있음).
| 실행 지점 | 분류 | 근거 |
|---|---|---|
| test_o3_scoped_guard.py:388, :472 (b13 두 테스트) | **대상 → 수정됨** | 체크아웃 run_loop.sh, `--creator nonexistent-agent --task …`, cwd=REPO_ROOT; 이제 `MAM_LOOP_MARKER=tmp_path/loop-guard-active`(:390, :470) |
| test_issue5_phase3_loop.py:166, :184 | 이미 지정 | :163 `MAM_LOOP_MARKER=tmp_path/loop-marker` |
| test_loop_cli.py:52 (`_run_loop`) | 이미 지정 | env_extra 에 `MAM_LOOP_MARKER=tmp_path/loop-guard` |
| test_o2_race_free_lock.py:203 (o2_12) | 이미 지정(잠금 경합 자체를 시험) | :202 `MAM_LOOP_MARKER=marker(tmp_path)` — 의도 유지 |
| test_issue25_plan_reuse.py:367 (p7b) | 이미 지정 | `MAM_LOOP_MARKER=tmp_path/loop.marker` |
| test_issue21_loop_b.py:239, test_issue21_worktree_prereqs.py:405 | 이미 지정 | WORKSPACE_ROOT=tmp + `MAM_LOOP_MARKER` |
| test_issue21_acceptance.py:298, test_issue36_prompt_transport.py:540, test_b7_diff_untracked.py:189, test_tier4_e2e.py:285 | 복사본 트리의 run_loop.sh(`main`/`ctl`/`base`/`tmp_path`) | 체크아웃이 아님(acceptance 는 marker 도 지정) |
| test_issue5_phase4_runtime.py:132-146 (p4_03), test_issue25:355, test_issue5_phase3:151, test_o1_rebuttal.py:72 | `--help` → :110 에서 종료 | 잠금 전 |
| test_o1_rebuttal.py:76 (`--max-rebut abc`) | 인자 오류 → 잠금 전 종료 | :66-98 |
| test_o3 Z-7/Z-14(:137, :155), test_deploy_freshness:473, test_issue5_phase0_contracts:481, test_o1:24/:166 | 소스 텍스트 읽기만 | 실행 없음 |
| LoopRunner in-process(issue18, issue21_loop_b, issue25, issue30, issue33, issue34, issue36, issue6, phase4 등) | 잠금 미획득 | 위 runner.py 근거; 해당 없음 |
경험적 확인(W1 방식): HEAD 트리 사본에서 `WORKSPACE_ROOT=<사본>` 으로 브리프 후보 15개 모듈을 **파일 전체** 단위로 실행하고 매번 `.mam` 확인 — **15개 모두 clean**(b7 20, deploy_freshness 13+18 skip, issue21_acceptance 10, issue21_loop_b 34, worktree_prereqs 35, issue25 17, issue36 23+1 skip, phase0 41, phase3 12, phase4 5, loop_cli 9, o1 10, o2 22, o3 32, tier4_e2e 5 — 모두 passed). 놓친 실행 지점 없음.
## 2. 할 일 2·3 — 수정 방식과 회귀 테스트
- 대상이 한 모듈의 두 테스트뿐이라 conftest fixture 대신 각 호출에 env 지정 — 브리프의 "검토" 요건에 맞는 최소 변경. o2 의 잠금 경합·o3 의 marker 검사(mam_sandbox 아래 marker)는 손대지 않았다.
- L1(test_loop_marker_isolation.py:36-50)은 W1 과 같은 방법(체크아웃 복사 → 7개 노드 실행 → `.mam` 부재)이며 NODES 는 위 표의 "잠금까지 가는" 7개와 정확히 일치한다. W1 에도 `.mam` 디렉터리 자체의 부재 단언이 추가돼(:71) 잠금 디렉터리 생성이 두 테스트에서 잡힌다.
- **수정 전 코드에서 실패 확인**: L1 자체는 `git ls-files` 가 필요해 `git archive` 사본에서는 못 돌리므로, L1 의 방법을 base 트리 사본 안에서 직접 재현했다 — 7개 노드를 `WORKSPACE_ROOT=<base 사본>` 으로 실행(7 passed) → 사본에 **`.mam/` 생성**(L1 의 마지막 단언이 실패하는 조건). 노드별로는 **b13 두 테스트만** 생성하고 나머지 5개는 clean — 테스트 머리말의 "The first two took the checkout's lock at 97c2946" 와 일치. HEAD 에서는 (a)(b) 모두에서 L1·W1 통과.
## 3. 할 일 4 — 실행 (검증 경로 워크트리, 워크트리 .venv Python 3.9.6, `env -i` 로 HERDR_*·MAM_*·MQTT_* 제거)
| 실행 | 결과 |
|---|---|
| (a) WORKSPACE_ROOT 없이: test_issue17_workspace_root_isolation, test_loop_marker_isolation, test_o3_scoped_guard, test_o2_race_free_lock | **57 passed / 0 failed** (45 s); 실행 뒤 워크트리에 `.mam/` **없음** |
| (b) WORKSPACE_ROOT=<워크트리 밖 센티널>: 같은 4개 파일 | **57 passed / 0 failed** (45 s); 센티널 **비어 있음**; 워크트리에 `.mam/` 없음 |
- 본 체크아웃의 실제 잠금 `.mam/loop-guard-active` mtime 전후 동일(1791502813) — 이 리뷰 중 본 체크아웃에서는 아무것도 실행하지 않았다. 실제 .mam/jobs 항목 수 불변. 브로커 bind 제약 없음.
- 이식성: Python 테스트 코드만(3.9 문법). L1 은 nested pytest + `git ls-files`, 이 Mac 에서 통과.
## 4. 비차단
- L1·W1 둘 다 노드 목록을 수동 유지한다(이전 리뷰의 같은 지적). run_loop.sh 를 잠금까지 실행하는 테스트를 새로 추가하면 L1 NODES 에도 넣어야 한다.
- L1 은 `_copy_checkout` 을 W1 모듈에서 import 하므로 두 테스트 파일이 결합돼 있다 — 작동에는 문제없고, 공용 헬퍼를 conftest 로 옮길지는 취향 문제.
- 새 관찰 없음.
[VERDICT: PASS]
@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261008T234013Z-4f5d21",
"base_sha": "97c2946ee0e11eabcecafe6407755c33c0289218",
"head_sha": "a93c9bf29279f65ce0b9c9fb2c1aeea90a659420",
"tree_sha": "741c19ba4c17601768b06684d5843d218a72beb1",
"review_commit": "a93c9bf29279f65ce0b9c9fb2c1aeea90a659420",
"reviewer": "planner-reviewer-codex-01",
"job_id": "527f1409",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-09T00:45:03Z"
}
@@ -0,0 +1,126 @@
# Review — 527f1409
## 판정
PASS. 누적 변경에서 차단할 결함을 찾지 못했다. O3의 두 실제 run_loop 실행이 테스트 tmp_path 아래의 marker를 사용하고, 기존 잠금 경합/생성/해제 테스트의 marker는 유지된다. 새 회귀 테스트는 수정 전의 빈 `.mam/` 생성도 탐지한다.
## Review identity
- loop: `loop-20261008T234013Z-4f5d21`
- base: `97c2946ee0e11eabcecafe6407755c33c0289218`
- head: `a93c9bf29279f65ce0b9c9fb2c1aeea90a659420`
- tree: `741c19ba4c17601768b06684d5843d218a72beb1`
- 검증 cwd: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261008T234013Z-4f5d21/1`
- Python: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/loop-marker/.venv/bin/python` — 3.9.6.
- Bash: GNU bash 3.2.57(1)-release, arm64-apple-darwin25.
identity를 확인했고 git status는 깨끗하다. `git diff --check 97c2946 HEAD` 및 변경 Python 파일 3개의 AST 파싱이 통과했다. `.agents/skills`에는 변경이 없다. creator 소스 작업 트리에서 실행/수정하지 않았고 검증 트리의 코드도 수정하지 않았다.
## 변경 검토
- `tests/test_o3_scoped_guard.py:391`: reexec argv 테스트의 subprocess 환경에 tmp_path/loop-guard-active를 명시한다. 기존 환경은 dict(os.environ)으로 보존하므로 marker만 바뀐다.
- `tests/test_o3_scoped_guard.py:470`: no-freeze 테스트에도 같은 방식으로 임시 marker를 지정한다.
- 수정 대상이 한 모듈의 두 호출이므로 공통 fixture나 전역 autouse 없이 직접 지정한 범위가 적절하다. 다른 O2/O3 테스트가 의도적으로 만든 marker 경로는 건드리지 않는다.
- `tests/test_loop_marker_isolation.py:34`: checkout 복사본에서 실제 관련 테스트들을 nested pytest로 실행한 뒤 `.mam` 디렉터리 자체가 없음을 검사한다. 실행 실패나 skip을 성공으로 취급하지 않는다.
- `tests/test_issue17_workspace_root_isolation.py:71`: 이전의 하위 항목 목록 검사에 디렉터리 부재 검사를 추가한다. 잠금을 해제하면서 marker만 삭제하고 빈 `.mam/`을 남기는 경우까지 잡는다.
## 실제 실행 결과
이전 작업의 샌드박스 제한은 현재 해소되어, 이번에는 **정상 conftest와 실제 로컬 MQTT broker를 그대로 사용했다**. fixture를 제외하거나 ps를 대체하지 않았다.
실행 전 HERDR_*, MAM_*, MQTT_*, SKS_*, WORKSPACE_ROOT 및 상속된 control-plane 경로 환경을 제거했다. HOME은 새 임시 경로로 지정했다. `.mam.env`를 export하거나 내용을 출력하지 않았다. bytecode/cache 쓰기는 비활성화하고 basetemp/로그는 검증 트리 밖에 두었다.
실행 파일(모두 전체 파일):
- `tests/test_o2_race_free_lock.py`
- `tests/test_o3_scoped_guard.py`
- `tests/test_issue17_workspace_root_isolation.py`
- `tests/test_loop_marker_isolation.py`
따라서 변경한 테스트 파일 전부와 브리프의 필수 동반 파일이 포함된다. W1과 L1의 nested pytest도 실제로 완료되었다.
| 조건 | 결과 | 외부 sentinel | 검증 checkout `.mam/` |
|---|---|---|---|
| WORKSPACE_ROOT unset | 57 passed, 47.85s | 0 entries | 실행 전후 없음 |
| WORKSPACE_ROOT=기존 빈 sentinel | 57 passed, 48.29s | 0 entries | 실행 전후 없음 |
O2의 live-holder 거부, 경합 단일 승자, stale 회수, marker 보존, O3의 잠금 생성/해제 및 guard 동작이 통과했다. Python 3.9 및 macOS 기본 Bash 3.2에서 검증했다.
재현 드라이버/로그:
- `/tmp/527f1409-run.py`
- `/tmp/527f1409-unset.log`
- `/tmp/527f1409-sentinel.log`
드라이버는 환경을 정리한 뒤 해당 검증 cwd에서 다음 형태로 실행한다:
```text
<loop-marker venv>/bin/python -m pytest -p no:cacheprovider --basetemp=<외부 임시 경로>/bt -q --tb=short <위 4개 파일>
```
임시 실행 root:
- unset: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/527f1409-unset-77llo387`
- sentinel: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/527f1409-sentinel-p9dxhcge`
각 root의 sentinel 디렉터리가 비어 있음을 실행 후 확인했다.
## 수정 전 실패 확인
`/tmp/527f1409-negative.py`에서 **동일한 새 L1 회귀 함수**를 실행하되, `_copy_checkout`가 만든 외부 임시 복사본의 O3 테스트 파일만 base `97c2946` 버전으로 교체했다. 제품 코드는 base와 head가 동일하다. 원본 검증 트리나 creator 소스는 수정하지 않았다.
절차:
1. 현재 checkout을 임시 디렉터리 아래에 복사한다.
2. 그 복사본의 `tests/test_o3_scoped_guard.py`만 `git show 97c2946:tests/test_o3_scoped_guard.py` 내용으로 바꾼다.
3. 새 L1 함수가 선택한 실제 테스트들을 그대로 실행한다.
4. nested pytest의 성공/통과 수 검사 이후, 최종 `.mam` 부재 assertion에서 실패하는지 확인한다.
실제 결과:
```text
NEGATIVE_ASSERTION a test took the checkout's loop lock (its .mam was created)
COPY_MAM_EXISTS True
COPY_MAM_ENTRIES []
```
즉 테스트 본체가 성공해도 잠금 디렉터리가 남는 기존 결함을 정확히 재현했다. 현재 head에서는 같은 L1이 두 환경 모두 통과한다. 단순 테스트 실행 실패를 negative control 성공으로 센 것이 아니다.
- 로그: `/tmp/527f1409-negative.log`
- negative root: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/527f1409-negative-x1lkngmg`
## 실행 지점 조사 및 제외 근거
직접 `rg`로 run_loop.sh, RUN_LOOP, MAM_LOOP_MARKER, MAM_REAL_ROOT, runner 진입을 교차 검색하고 호출 인자/환경 및 제품 코드의 잠금 시점을 확인했다. 검색 기록은 `/tmp/527f1409-runner-audit.txt`에 있다.
| 후보 파일:라인 | 판정/제외 근거 |
|---|---|
| test_o3_scoped_guard.py:388,472 | checkout 스크립트로 실제 잠금까지 도달. 이번에 tmp marker 지정. |
| test_o3_scoped_guard.py:422 | --help로 잠금 전에 종료. |
| test_o3_scoped_guard.py:164,437 | 추출 함수/잠금 harness는 tmp_path를 REPO_ROOT 또는 MAM_REAL_ROOT/marker로 지정한다. 기존 marker 동작 검사 유지. |
| test_o2_race_free_lock.py:203 | 이미 tmp_path의 경합 marker를 MAM_LOOP_MARKER로 전달. 다른 호출은 loop_lock.sh 자체 또는 bash -n 검사다. |
| test_issue5_phase3_loop.py:163,181 | Python/legacy 두 실제 실행 모두 이미 tmp marker 지정. :151은 --help. |
| test_loop_cli.py:53,98 | session-check 실행은 tmp marker, planner 실행은 `_loop_env`의 tmp marker 및 임시 control root를 사용. 나머지 오류/도움말은 잠금 전 종료. |
| test_issue25_plan_reuse.py:355,365 | 도움말 또는 legacy 옵션 거부. 후자는 이미 tmp marker 지정, marker가 생기지 않음도 검사. |
| test_issue21_worktree_prereqs.py:398 | legacy 옵션 거부 실행에 이미 tmp marker 지정. Python runner 검사들은 임시 control root/fixture 사용. |
| test_issue21_loop_b.py:234 | checkout의 shell 호출은 임시 WORKSPACE_ROOT와 MAM_LOOP_MARKER를 명시. |
| test_issue21_acceptance.py:275,297 | 복사된 임시 main 트리의 shell 실행이며 경합용 임시 marker를 명시. |
| test_issue36_prompt_transport.py:532,539 | 임시 ctl의 복사된 스크립트와 ctl 아래 marker를 사용. :630의 추출 harness도 자기 PWD를 root로 지정. |
| test_issue5_phase4_runtime.py:142,166,183 | 실제 shell 호출은 모두 --help라 잠금 전에 종료. |
| test_o1_rebuttal.py:72,76 | --help 또는 잘못된 --max-rebut 인자로 잠금 전 종료. 나머지는 함수 추출/정적 검사. |
| test_b7_diff_untracked.py:174,189 | run_loop는 텍스트 읽기 또는 bash -n. 실제 수행하는 diff_collect는 루프 잠금을 잡지 않는다. |
| test_deploy_freshness.py:473 | 텍스트에서 export 구문을 확인하는 정적 검사. |
| test_issue5_phase0_contracts.py:481 | 문서/계약 문자열 검사. |
| test_tier4_e2e.py:279,285 | mam_sandbox에 복사된 run_loop 실행이므로 잠금 root도 임시 트리다. |
| test_issue17_workspace_root_isolation.py:56 | W1은 별도 checkout 복사본에서 선택 테스트를 실행. 새 `.mam` 부재 검사로 빈 디렉터리도 탐지. |
| test_loop_marker_isolation.py:34 | L1도 별도 복사본에서 실행하고 그 복사본의 `.mam` 부재를 확인. |
직접 Python `LoopRunner.run()` 진입도 확인했다. runner.py:1241의 run/_run_body는 shell의 mam_acquire_loop_lock를 호출하지 않는다. shell 진입이 실제 잠금을 획득하고 Python의 release_guard는 현재 PID 소유 marker만 해제하는 구조다. 따라서 Python runner의 단순 인스턴스 생성/메서드 mock 검사들을 shell 잠금 획득 누락으로 분류하지 않았다.
새 L1 목록은 일부 잠금 이전 거부 경로도 보수적으로 포함한다. 명시적으로 임시 marker를 이미 쓰는 다른 후보가 L1 목록에 전부 들어가야만 격리가 성립하는 구조는 아니며, 해당 제외 사유를 위에서 확인했다. 추가로 수정해야 할 checkout 잠금 획득 지점은 발견하지 못했다.
## Lifecycle
요청된 started 명령을 먼저 실행했고 exit 0으로 완료되었다. 정상 heartbeat 절차를 사용했다. 보고서 작성 뒤 요청된 completed 명령을 실행한다.
[VERDICT: PASS]
@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261008T234013Z-4f5d21",
"base_sha": "97c2946ee0e11eabcecafe6407755c33c0289218",
"head_sha": "a93c9bf29279f65ce0b9c9fb2c1aeea90a659420",
"tree_sha": "741c19ba4c17601768b06684d5843d218a72beb1",
"review_commit": "a93c9bf29279f65ce0b9c9fb2c1aeea90a659420",
"reviewer": "reviewer-agy-01",
"job_id": "c2b1b804",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-09T00:45:03Z"
}
@@ -0,0 +1,131 @@
# PR Review Report: Loop Marker Isolation (Prevent Tests Taking Checkout Loop Lock)
- **Reviewer Agent**: `agy` (session: `herdr:reviewer-agy-01`)
- **Job ID**: `c2b1b804`
- **Role**: Reviewer
- **Target Branch**: `test/loop-marker-tmp`
- **Verification Directory**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261008T234013Z-4f5d21/1`
- **Base Commit**: `97c2946ee0e11eabcecafe6407755c33c0289218` (main)
- **Head Commit**: `a93c9bf29279f65ce0b9c9fb2c1aeea90a659420`
- **Tree SHA**: `741c19ba4c17601768b06684d5843d218a72beb1`
- **Cumulative Commits**:
- `a93c9bf`: `test(loop-lock): the b13 runs of the checkout's run_loop.sh take a loop lock of their own`
---
## 1. Executive Summary
When running tests from the repository root, `tests/test_o3_scoped_guard.py`'s `test_b13_reexec_preserves_original_argv` and `test_b13_no_freeze_switch_disables_reexec` previously invoked the checkout's real `run_loop.sh` with `cwd=REPO_ROOT` without specifying `MAM_LOOP_MARKER`. Because `run_loop.sh` derives `MAM_REAL_ROOT=REPO_ROOT` and defaults `MAM_LOOP_MARKER="${MAM_LOOP_MARKER:-$MAM_REAL_ROOT/.mam/loop-guard-active}"`, `mam_acquire_loop_lock` executed `mkdir -p "$(dirname "$MAM_LOOP_MARKER")"`, creating `.mam/` in the real checkout and briefly acquiring the live loop guard.
Commit `a93c9bf` surgically resolves this issue:
1. `tests/test_o3_scoped_guard.py`: Sets `MAM_LOOP_MARKER=str(tmp_path / "loop-guard-active")` for both `b13` tests.
2. A comprehensive audit across all test files confirms no other test invokes the checkout's `run_loop.sh` past argument parsing without an isolated marker or sandbox.
3. Added `tests/test_loop_marker_isolation.py:test_l1_no_test_takes_the_loop_lock_of_its_checkout` which copies the checkout, runs all tests reaching the lock, and asserts no `.mam/` is created in the copy.
4. Added an explicit `assert not (copy / ".mam").exists()` check to `tests/test_issue17_workspace_root_isolation.py:test_w1`.
5. Executed all target and related suites under both (a) unset `WORKSPACE_ROOT` and (b) `WORKSPACE_ROOT=<sentinel>`: 57 passed, 0 failed, with the sentinel directory remaining 100% empty and no `.mam/` left behind in the worktree.
6. Product code (`.agents/skills`) remains 100% untouched.
All review criteria and task requirements are satisfied. The final verdict is **PASS**.
---
## 2. Evaluation of Task Requirements
| Requirement | Description | Status | Details |
|---|---|---|---|
| **1. Comprehensive Audit of `run_loop.sh` Tests** | Investigate all candidate tests invoking `run_loop.sh`. Verify whether they execute the checkout's script past argument parsing without custom marker/sandbox. | **PASS** | Complete audit conducted across all 16 candidate test modules (detailed in Section 3). Only the two `b13` tests in `test_o3_scoped_guard.py` were unprotected. |
| **2. Temporary Marker Isolation** | Point `MAM_LOOP_MARKER` under `tmp_path` for affected tests while preserving deliberate lock tests and avoiding harmful global/autouse fixtures. | **PASS** | Directly specified `MAM_LOOP_MARKER=str(tmp_path / "loop-guard-active")` in `test_b13_reexec_preserves_original_argv` and `test_b13_no_freeze_switch_disables_reexec`. Deliberate lock test suites (`test_o2_race_free_lock.py`, etc.) retain their intended marker semantics without interference. |
| **3. Regression Tests** | Add a regression test running candidate tests in a clean copy of the checkout and asserting no `.mam/` is created. Verify failure prior to fix. | **PASS** | Added `tests/test_loop_marker_isolation.py` (`test_l1`), and added `.mam` absence assertion in `tests/test_issue17_workspace_root_isolation.py:test_w1`. At base commit `97c2946`, `test_b13` created `$REPO_ROOT/.mam` on lock acquisition, causing `test_l1` to fail. |
| **4. Suite & Sentinel Verification** | Run modified test files + `test_o2_race_free_lock.py`, `test_o3_scoped_guard.py`, `test_issue17_workspace_root_isolation.py` under (a) unset `WORKSPACE_ROOT` and (b) `WORKSPACE_ROOT=<sentinel>`, verifying sentinel is empty and no `.mam/` exists in tree. | **PASS** | Both runs (a) and (b) passed completely (57/57 passed each). Sentinel directory remained 100% empty (`find "$SENTINEL" -type f` returned 0 files), and verification tree has no `.mam/` directory. |
---
## 3. Comprehensive Audit Table of `run_loop.sh` Candidate Modules
| Test File | Reference / Invocation | Reaches Loop Lock? | Exclusion / Remediation Rationale |
|---|---|---|---|
| [`tests/test_b7_diff_untracked.py`](file:///tests/test_b7_diff_untracked.py#L174) | Lines 174, 189: `read_text()`, `bash -n` | No | Static code inspection and bash syntax check only; does not execute `run_loop.sh`. |
| [`tests/test_deploy_freshness.py`](file:///tests/test_deploy_freshness.py#L473) | Line 473: `read_text()` | No | Static text inspection of `MAM_ENV_FILE` export; does not execute script. |
| [`tests/test_issue17_workspace_root_isolation.py`](file:///tests/test_issue17_workspace_root_isolation.py#L25) | Runs tests in `tmp_path / "checkout"` copy | Yes (in copy) | Runs isolated nodes in a temporary checkout copy; protected by test-level markers in the sub-tests. |
| [`tests/test_issue21_acceptance.py`](file:///tests/test_issue21_acceptance.py#L298) | Line 298: subprocess call | Yes | Already sets `env["MAM_LOOP_MARKER"] = str(marker)` where `marker` is in `tmp_path`. |
| `tests/test_issue21_loop_b.py` | N/A | No | File does not exist / references removed in prior refactors. |
| [`tests/test_issue21_worktree_prereqs.py`](file:///tests/test_issue21_worktree_prereqs.py#L405) | Line 405: `test_t2_8` | Yes | Already sets `"MAM_LOOP_MARKER": str(marker)` where `marker` is in `mam_sandbox`. |
| [`tests/test_issue25_plan_reuse.py`](file:///tests/test_issue25_plan_reuse.py#L367) | Line 355: `--help`; Line 367: `test_p7b` | Yes | `--help` exits before lock; `test_p7b` already sets `"MAM_LOOP_MARKER": str(marker)` in `tmp_path`. |
| [`tests/test_issue36_prompt_transport.py`](file:///tests/test_issue36_prompt_transport.py#L540) | Line 540: `_run_shell_loop`; Line 630: `read_text()` | Yes | Runs inside temporary fixture workspace `ctl` with explicit `"MAM_LOOP_MARKER": ctl / ".mam" / "loop-marker"`. |
| [`tests/test_issue5_phase0_contracts.py`](file:///tests/test_issue5_phase0_contracts.py#L481) | Line 481: `read_text()` | No | Static assertion in baseline text check. |
| [`tests/test_issue5_phase3_loop.py`](file:///tests/test_issue5_phase3_loop.py#L151) | Line 151: `--help`; Lines 165, 183 | Yes | `--help` exits before lock; execution tests already pass `env["MAM_LOOP_MARKER"] = str(tmp_path / "loop-marker")`. |
| [`tests/test_issue5_phase4_runtime.py`](file:///tests/test_issue5_phase4_runtime.py#L142) | Lines 142, 166: `--help` | No | Only executes `--help` to test freeze directory bypass logic; exits before lock acquisition. |
| [`tests/test_loop_cli.py`](file:///tests/test_loop_cli.py#L23) | Line 51: `test_creator_parses...`; others: `--help` / bad args | Yes | Argument errors and `--help` exit before lock; `test_creator_parses...` already sets `"MAM_LOOP_MARKER": str(tmp_path / "loop-guard")`. |
| [`tests/test_o1_rebuttal.py`](file:///tests/test_o1_rebuttal.py#L24) | Line 24: function extraction; Line 166: `read_text()` | No | Extracts helper functions to execute standalone or reads text; never runs `run_loop.sh` main entry point. |
| [`tests/test_o2_race_free_lock.py`](file:///tests/test_o2_race_free_lock.py#L203) | Line 203: `test_o2_12` | Yes | Already sets `run_env["MAM_LOOP_MARKER"] = str(tmp_path / "loop-guard-active")`. |
| [`tests/test_o3_scoped_guard.py`](file:///tests/test_o3_scoped_guard.py#L387) | Lines 387, 471: `test_b13_*` | **Yes (Defect)** | **Remediated**: Added `MAM_LOOP_MARKER=str(tmp_path / "loop-guard-active")` to both invocations. |
| [`tests/test_tier4_e2e.py`](file:///tests/test_tier4_e2e.py#L285) | Line 285: `mam_sandbox` copy | Yes | Runs inside isolated `mam_sandbox` directory tree, not the repository checkout. |
---
## 4. Test Suite Execution & Verification Results
All tests were executed within the verification tree:
`/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261008T234013Z-4f5d21/1`
### 4.1. Run (a): Without WORKSPACE_ROOT (Requirement 4)
```bash
AGENT_PYTHON_BIN=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python \
PYTHONDONTWRITEBYTECODE=1 \
env -u WORKSPACE_ROOT \
/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python \
-m pytest -p no:cacheprovider -q --basetemp=/tmp/pytest-loop-marker-a \
tests/test_loop_marker_isolation.py \
tests/test_o3_scoped_guard.py \
tests/test_o2_race_free_lock.py \
tests/test_issue17_workspace_root_isolation.py
```
- **Exit Code**: `0`
- **Output**: `57 passed in 46.27s`
- **Tree Check**: `.mam` does not exist in the verification directory.
### 4.2. Run (b): With WORKSPACE_ROOT=<sentinel> (Requirement 4)
```bash
SENTINEL=/tmp/sentinel-review-b
mkdir -p "$SENTINEL"
AGENT_PYTHON_BIN=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python \
WORKSPACE_ROOT="$SENTINEL" \
PYTHONDONTWRITEBYTECODE=1 \
/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python \
-m pytest -p no:cacheprovider -q --basetemp=/tmp/pytest-loop-marker-b \
tests/test_loop_marker_isolation.py \
tests/test_o3_scoped_guard.py \
tests/test_o2_race_free_lock.py \
tests/test_issue17_workspace_root_isolation.py
```
- **Exit Code**: `0`
- **Output**: `57 passed in 48.74s`
- **Sentinel Contents**: `find "$SENTINEL" -type f` returned 0 files (100% clean).
- **Tree Check**: `.mam` does not exist in the verification directory.
### 4.3. Auxiliary Related Loop Suites
Executed `test_issue5_phase3_loop.py`, `test_issue5_phase4_runtime.py`, `test_loop_cli.py`, `test_issue25_plan_reuse.py`, `test_o1_rebuttal.py`:
- **Exit Code**: `0`
- **Output**: `53 passed in 44.25s`
---
## 5. Non-Functional & Safety Checks
1. **Surgical Precision**:
- `git diff --stat 97c2946..a93c9bf` shows 3 files changed (+55 lines), strictly confined to `tests/`.
- Zero changes to `.agents/skills`.
2. **Portability**:
- Compatible with macOS, Python 3.9+, and bash 3.2.
- Preserves POSIX shell semantics and subshell environment isolation.
3. **Safety & Cleanliness**:
- No interaction with real `.mam.env`, live daemon, or active sessions.
- Clean git status; follows `test(loop-lock): ...` commit naming convention.
---
## 6. Conclusion & Final Verdict
The changes resolve the checkout loop lock acquisition defect surgically and completely. Tests executing `run_loop.sh` are thoroughly isolated, regression tests verify zero `.mam` creation, and all suites pass with zero regressions.
[VERDICT: PASS]
@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261008T234013Z-4f5d21",
"base_sha": "97c2946ee0e11eabcecafe6407755c33c0289218",
"head_sha": "a93c9bf29279f65ce0b9c9fb2c1aeea90a659420",
"tree_sha": "741c19ba4c17601768b06684d5843d218a72beb1",
"review_commit": "a93c9bf29279f65ce0b9c9fb2c1aeea90a659420",
"reviewer": "reviewer-grok-01",
"job_id": "7eab3b75",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-09T00:45:03Z"
}
@@ -0,0 +1,70 @@
# Review 7eab3b75 — tests must not take the checkout loop lock
Head `a93c9bf29279f65ce0b9c9fb2c1aeea90a659420` (tree `741c19ba4c17601768b06684d5843d218a72beb1`) gives the two checkout `run_loop.sh` runs that reach the lock a marker under the test's `tmp_path`. The new isolation test passes, and the same command without that marker leaves an empty `.mam/` behind.
Snapshot `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261008T234013Z-4f5d21/1` only. Base `97c2946ee0e11eabcecafe6407755c33c0289218` (control-plane main, still that commit, porcelain empty). One commit, `a93c9bf test(loop-lock): the b13 runs of the checkout's run_loop.sh take a loop lock of their own`. Three files, +55, all under `tests/`. `git diff --check` is clean. `.agents/skills` and `VERSIONS.md` are outside the diff. No version bump.
Interpreter: worktree `.venv` Python 3.9.6, bash 3.2.57. The suite set `AGENT_PYTHON_BIN` to that interpreter and put its `bin` first on `PATH`.
## What the commit does
`run_loop.sh:15` sets `MAM_REAL_ROOT` to the checkout that contains the script. `run_loop.sh:181` takes `${MAM_LOOP_MARKER:-$MAM_REAL_ROOT/.mam/loop-guard-active}` after the arguments parse. `loop_lock.sh:66` `mkdir -p`s the marker's parent. The trap at `run_loop.sh:264` removes the marker file on the way out and leaves the directory.
`tests/test_o3_scoped_guard.py:391` and `:470` set `MAM_LOOP_MARKER` to `tmp_path / "loop-guard-active"` for `test_b13_reexec_preserves_original_argv` and `test_b13_no_freeze_switch_disables_reexec`. Both still run the checkout script with `cwd` at the repo root and `--creator nonexistent-agent`. The marker's parent is the test directory, so the `mkdir -p` does not create the checkout's `.mam`.
The lock-behavior tests keep their own paths. `test_o2_12_run_loop_exits_on_lock_failure` (`tests/test_o2_race_free_lock.py:205`) still points `MAM_LOOP_MARKER` at the marker the test is holding. The o3 scripts that call `mam_acquire_loop_lock` directly (`test_z9_*` around `:177`, `:228`, `:266`, `:298`, and `test_b13_release_guard_cleans_up_and_releases_lock` at `:438`) still use `tmp_path` as `REPO_ROOT`. Nothing in `conftest.py` sets the marker, and it is not autouse.
`tests/test_loop_marker_isolation.py` adds L1. It copies this checkout, runs the seven nodes below with `WORKSPACE_ROOT` naming the copy, and requires the copy to have no `.mam` at all. `tests/test_issue17_workspace_root_isolation.py:71` adds the same directory check to W1. W1 already required `.mam` to contain no files; an empty directory left by `mkdir -p` passed that check.
## Survey
A checkout `run_loop.sh` reaches `mam_acquire_loop_lock` (`run_loop.sh:248`) only after argument parsing. `--help` and `usage` exit at `:66`. A missing `--creator`/`--task`, `--target-agent`, `--planner` without `--plan`, a bad `--max-rebut`, and the legacy-shell refusal of the Python-only options (`:136-141`) all exit before the lock. `bash -n` and a `read_text` do not execute it. A script copied under `tmp_path` or `mam_sandbox` has its own `MAM_REAL_ROOT`.
The two `b13` runs were the only executions of this checkout's `run_loop.sh` that got as far as the lock without `MAM_LOOP_MARKER` or a temp `MAM_REAL_ROOT`. The other runs that do reach the lock already name a marker.
| Location | Why this is not the checkout lock |
| --- | --- |
| `test_o3_scoped_guard.py:391`, `:470` | this commit sets `MAM_LOOP_MARKER` under `tmp_path` |
| `test_issue5_phase3_loop.py:163`, `:181` | `MAM_LOOP_MARKER` is `tmp_path / "loop-marker"` |
| `test_loop_cli.py:53` | `MAM_LOOP_MARKER` is `tmp_path / "loop-guard"` |
| `test_loop_cli.py:100` | `_loop_env` sets the marker and `MAM_REAL_ROOT` to `mam_sandbox` |
| `test_o2_race_free_lock.py:205` | the marker is the one the test holds, on purpose |
| `test_issue25_plan_reuse.py:370` | marker is set; `MAM_LOOP_PY=0` plus `--plan-file` exits at `run_loop.sh:140` before acquire |
| `test_issue21_worktree_prereqs.py:404` | marker is set on `mam_sandbox`; the same refusal exits before acquire |
| `test_issue21_loop_b.py:238` | marker and `WORKSPACE_ROOT` are `tmp_path`; the same refusal exits before acquire |
| `test_issue21_acceptance.py:298` | the script is the temp repo's copy, and the marker is set |
| `test_issue36_prompt_transport.py:539` | copied skills tree; marker is `ctl/.mam/loop-marker` |
| `test_tier4_e2e.py:433` | the script lives under `mam_sandbox`, so `MAM_REAL_ROOT` is that sandbox |
| `test_o1_rebuttal.py:72`, `:76` | `--help`, then `--max-rebut abc`; both exit in the parser |
| `test_loop_cli.py:36`, `:45`, `:63`, `:71` | `--help`, missing args, `--target-agent`, `--planner` without `--plan` |
| `test_issue5_phase3_loop.py:151`, `test_issue5_phase4_runtime.py:141`, `test_issue25_plan_reuse.py:355`, `test_o3_scoped_guard.py:422` | `--help` |
| `test_o2_race_free_lock.py:232`, `test_b7_diff_untracked.py:191` | `bash -n` |
| `test_b7_diff_untracked.py:174` | sources `diff_collect.sh`, which sources `lib_int.sh` |
| `test_deploy_freshness.py:473`, `test_issue5_phase0_contracts.py:481`, `test_o3_scoped_guard.py:137` | the file is read as text |
| `test_o3_scoped_guard.py:177` and the sibling z9 scripts | `loop_lock.sh` directly, `REPO_ROOT` is `tmp_path` |
`lib_py/loop/runner.py:1226` releases `MAM_LOOP_MARKER` or `self.mam_dir / "loop-guard-active"`. The acquire lives in `run_loop.sh`. L1's node list also names `test_p7b_the_legacy_shell_loop_refuses_the_option`, which exits before the lock. That node still belongs in the regression: it runs this checkout's script.
## Requirement 3 — the unfixed command leaves `.mam/`
Product `run_loop.sh` is unchanged, so the command `test_b13_no_freeze_switch_disables_reexec` used to run is the pre-fix case. The script tree was a copy of the snapshot's `.agents` under `/tmp/7eabpre`, not the snapshot and not the control-plane checkout. `WORKSPACE_ROOT` pointed at a side directory, so `lib.sh`'s shim was not the thing creating the copy's `.mam`. Inherited `HERDR_*`, `MAM_*`, `MQTT_*`, and `SKS_*` were removed.
Without `MAM_LOOP_MARKER`, `bash run_loop.sh --creator nonexistent-agent --task "test goal"` exited 1 and left `/tmp/7eabpre/old-checkout/.mam/` as an empty directory. The marker file was gone. The shim landed in the side directory (`.mam/shim/herdr`).
The same command with `MAM_LOOP_MARKER` set to `/tmp/7eabpre/own-marker/loop-guard-active` exited 1 and left the second copy with no `.mam`. The marker file was released. The snapshot's `.mam` stayed absent, and the control-plane lock stayed `mtime=1791502813` `size=70`.
## Requirement 4 — suite
`AGENT_PYTHON_BIN` and `PATH` pointed at the worktree `.venv`. Inherited `HERDR_*`, `MAM_*`, `MQTT_*`, `SKS_*`, and `WORKSPACE_ROOT` were cleared in the parent. `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider`. Basetemp directories are `/tmp/7eabA.n4k8Qw` and `/tmp/7eabB.p2m7Lx`. Neither path contains `mam-review`.
The four files are `tests/test_issue17_workspace_root_isolation.py`, `tests/test_loop_marker_isolation.py`, `tests/test_o2_race_free_lock.py`, and `tests/test_o3_scoped_guard.py`.
(a) `WORKSPACE_ROOT` unset: **57 passed** in 47.60s, exit 0.
(b) `WORKSPACE_ROOT=/tmp/7eabsent.t8c3Vr` (the directory existed): **57 passed** in 47.43s, exit 0. The sentinel was empty afterwards.
`pytest_configure` drops a `WORKSPACE_ROOT` whose real path is some other directory, so the outer sentinel staying empty is that drop. L1 and W1 keep a root that names the copied checkout, and both require that copy to have no `.mam`. Both passed inside (a) and (b).
After the pair: snapshot `.mam` absent, source worktree `.mam` absent, snapshot and worktree porcelain empty, control-plane main still `97c2946` with empty porcelain, control-plane `loop-guard-active` still `mtime=1791502813` `size=70`, `~/.config/herdr/config.toml` mtime `1784456394`, directory mtime `1791382452`.
[VERDICT: PASS]
+1 -1
View File
@@ -29,7 +29,7 @@ export WORKSPACE_ROOT
# Framework semantic version. Single runtime source of truth; kept in lockstep # 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. # 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. # NOTE: unlike other MAM_* variables this one is intentionally NOT env-overridable.
MAM_VERSION="4.8.8" MAM_VERSION="4.8.9"
export MAM_VERSION export MAM_VERSION
AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}" AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}"
+1 -1
View File
@@ -13,7 +13,7 @@ import sys
from typing import List, Sequence from typing import List, Sequence
# Framework version locked with framework constants # Framework version locked with framework constants
FRAMEWORK_VERSION = "4.8.8" FRAMEWORK_VERSION = "4.8.9"
CORE_MODULES: Sequence[str] = ( CORE_MODULES: Sequence[str] = (
# Standard library modules # Standard library modules
@@ -1,7 +1,7 @@
--- ---
name: multi-agent-mux-create 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." 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.8.8 version: 4.8.9
author: godopu author: godopu
license: MIT license: MIT
platforms: [linux, macos] platforms: [linux, macos]
@@ -1,7 +1,7 @@
--- ---
name: multi-agent-mux-delegate-job 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." 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.8.8 version: 4.8.9
author: godopu author: godopu
license: MIT license: MIT
platforms: [linux, macos, windows] platforms: [linux, macos, windows]
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
name: multi-agent-mux-loop 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." 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.8.8 version: 4.8.9
author: godopu author: godopu
license: MIT license: MIT
platforms: [linux, macos] platforms: [linux, macos]
@@ -1,7 +1,7 @@
--- ---
name: multi-agent-mux-monitor 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." 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.8.8 version: 4.8.9
author: godopu author: godopu
license: MIT license: MIT
platforms: [linux, macos] platforms: [linux, macos]
@@ -1,7 +1,7 @@
--- ---
name: multi-agent-mux-orc-onboard 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." description: "Register current or specified orchestrator session UUID into agent-sessions.yaml orchestrator_uuids list to prevent sub-agent discovery capture."
version: 4.8.8 version: 4.8.9
author: godopu author: godopu
license: MIT license: MIT
platforms: [linux, macos] platforms: [linux, macos]
@@ -1,7 +1,7 @@
--- ---
name: multi-agent-mux-resume 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." 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.8.8 version: 4.8.9
author: godopu author: godopu
license: MIT license: MIT
platforms: [linux, macos] platforms: [linux, macos]
@@ -1,7 +1,7 @@
--- ---
name: multi-agent-mux-status 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." 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.8.8 version: 4.8.9
author: godopu author: godopu
license: MIT license: MIT
platforms: [linux, macos] platforms: [linux, macos]
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
name: multi-agent-mux-stop 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." 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.8.8 version: 4.8.9
author: godopu author: godopu
license: MIT license: MIT
platforms: [linux, macos] platforms: [linux, macos]
+1 -1
View File
@@ -2,7 +2,7 @@
본 문서는 `herdr_agent_orchestration` 오케스트레이션 및 메시징 백플레인 워크플로우를 새로운 프로젝트에 도입하여 이식하고, 새로운 개발자/에이전트가 초기 가동을 시작할 때 수행해야 하는 환경 설정 및 구축 절차를 안내합니다. 본 문서는 `herdr_agent_orchestration` 오케스트레이션 및 메시징 백플레인 워크플로우를 새로운 프로젝트에 도입하여 이식하고, 새로운 개발자/에이전트가 초기 가동을 시작할 때 수행해야 하는 환경 설정 및 구축 절차를 안내합니다.
[![Version: v4.8.8](https://img.shields.io/badge/version-v4.8.8-blue.svg)](VERSIONS.md) [![Version: v4.8.9](https://img.shields.io/badge/version-v4.8.9-blue.svg)](VERSIONS.md)
새로운 에이전트는 이 안내서에 기술된 절차를 순차적으로 실행하여 초기 환경을 안정적으로 설정할 수 있습니다. 새로운 에이전트는 이 안내서에 기술된 절차를 순차적으로 실행하여 초기 환경을 안정적으로 설정할 수 있습니다.
+1 -1
View File
@@ -2,7 +2,7 @@
This document guides you through the setup and initialization procedures required to adopt the `herdr_agent_orchestration` orchestration and messaging backplane workflow in a new project, enabling a new developer or agent to get up and running quickly. This document guides you through the setup and initialization procedures required to adopt the `herdr_agent_orchestration` orchestration and messaging backplane workflow in a new project, enabling a new developer or agent to get up and running quickly.
[![Version: v4.8.8](https://img.shields.io/badge/version-v4.8.8-blue.svg)](VERSIONS.md) [![Version: v4.8.9](https://img.shields.io/badge/version-v4.8.9-blue.svg)](VERSIONS.md)
A new agent can follow the steps in this guide sequentially to establish a stable and reliable initial environment. A new agent can follow the steps in this guide sequentially to establish a stable and reliable initial environment.
+1 -1
View File
@@ -1,6 +1,6 @@
# Messaging System Technical Analysis & Architecture Report # Messaging System Technical Analysis & Architecture Report
Applies to: MAM v4.8.8 Applies to: MAM v4.8.9
This report provides a comprehensive, deep-dive analysis of the messaging system implemented in the `multi-agent-mux-delegate-job` skill. It covers the MQTT broker architecture, event protocols, job lifecycles, codebase internals, cross-system integration, and a list of known limitations along with production recommendations. This report provides a comprehensive, deep-dive analysis of the messaging system implemented in the `multi-agent-mux-delegate-job` skill. It covers the MQTT broker architecture, event protocols, job lifecycles, codebase internals, cross-system integration, and a list of known limitations along with production recommendations.
+1 -1
View File
@@ -2,7 +2,7 @@
Herdr와 MQTT 브로커를 기반으로 구축된 고신뢰성 **다중 에이전트 오케스트레이션 및 메시징 백플레인** 프레임워크입니다. `claude`, `agy`, `hermes`, `grok`, `opencode`, `codex` 등 다중 LLM 백엔드 에이전트 전반에 걸쳐 장시간 수행되는 작업(코드 생성, 리팩토링, 보안 검토 등)을 조정, 격리 및 감사할 수 있도록 설계되었습니다. Herdr와 MQTT 브로커를 기반으로 구축된 고신뢰성 **다중 에이전트 오케스트레이션 및 메시징 백플레인** 프레임워크입니다. `claude`, `agy`, `hermes`, `grok`, `opencode`, `codex` 등 다중 LLM 백엔드 에이전트 전반에 걸쳐 장시간 수행되는 작업(코드 생성, 리팩토링, 보안 검토 등)을 조정, 격리 및 감사할 수 있도록 설계되었습니다.
[![Version: v4.8.8](https://img.shields.io/badge/version-v4.8.8-blue.svg)](VERSIONS.md) [![Version: v4.8.9](https://img.shields.io/badge/version-v4.8.9-blue.svg)](VERSIONS.md)
--- ---
+1 -1
View File
@@ -2,7 +2,7 @@
An advanced, high-reliability **Multi-Agent Orchestration & Messaging Backplane** framework built on Herdr and MQTT. It is designed to coordinate, isolate, and audit long-running agent tasks (such as code generation, refactoring, and security reviews) across multiple LLM backend clients (`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`). An advanced, high-reliability **Multi-Agent Orchestration & Messaging Backplane** framework built on Herdr and MQTT. It is designed to coordinate, isolate, and audit long-running agent tasks (such as code generation, refactoring, and security reviews) across multiple LLM backend clients (`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`).
[![Version: v4.8.8](https://img.shields.io/badge/version-v4.8.8-blue.svg)](VERSIONS.md) [![Version: v4.8.9](https://img.shields.io/badge/version-v4.8.9-blue.svg)](VERSIONS.md)
--- ---
+23 -10
View File
@@ -6,7 +6,7 @@
## 📌 현재 버전 개요 (Current Release) ## 📌 현재 버전 개요 (Current Release)
- **프레임워크 버전**: `v4.8.8` - **프레임워크 버전**: `v4.8.9`
- **최신 릴리스 일시**: 2026-10-09 (KST) - **최신 릴리스 일시**: 2026-10-09 (KST)
- **기준 브랜치**: `main` - **기준 브랜치**: `main`
- **핵심 아키텍처**: - **핵심 아키텍처**:
@@ -24,23 +24,36 @@
## 🧭 스킬 패키지 버전 매트릭스 (Skills Version Matrix) ## 🧭 스킬 패키지 버전 매트릭스 (Skills Version Matrix)
모든 8개 스킬은 YAML frontmatter 메타데이터(`author`, `version`, `platforms`, `environments`) 표준화를 통해 `v4.8.8`로 동기화되어 배포됩니다. 모든 8개 스킬은 YAML frontmatter 메타데이터(`author`, `version`, `platforms`, `environments`) 표준화를 통해 `v4.8.9`로 동기화되어 배포됩니다.
| 스킬명 | 버전 | 역할 및 주요 책임 | 상태 | | 스킬명 | 버전 | 역할 및 주요 책임 | 상태 |
| :--- | :---: | :--- | :---: | | :--- | :---: | :--- | :---: |
| **`multi-agent-mux-create`** | `4.8.8` | 에이전트 세션 신규 생성 및 Herdr 컨테이너 격리 스폰 | ✅ 배포 | | **`multi-agent-mux-create`** | `4.8.9` | 에이전트 세션 신규 생성 및 Herdr 컨테이너 격리 스폰 | ✅ 배포 |
| **`multi-agent-mux-stop`** | `4.8.8` | 대화 UUID 원자적 캡처 및 세션 안전 종료 (Graceful Stop) | ✅ 배포 | | **`multi-agent-mux-stop`** | `4.8.9` | 대화 UUID 원자적 캡처 및 세션 안전 종료 (Graceful Stop) | ✅ 배포 |
| **`multi-agent-mux-resume`** | `4.8.8` | 온디스크 대화 컨텍스트 기반 Tier-1 초고속 세션 복원 | ✅ 배포 | | **`multi-agent-mux-resume`** | `4.8.9` | 온디스크 대화 컨텍스트 기반 Tier-1 초고속 세션 복원 | ✅ 배포 |
| **`multi-agent-mux-status`** | `4.8.8` | 실시간 Herdr 세션 및 레지스트리 드리프트 스냅샷 조회 | ✅ 배포 | | **`multi-agent-mux-status`** | `4.8.9` | 실시간 Herdr 세션 및 레지스트리 드리프트 스냅샷 조회 | ✅ 배포 |
| **`multi-agent-mux-monitor`** | `4.8.8` | YAML ↔ 런타임 상태 간 자율 조정자 (Reconciler Loop) | ✅ 배포 | | **`multi-agent-mux-monitor`** | `4.8.9` | YAML ↔ 런타임 상태 간 자율 조정자 (Reconciler Loop) | ✅ 배포 |
| **`multi-agent-mux-delegate-job`** | `4.8.8` | MQTT 이벤트 채널 기반 비동기 단위 작업 위임 | ✅ 배포 | | **`multi-agent-mux-delegate-job`** | `4.8.9` | MQTT 이벤트 채널 기반 비동기 단위 작업 위임 | ✅ 배포 |
| **`multi-agent-mux-loop`** | `4.8.8` | Planner-Creator-Reviewer 3자 자율 계획·실행·피어리뷰 루프 | ✅ 배포 | | **`multi-agent-mux-loop`** | `4.8.9` | Planner-Creator-Reviewer 3자 자율 계획·실행·피어리뷰 루프 | ✅ 배포 |
| **`multi-agent-mux-orc-onboard`** | `4.8.8` | 오케스트레이터 UUID 격리 등록 및 서브 세션 오염 방지 | ✅ 배포 | | **`multi-agent-mux-orc-onboard`** | `4.8.9` | 오케스트레이터 UUID 격리 등록 및 서브 세션 오염 방지 | ✅ 배포 |
--- ---
## 📋 버전별 상세 변경 내역 (Changelog) ## 📋 버전별 상세 변경 내역 (Changelog)
### 🚀 `v4.8.9` — 테스트가 체크아웃의 실제 루프 잠금을 잡지 않는다 (2026-10-09)
> **패치 (PATCH Release, 테스트 전용)**: v4.8.8 센티널 실행 뒤 리뷰 워크트리에 빈 `.mam/` 이 남은 원인. 제품 코드(`.agents/skills`)는 버전 줄 외에 바뀌지 않았다.
#### 🌟 주요 개선 사항
1. **원인**: `tests/test_o3_scoped_guard.py` 의 `test_b13_reexec_preserves_original_argv`·`test_b13_no_freeze_switch_disables_reexec` 는 체크아웃의 `run_loop.sh` 를 `--creator` 와 함께 실행해 인자 해석을 지나 `mam_acquire_loop_lock` 까지 간다. 잠금 위치 기본값은 `$MAM_REAL_ROOT/.mam/loop-guard-active` 이고 `MAM_REAL_ROOT` 는 스크립트 자신의 체크아웃이라 `WORKSPACE_ROOT` 를 따르지 않는다. 세션이 없어 곧 끝나며 marker 는 지우지만 `.mam/` 은 남는다. 본 체크아웃에서는 그 순간 실제 루프의 잠금을 잡았다 놓는다(실제 루프가 거부되거나 테스트가 거부됨).
2. **수정**: 두 테스트가 `MAM_LOOP_MARKER` 를 자기 `tmp_path` 아래로 둔다. `run_loop.sh` 를 쓰는 16 개 모듈을 테스트마다 체크아웃의 새 `.mam` 을 감시하며 훑었고, 나머지는 이미 marker 를 지정하거나 잠금 전에 끝나거나(`--help`, 인자 오류) 복사본 트리를 실행한다. 잠금 동작을 시험하는 테스트(`test_o2_race_free_lock` 등)는 그대로다.
#### ✅ 품질 검증
- **회귀 테스트**: `tests/test_loop_marker_isolation.py` L1 — 체크아웃의 `run_loop.sh` 로 잠금까지 가는 테스트들을 체크아웃 복사본에서 돌려 `.mam` 이 생기지 않아야 한다. v4.8.8 의 W1(`tests/test_issue17_workspace_root_isolation.py`)은 파일만 보아 빈 잠금 디렉터리를 놓쳤으므로 이제 `.mam` 자체가 없어야 한다. 97c2946 에서 둘 다 실패.
- **리뷰 (병합 워크스페이스, 루프 `--all-reviewer`)**: 1회차(@a93c9bf) **4/4 PASS**. 리포트와 identity 사이드카는 `.agents/reports/<session>/`.
- **전체 스위트 (병합 워크스페이스, macOS·Python 3.9·herdr 0.9.3, MQTT 환경변수 없이, `WORKSPACE_ROOT` = 빈 센티널)**: a93c9bf **1752 passed / 0 failed / 38 skipped**(73 분). 실행 뒤 센티널은 비었고, 리뷰 워크트리에 `.mam/` 이 없었고, 본 체크아웃의 `.mam/shim/herdr` 수정 시각은 그대로였다.
### 🚀 `v4.8.8` — Issue #17 후속: lib.sh 를 source 하는 테스트가 상속된 `WORKSPACE_ROOT` 에 쓰지 않는다 (2026-10-09) ### 🚀 `v4.8.8` — Issue #17 후속: lib.sh 를 source 하는 테스트가 상속된 `WORKSPACE_ROOT` 에 쓰지 않는다 (2026-10-09)
> **패치 (PATCH Release, 테스트 전용)**: v4.8.7 리뷰(claude 비차단)가 남긴 후속. 제품 코드(`.agents/skills`)는 바뀌지 않았다. lib.sh 는 source 될 때마다 `$WORKSPACE_ROOT/.mam/shim/herdr` 를 다시 쓰는데(`_init_herdr_isolation`), 셸 환경을 그대로 물려받는 테스트는 그 값이 가리키는 실제 워크스페이스(에이전트 pane 에서는 그 pane 의 워크스페이스, PR #51 리뷰 때는 다른 워크스페이스)의 shim 을 덮어썼다. > **패치 (PATCH Release, 테스트 전용)**: v4.8.7 리뷰(claude 비차단)가 남긴 후속. 제품 코드(`.agents/skills`)는 바뀌지 않았다. lib.sh 는 source 될 때마다 `$WORKSPACE_ROOT/.mam/shim/herdr` 를 다시 쓰는데(`_init_herdr_isolation`), 셸 환경을 그대로 물려받는 테스트는 그 값이 가리키는 실제 워크스페이스(에이전트 pane 에서는 그 pane 의 워크스페이스, PR #51 리뷰 때는 다른 워크스페이스)의 shim 을 덮어썼다.