From ffe8665b2779e588f0e8c49f573e8b71ce62fb26 Mon Sep 17 00:00:00 2001 From: Godopu Date: Mon, 12 Oct 2026 21:38:50 +0900 Subject: [PATCH] release(v4.9.0): Issue #50 / #55 - archive the review reports and bump the lockstep version Merge review on macOS (--review-only loops, --all-reviewer): round 1 at 320aa9f 4/4 NOT PASS, round 2 codex F1 (a claimed dry-run job holds its session), round 3 4/4 PASS (discarded: the worktree changed mid-review), round 4 at 21f0c12 claude/agy/grok PASS with codex interrupted by a stream disconnect, round 5 codex PASS. Full suite at 21f0c12 on macOS / Python 3.9 / bash 3.2 / herdr 0.9.3: 1860 passed / 0 failed / 38 skipped; no new entries in ~/.claude/projects. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff --- .../report-ae9c1f28.md | 42 +++++++++ .../report-7ac94ed6.identity.json | 14 +++ .../report-7ac94ed6.md | 59 ++++++++++++ .../reviewer-agy-01/report-05f28276.md | 92 +++++++++++++++++++ .../reviewer-grok-01/report-51507ec8.md | 36 ++++++++ .agents/skills/lib.sh | 2 +- .agents/skills/lib_py/loop/eager.py | 2 +- .../skills/multi-agent-mux-create/SKILL.md | 2 +- .../multi-agent-mux-delegate-job/SKILL.md | 2 +- .agents/skills/multi-agent-mux-loop/SKILL.md | 2 +- .../skills/multi-agent-mux-monitor/SKILL.md | 2 +- .../multi-agent-mux-orc-onboard/SKILL.md | 2 +- .../skills/multi-agent-mux-resume/SKILL.md | 2 +- .../skills/multi-agent-mux-status/SKILL.md | 2 +- .agents/skills/multi-agent-mux-stop/SKILL.md | 2 +- BOOTSTRAP.ko.md | 2 +- BOOTSTRAP.md | 2 +- MESSAGING.md | 2 +- README.ko.md | 2 +- README.md | 2 +- VERSIONS.md | 37 +++++--- 21 files changed, 284 insertions(+), 26 deletions(-) create mode 100644 .agents/reports/planner-reviewer-claude-01/report-ae9c1f28.md create mode 100644 .agents/reports/planner-reviewer-codex-01/report-7ac94ed6.identity.json create mode 100644 .agents/reports/planner-reviewer-codex-01/report-7ac94ed6.md create mode 100644 .agents/reports/reviewer-agy-01/report-05f28276.md create mode 100644 .agents/reports/reviewer-grok-01/report-51507ec8.md diff --git a/.agents/reports/planner-reviewer-claude-01/report-ae9c1f28.md b/.agents/reports/planner-reviewer-claude-01/report-ae9c1f28.md new file mode 100644 index 0000000..6cf4b38 --- /dev/null +++ b/.agents/reports/planner-reviewer-claude-01/report-ae9c1f28.md @@ -0,0 +1,42 @@ +# PR #56 4차 재리뷰 — 수정 커밋 21f0c12 (job ae9c1f28) + +- 리뷰어: planner-reviewer-claude-01 (Reviewer) +- 리뷰 identity: loop-20261012T013821Z-656dfe / base efd27def .. head 21f0c12b (커밋 1개, 2 파일) / tree c2357ac0 +- 검증 경로: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261012T013821Z-656dfe/1` — HEAD·tree 가 identity 와 일치, `git status --porcelain` 0 줄(시작·종료 시). 본 체크아웃 HEAD 42e5173(main) 변동 없음. +- 실행: 워크트리 venv python 3.9.6, `env -i` 로 HERDR_*/MAM_*/MQTT_*/WORKSPACE_ROOT/CLAUDE_PROJECT_DIR/HOME_DIR/CODEX_HOME 을 비우고 호스트의 긴 TMPDIR 은 그대로. `.mam.env` 는 읽지 않았다. 범위는 이 커밋만. + +## 1. 전제 확인 — 'default' 스캔은 정말 HERDR_SESSION_NAME 의 서버를 보는가 (확인할 것 1) + +**코드.** reconcile 의 스캔은 `subprocess.run(['herdr', ...])` 로 PATH 의 `herdr` 를 부른다(`reconcile.sh:505-510`). lib.sh 를 source 하면 `$WORKSPACE_ROOT/.mam/shim` 이 PATH 맨 앞에 들어가고(`lib.sh:144-147`), create_session.sh 는 lib.sh 를 source 한 채 `reconcile.sh --once` 를 자식으로 띄우므로(`create_session.sh:748`) 그 reconcile 의 `herdr` 는 shim 이고 `HERDR_SESSION_NAME`(= create 의 서버, 워크스페이스 슬러그)을 물려받는다. shim 은 `-L X` 를 `HERDR_SESSION_NAME=X` 로 바꾸고(`lib.sh:184-190`), 실제 바이너리를 부를 때 `HERDR_SESSION_NAME` 이 비어 있지 않고 `default` 가 아니면 `--session <그 값>` 을 앞에 붙인다(`lib.sh:270-283`; `ls` 경로의 python 도 `MAM_LS_SESSION` 으로 같은 값을 씀, `:1256-1318`). 따라서 `-L` 없는 'default' 스캔 = `herdr --session ls` 이다. 전제 성립. + +**실서버(herdr 0.9.3, 일회용 HOME `/tmp/ae9c-h*`, 일회용 서버 이름).** +- lib.sh 를 source 한 셸에서 `HERDR_SESSION_NAME=t-ae9c-srv` 로 shim `herdr ls -F …`(`-L` 없음) → rc 0. 그 HOME 아래 생긴 서버는 `.config/herdr/sessions/t-ae9c-srv/` 하나뿐이고 default 소켓 `.config/herdr/herdr.sock` 은 생기지 않았다 → 'default' 스캔이 환경변수의 서버로 갔다. `-L t-ae9c-srv ls` 도 rc 0(같은 서버). 서버는 `--session t-ae9c-srv server stop` 으로 내리고 HOME 제거, 잔류 프로세스 0. +- 참고로 **원 바이너리는 `HERDR_SESSION_NAME` 을 모른다**: `/opt/homebrew/bin/herdr status server` 는 `HERDR_SESSION_NAME=t-ae9c-x` 에서 default 소켓(`…/herdr.sock`)을, `HERDR_SESSION=t-ae9c-y` 와 `--session t-ae9c-z` 에서 `sessions/<이름>/herdr.sock` 을 가리켰다. 즉 전제는 shim 을 거칠 때만 성립하는데, reconcile 은 위와 같이 shim 을 거친다(그리고 lib.sh 를 source 한 셸에서는 `herdr` 자체가 함수다). 수정이 "HERDR_SESSION_NAME 이 비어 있지 않고 default 가 아닐 때" 로 조건을 건 것은 `_real_herdr` 의 조건(`:271-274`)과 정확히 같은 경계다. + +## 2. 변경 자체 (`reconcile.sh:659-666`) +- drift B 에서만, `srv == 'default'` 이고 ambient 가 유효할 때 `srv` 를 ambient 로 바꾼다. 이후의 `server_opt`, `herdr_session`/`herdr_server`, `start_command`/`attach_command`/`kill_command` 가 모두 그 `srv` 를 쓰므로 행이 일관되게 "답한 서버" 로 적힌다. `srv` 는 루프 머리(`:607`)에서 매 반복 `t['server']` 로 다시 읽히므로 다음 항목으로 새지 않는다. 스캔(`:505`)·drift A(`:594-595`)·`pane_meta(name, srv)`(`:650`, 스캔한 라벨로 호출 — 같은 ambient 로 답하므로 동일 서버) 는 그대로다. +- bash 변경 없음(`bash -n` 통과), python 3.9 호환. +- `tests/data/skill_code_blocks.json`: `create_session.sh:550` 이 실제로 `raise SystemExit(4)` 임을 확인(`sed -n 550p`), `test_issue42_skill_blocks` 통과. + +## 3. 부작용 검토 (확인할 것 2) +- **다음 reconcile 에서 drift A 로 오판되지 않는가.** 행의 서버가 X(ambient)로 적히면 다음 reconcile 은 그 행에서 X 를 `unique_servers` 에 넣고(`:499-502`) `-L X` 로 스캔하므로(`:505-507`) `alive_set` 에 `(name, X)` 가 들어가고(`:581`), drift A 의 비교 `(name, X)`(`:594-595`)가 맞는다 — 데몬의 환경(HERDR_SESSION_NAME 유무·다른 값)과 무관하다. 같은 create 환경에서는 'default' 스캔도 X 로 답해 `(name,'default')` 까지 더해지지만 비교에 쓰이지 않을 뿐 해가 없다. scenario4 가 바로 이 경우다: 같은 폴더 6 개 create 가 각자 `reconcile --once` 를 띄워 서로의 pane 을 보며, 6 행이 모두 살아남아야 한다 → 통과(아래). +- **기존 'default' 행·HERDR_SESSION_NAME 없는 데몬.** 조건이 ambient 가 비어 있거나 'default' 면 발동하지 않으므로(`:665`) 종전과 같은 'default' 라벨이 적힌다. 이미 'default' 로 적힌 행의 drift A 판정은 이 커밋이 건드리지 않았다. +- **같은 pane 이 'default' 스캔과 X 스캔에 두 번 보일 때.** `herdr_sessions` 에는 `(name,'default')`·`(name,X)` 두 항목이 들어오지만 drift B 는 이름 기준으로 건너뛴다: `any(_sanitize(y) == name for y in yaml_session_names)`(`:610`) 와 등록 직후의 `yaml_session_names.add(name)`(`:715`). 어느 항목이 먼저 오든(정렬된 서버 순) 첫 항목이 X 라벨로 등록되고 두 번째는 건너뛴다 → 중복 없음. 두 경로 모두 라벨은 X 로 같다(`-L X` 는 `srv=X`, 'default' 는 ambient 로 교정). +- **`HERDR_SERVER_NAME` 만 있는 경우.** `unique_servers` 는 그 값을 넣지만(`:497-498`) 라벨 교정은 `HERDR_SESSION_NAME` 만 본다. shim 의 라우팅(`_real_herdr`)도 `HERDR_SESSION_NAME` 만 보므로 그때의 'default' 스캔은 실제로 default 서버를 본다 — 라벨 'default' 가 맞다. 일관됨. +- 비차단 메모(기존 코드): `:610` 의 `(name, srv) in yaml_session_names` 는 이름 집합에 튜플을 넣어 보는 비교라 항상 거짓이고, 실제 중복 방지는 뒤의 `any(...)` 가 한다. 동작에는 영향 없음. + +## 4. 테스트 (확인할 것 3) + +| 항목 | 결과 | +|---|---| +| head: `test_tier4_e2e.py::test_e2e_scenario4_parallel_flock_locking` + `test_issue50_pane_lock.py` + `test_issue42_skill_blocks.py` + `test_issue9_server_env.py` + `test_b19_headless_reconcile_fixes.py` + `test_tier1_unit.py` + `test_tier2_component.py` + `test_workspace_scope.py` + `test_b4_session_created.py` | **212 passed, 1 skipped** (464 s) | +| head: `test_issue50_dispatch_herdr.py` (실서버 16 케이스, a3 = 같은 폴더 3 개 동시 create 포함) | **16 passed** (548 s) | +| 수정 전 대조군: `git archive HEAD` 복사본에 reconcile.sh 만 efd27de 로 되돌리고 scenario4 | **실패** — `ERROR: parallel-sess-3-creator-claude already running in agent-sessions.yaml`, `assert 4 == 0` (브리프의 증상 그대로 재현) | + +## 5. 잔류물 +- 일회용 HOME `/tmp/ae9c-h`, `/tmp/ae9c-h2`, `/tmp/ae9c-h3` 제거, 일회용 서버 `t-ae9c-srv` 정지(잔류 프로세스 0). `/tmp/mam50-*` 박스 0, 실험 herdr 서버 0. 검증 경로에는 `.mam` 조차 생기지 않았고 `git status` 깨끗. scratch 복사본·basetemp·프로브 스크립트는 세션 scratchpad 아래. 실제 `.mam`·모니터·세션·실제 서버·에이전트 홈은 건드리지 않았다(실제 서버에 대해서는 `herdr status` 읽기 한 번뿐). + +## 결론 +'default' 스캔이 shim 을 거쳐 HERDR_SESSION_NAME 의 서버를 본다는 전제는 코드와 실서버로 확인됐고, 행을 "답한 서버" 로 적는 교정은 drift A·중복 등록·기존 행·환경변수 없는 데몬에 부작용이 없다. create 의 자기 서버 행 인수(F2)와 맞물려 scenario4 가 이 Mac 에서 통과하며, 수정 전에는 브리프의 rc 4 로 실패한다. + +[VERDICT: PASS] diff --git a/.agents/reports/planner-reviewer-codex-01/report-7ac94ed6.identity.json b/.agents/reports/planner-reviewer-codex-01/report-7ac94ed6.identity.json new file mode 100644 index 0000000..d6335c8 --- /dev/null +++ b/.agents/reports/planner-reviewer-codex-01/report-7ac94ed6.identity.json @@ -0,0 +1,14 @@ +{ + "loop_id": "loop-20261012T123226Z-e64a91", + "base_sha": "efd27def47a4d67684694ccf43a6ebeede01620f", + "head_sha": "21f0c12b9e2195cba8e196d6ccc2469173302e8a", + "tree_sha": "c2357ac01824d099fcbb7e9f7433b6b05f5e86a6", + "review_commit": "21f0c12b9e2195cba8e196d6ccc2469173302e8a", + "reviewer": "planner-reviewer-codex-01", + "job_id": "7ac94ed6", + "verdict": "PASS", + "transport": "herdr", + "evidence": "review-tree", + "isolation": "worktree", + "verified_at": "2026-10-12T12:38:13Z" +} \ No newline at end of file diff --git a/.agents/reports/planner-reviewer-codex-01/report-7ac94ed6.md b/.agents/reports/planner-reviewer-codex-01/report-7ac94ed6.md new file mode 100644 index 0000000..93ebb77 --- /dev/null +++ b/.agents/reports/planner-reviewer-codex-01/report-7ac94ed6.md @@ -0,0 +1,59 @@ +# Job 7ac94ed6 review + +판정: PASS. 지정된 변경 2개 파일에서 차단할 결함을 발견하지 않았다. 이전 잡 4d9c73e5의 임시 산출물을 사용하지 않고 코드 검토, 테스트 실행, 실서버 탐침을 새로 수행했다. 소스 수정·커밋·push는 하지 않았다. + +## 검증 identity + +- loop: `loop-20261012T123226Z-e64a91` +- base: `efd27def47a4d67684694ccf43a6ebeede01620f` +- head: `21f0c12b9e2195cba8e196d6ccc2469173302e8a` +- tree: `c2357ac01824d099fcbb7e9f7433b6b05f5e86a6` +- 검증 cwd: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261012T123226Z-e64a91/1` +- 실행 Python: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-56-fix/.venv/bin/python` (3.9.6); macOS, bash 3.2, herdr 0.9.3. + +검증 시작과 종료 시 HEAD/tree가 지정 값과 일치했고 `git status --short`는 비어 있었다. `git diff --check efd27def47a4d67684694ccf43a6ebeede01620f` 및 `/bin/bash -n .agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh`가 통과했다. + +## 코드 검토 결과 + +1. **응답한 서버로 라벨을 기록하는 전제가 맞다.** `.agents/skills/lib.sh:185–189`의 `-L` 처리는 `HERDR_SESSION_NAME`을 설정하고, `:270–283`의 `_real_herdr`는 그 이름을 실제 herdr의 `--session`으로 전달한다. `:1256–1274`의 `ls`도 이 경로를 사용한다. `reconcile.sh:505–530`에서 default 스캔은 `-L`을 생략하므로 상속된 이름의 서버가 응답한다. 새 `reconcile.sh:664–667`은 비어 있지 않은 비-default 상속 이름에 한정해 라벨을 보정하고, `:681–687`의 두 서버 필드와 start/attach/kill 명령에 일관되게 반영한다. 실제 일회용 herdr로도 전제를 확인했다. +2. **중복 등록 및 이후 drift A 오판이 관측되지 않았다.** `reconcile.sh:494–502`는 환경과 기존 YAML의 서버를 수집한다. 저장된 명시적 서버는 다음 실행의 환경이 제거되거나 바뀌어도 스캔 대상이다. `:607–611`의 이름 검사와 `:714–715`의 등록 직후 이름 추가가 default/명시적 서버 스캔에서 같은 pane이 두 번 발견될 때 중복을 막는다. 실서버 탐침에서 양쪽 alias가 alive에 존재해도 등록 행과 최초 drift B는 각각 하나였다. 후속 환경 변경 실행에서도 행은 running이고 drift A/B가 없었다. +3. **기존 default 행의 처리 범위는 유지된다.** 수정은 새 drift B 행 생성에 국한된다. pane_meta와 drift A, 기존 행의 처리는 변경하지 않는다. 실서버 탐침에서 기존 default 행은 재라벨링 없이 running으로 남았다. 상속 서버가 없는 일반 default 경로는 기존 scenario3 회귀 테스트로도 확인했다. 이 수정은 기존 default 행의 마이그레이션을 제공하지 않는다. +4. **create의 F2 서버 일치 조건과 맞는다.** `create_session.sh:540–543`은 monitor 행의 서버가 현재 create 서버와 같아야 인수한다. 새 라벨은 이 조건을 만족시키며 다른 서버 행을 인수하도록 조건을 완화하지 않는다. 같은 폴더 6개 병렬 create 사례 및 pane-lock 회귀 테스트가 통과했다. +5. **코드 블록 메타데이터 갱신이 정확하다.** `tests/data/skill_code_blocks.json:620`의 참조는 실제 `create_session.sh:550`의 `raise SystemExit(4)`를 가리키고 관련 검증이 통과했다. + +## 새로 수행한 검증 + +총 **51 passed, 1 skipped**. 모든 실행의 exit code는 0이다. + +| 검증 | 결과 | 근거 | +|---|---|---| +| `test_tier4_e2e.py::test_e2e_scenario4_parallel_flock_locking`, `::test_e2e_scenario3_drift_auto_reconciliation`, `test_issue50_pane_lock.py`, `test_issue42_skill_blocks.py` | 50 passed, 1 skipped, 97.61초 | `/tmp/7ac94ed6-review/tests.log` | +| 새 실제 herdr 서버/환경 변경 탐침 | 1 passed, 10.89초 | `/tmp/7ac94ed6-review/probe.log`, `/tmp/7ac94ed6-review/test_probe.py` | + +건너뛴 1건은 `tests/test_issue42_skill_blocks.py:266`: 추적되지 않는 `.claude/skills`가 리뷰 worktree에 없어서 발생한 기존 조건부 skip이다. scenario4는 mock herdr를 사용하는 회귀 테스트이며, 6개 병렬 create의 성공과 고유 행을 검증한다. 이를 실제 herdr 병렬 create 검증으로 간주하지 않았다. + +재현 launcher: `/tmp/7ac94ed6-review/run.py`. 지정 Python으로 다음 인자를 전달한다. + +```text +/tmp/7ac94ed6-review/run.py tests/test_tier4_e2e.py::test_e2e_scenario4_parallel_flock_locking tests/test_tier4_e2e.py::test_e2e_scenario3_drift_auto_reconciliation tests/test_issue50_pane_lock.py tests/test_issue42_skill_blocks.py +/tmp/7ac94ed6-review/run.py probe +``` + +launcher는 검증 cwd를 고정하고, 상속 HERDR_*/MAM_*/MQTT_*/SKS_* 및 지정 경로 환경변수를 제거한다. 일회용 HOME, 검증 트리 밖 basetemp, `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider`, 지정 `AGENT_PYTHON_BIN`을 사용한다. 일반 테스트 root는 `/tmp/7ac94ed6-u75tye4e`, 탐침 root는 `/tmp/7ac94ed6-2ppzeojn`이었다. + +### 실제 herdr 탐침 절차와 기대/실제 결과 + +일회용 HOME과 서버 `review7-a88dd0c7`에서 fake Claude pane `t50-creator-claude`를 실제 herdr로 시작하고, 빈 임시 YAML을 준비했다. 실제 운영 서버나 사용자 에이전트 홈, `.mam.env`, 운영 모니터 데몬은 사용하지 않았다. + +1. 상속 `HERDR_SESSION_NAME`으로 실행한 무옵션 `herdr ls`와 명시적 `-L review7-a88dd0c7` 조회가 같은 pane을 반환해야 한다. **실제: 두 출력 일치.** +2. 실제 `reconcile.sh --once --emit-diff` 실행 시 default와 명시적 서버 스캔이 같은 pane을 보더라도 하나의 행만 등록하고 서버 필드·제어 명령은 실제 이름을 사용해야 한다. **실제: alive에 두 alias, 행 1개, 최초 B 1개, A 없음, 두 필드와 명령 모두 실제 이름.** +3. 같은 환경에서 다시 실행해도 중복이나 terminated 판정이 없어야 한다. **실제: running 1개, 추가 A/B 없음.** +4. `HERDR_SESSION_NAME`을 제거한 다음 실행하고, 이어서 존재하지 않는 다른 일회용 이름으로 바꿔 실행해도 YAML의 명시적 서버를 조회해야 한다. **실제: 두 실행 모두 기존 서버 pane을 발견, running 유지, A/B 없음.** 다른 이름의 서버는 자동 시작하지 않도록 설정했다. +5. 임시 기존 행의 두 서버 필드를 default로 설정한 뒤 원래 상속 환경에서 실행하면 기존 행의 처리 방식이 유지되어야 한다. **실제: default 라벨 유지, running, A 없음.** +6. finally에서 탐침 전용 서버와 HOME을 정리하고 잔존 프로세스가 없는지 검사했다. **실제: 정리 및 잔존 검사 통과.** + +## 검증 범위의 한계 + +전체 프로젝트 스위트는 이 잡에서 실행하지 않았다. 장기 실행 데몬을 시작하지 않았으며 환경 변경은 실제 `--once` 연속 실행과 코드 검토로 검증했다. 실제 herdr 탐침의 에이전트 명령은 fake Claude이므로 LLM 업무 수행 자체를 검증하는 시험은 아니다. 이 한계 내에서 요청된 서버 라벨·중복·후속 스캔·create 인수 회귀에 대한 차단 사유는 없다. + +[VERDICT: PASS] diff --git a/.agents/reports/reviewer-agy-01/report-05f28276.md b/.agents/reports/reviewer-agy-01/report-05f28276.md new file mode 100644 index 0000000..f0d6550 --- /dev/null +++ b/.agents/reports/reviewer-agy-01/report-05f28276.md @@ -0,0 +1,92 @@ +# Code Review Report (4th Round Re-Review): PR #56 (`fix/issue-50-55`) + +- **Job ID**: `05f28276` +- **Reviewer**: `agy` (Session: `herdr:reviewer-agy-01`, Role: `Reviewer`) +- **Date**: 2026-10-12 +- **Target Pull Request**: PR #56 (`fix/issue-50-55`, Closes #50, #55) +- **Base Commit**: `efd27def47a4d67684694ccf43a6ebeede01620f` (3rd round review head) +- **Head Commit**: `21f0c12b9e2195cba8e196d6ccc2469173302e8a` (Fix commit) +- **Verification Path**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261012T013821Z-656dfe/1` +- **Verification Environment**: macOS (Darwin 24.3.0, arm64), Python 3.9.6, bash 3.2.57(1)-release, `herdr` 0.9.3 + +--- + +## 1. Executive Summary + +This 4th-round re-review evaluated commit `21f0c12b9e2195cba8e196d6ccc2469173302e8a`, which addresses the regression discovered on macOS during full-suite testing where [`tests/test_tier4_e2e.py::test_e2e_scenario4_parallel_flock_locking`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261012T013821Z-656dfe/1/tests/test_tier4_e2e.py) (6 concurrent creates into the same folder) failed with exit code 4 (`already running in agent-sessions.yaml`). + +### Root Cause +During parallel `create_session.sh` invocations, `create_session.sh` runs `reconcile.sh --once` under ambient `HERDR_SESSION_NAME=`. +1. In `reconcile.sh`, `unique_servers` always includes `'default'`. +2. The scan for `'default'` executes `herdr ls` without `-L`. +3. The `.mam/shim/herdr` wrapper inspects `HERDR_SESSION_NAME`; because it is set to ``, `_real_herdr` delegates to `"$HERDR_BIN" --session "$session" "$@"`. +4. Thus, the pane list returned by the `'default'` scan actually reflects the panes on server ``. +5. Previously, `reconcile.sh` drift B labeled these newly discovered panes with `herdr_server: default`. +6. When concurrent `create_session.sh` processes attempted to adopt the auto-registered pane via `_adopted_own`, they required `(s.get('herdr_server') or ...) == server_name` (where `server_name` was ``). Because the monitor row recorded `'default'`, the comparison failed and `create_session.sh` aborted with exit code 4. + +### The Fix +In [`multi-agent-mux-monitor/scripts/reconcile.sh:659-661`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261012T013821Z-656dfe/1/.agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh#L659-L661): +```python +_ambient = os.environ.get('HERDR_SESSION_NAME', '') +if srv == 'default' and _ambient and _ambient != 'default': + srv = _ambient +server_opt = f"-L {srv} " if srv != 'default' else "" +``` +If the sweep is querying `'default'` but `HERDR_SESSION_NAME` is set to an explicit server in the environment, `reconcile.sh` recognizes that the shim returned panes from that server, and records `herdr_session: _ambient`, `herdr_server: _ambient`, and `-L _ambient` in the entry. +Additionally, [`tests/data/skill_code_blocks.json`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261012T013821Z-656dfe/1/tests/data/skill_code_blocks.json) was updated to line 550 for `SystemExit(4)` in `create_session.sh`. + +**Verdict: PASS**. + +--- + +## 2. Detailed Technical Verification + +### 2.1 Correctness of the Ambient Server Attribution +- In `.agents/skills/lib.sh:271-280`: + ```bash + _real_herdr() { + local session="${HERDR_SESSION_NAME:-}" + if [ "$session" = "default" ]; then session=""; fi + if [ -n "$session" ]; then + if [ "${1:-}" = "--session" ] || [ "${1:-}" = "-s" ]; then "$HERDR_BIN" "$@"; else "$HERDR_BIN" --session "$session" "$@"; fi + else + "$HERDR_BIN" "$@" + fi + } + ``` + Calls to `herdr` without `-L` or `--session` are answered by `herdr --session "$session"`. Thus, attributing panes found in a `'default'` scan to `HERDR_SESSION_NAME` is strictly faithful to the actual server queried by `_real_herdr`. + +### 2.2 Side-Effect Analysis +1. **Drift A (Termination Detection)**: + - When a row is registered with `herdr_server: `, subsequent sweeps populate `unique_servers` from `d.get('herdr_sessions')`, adding ``. + - `reconcile.sh` explicitly scans `-L ` and places `(name, '')` into `alive_set`. + - In drift A, `(s.get('name'), s.get('herdr_server'))` matches `alive_set`. The row is **not** falsely terminated. +2. **Daemon Mode (No Ambient Server)**: + - In persistent monitor daemon runs (`reconcile.sh --subscribe`), `HERDR_SESSION_NAME` is empty or `'default'`. + - `_ambient and _ambient != 'default'` evaluates to `False`, preserving exact default behavior. +3. **Duplicate Registration Prevention**: + - In drift B, [`yaml_session_names.add(name)`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261012T013821Z-656dfe/1/.agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh#L715) is updated immediately when a pane is registered. + - If the same pane is enumerated in both the `'default'` scan and an explicit `-L ` scan within the same sweep, line 611 skips the duplicate (`if (name, srv) in yaml_session_names or any(_sanitize(y) == name for y in yaml_session_names): continue`). No duplicate entries are created. + +### 2.3 Verification of Fixed Tests +1. **`test_e2e_scenario4_parallel_flock_locking`**: + - Executed [`tests/test_tier4_e2e.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261012T013821Z-656dfe/1/tests/test_tier4_e2e.py): all 5 scenarios passed in 364.51s, with scenario 4 completing in 42.28s without exit code 4. +2. **`tests/test_issue50_pane_lock.py`**: + - Executed full test suite: 11 / 11 passed in 53.20s. +3. **`tests/test_issue42_skill_blocks.py`**: + - Executed: 37 passed, 1 skipped. Line 550 in `tests/data/skill_code_blocks.json` matches `create_session.sh` exactly. + +--- + +## 3. Test Execution Summary + +| Test Target | Result | Duration | +|---|---|---| +| `tests/test_tier4_e2e.py::test_e2e_scenario4_parallel_flock_locking` | **1 Passed** | 42.28s | +| `tests/test_tier4_e2e.py` (Full Suite, 5 Scenarios) | **5 Passed** | 364.51s | +| `tests/test_issue50_pane_lock.py` (Full Suite) | **11 Passed** | 53.20s | +| `tests/test_issue42_skill_blocks.py` | **37 Passed, 1 Skipped** | 0.17s | + +--- + +[VERDICT: PASS] diff --git a/.agents/reports/reviewer-grok-01/report-51507ec8.md b/.agents/reports/reviewer-grok-01/report-51507ec8.md new file mode 100644 index 0000000..2cfdb57 --- /dev/null +++ b/.agents/reports/reviewer-grok-01/report-51507ec8.md @@ -0,0 +1,36 @@ +# Review 51507ec8 — the monitor labels a row with the server that answered + +Head `21f0c12b9e2195cba8e196d6ccc2469173302e8a` (tree `c2357ac01824d099fcbb7e9f7433b6b05f5e86a6`) is the one commit `21f0c12 fix(issue-50): the monitor labels a row with the server that answered` on base `efd27def47a4d67684694ccf43a6ebeede01620f`. Two files, +9/−1. `git diff --check` is clean. Snapshot porcelain empty. Control-plane main is still `42e51735ba285930fc5b8ee70ba343277e9b1adb`, porcelain empty. + +Drift B writes the row after `pane_meta` and the cwd check. When the scan key is `default` and `HERDR_SESSION_NAME` is set to something else, that name replaces `srv` before `server_opt` and the row are built (`reconcile.sh:664-666`). `herdr_session`, `herdr_server`, and the start, attach, and kill commands all use that name (`:681-687`). The scan (`:505-509`), `pane_meta` (`:540-544`), and drift A (`:594-595`) still use the scan key. + +## Why the label matches the server that answered + +`unique_servers` always contains `default` and, when the variable is set, `HERDR_SESSION_NAME` (`reconcile.sh:494-496`). A `default` scan runs `herdr ls` with no `-L`. The shim's `ls` reads `HERDR_SESSION_NAME` (`lib.sh:1256-1259`) and calls `_real_herdr agent list` (`:1274`). `_real_herdr` prepends `--session` whenever that value is non-empty (`:270-283`; `default` is treated as empty at `:272-274`). A create exports `HERDR_SESSION_NAME` (`create_session.sh:373`) and its end-of-run `reconcile.sh --once` is a child of that process (`:748`), so the inherited value is the create's server. + +On this Mac, herdr 0.9.3, throwaway home `/tmp/m515h2`, session `t515b`: the server socket appeared under that home. `herdr --session t515b agent list` exited 0. `herdr agent list` with no `--session` exited 1 with `server_not_running` for `/tmp/m515h2/.config/herdr/herdr.sock`. The generated shim's `ls`, with `HERDR_SESSION_NAME=t515b`, no `-L`, and `MAM_NO_AUTOSTART=1`, exited 0. The same `ls` with the variable unset exited 1 and printed `no server running (herdr session default)`. `workspace create` on that server returned a pane; `agent list` stayed an empty agent list, so the shim's `ls` printed no name. The exit codes are the routing check. No socket named `t515b` appeared under the user home. + +Create adopts a monitor row when `herdr_server or herdr_session or 'default'` equals its own `HERDR_SESSION_NAME` (`create_session.sh:522`, `:540-543`). After this label they are the same string, the epoch check is unchanged, and the row is replaced. A row that fails that test still raises `SystemExit(4)` at `create_session.sh:550`. `tests/data/skill_code_blocks.json` now points exit code 4 at that line. + +## Side effects + +A throwaway workspace under `/tmp/m515ws` ran this snapshot's `reconcile.sh --once`. The shim was the one `lib.sh` writes, and the binary it called was a stand-in that returns one pane. `HERDR_SESSION_NAME=t515probe`. + +- Both scans called `agent list --session t515probe`. `default` sorts first, so the first call is the `default` scan. The result listed the pane under both scan keys, `probe-creator-claude|default` and `probe-creator-claude|t515probe`. The yaml received one row, `status: running`, `herdr_server` and `herdr_session` `t515probe`, note `auto-registered by monitor`, kill command `herdr -L t515probe kill-session -t probe-creator-claude`. `pane_meta` ran once. The second sighting is skipped because the name is added to `yaml_session_names` after the append (`reconcile.sh:715`) and the next entry hits the name check (`:610-611`). +- A second `--once` with `HERDR_SESSION_NAME` unset scanned the real default (the stand-in was asked for `agent list` with no `--session`) and the row's server (`--session t515probe`). The live set was `probe-creator-claude|t515probe`. The row stayed `running` on `t515probe`. Drift A did not fire. Drift A compares `(name, herdr_session or herdr_server or 'default')` with the scan keys (`:594-595`), and the daemon's server set includes each row's `herdr_session` (`:499-502`). +- With no `HERDR_SESSION_NAME`, a new pane visible only on the real default scan was stored as `herdr_server: default` and the kill command has no `-L`. The new `if` does not run. +- A row already stored as `default` stayed `running`, stayed `default`, and kept its note. The new `if` is on the new-entry path, past the name skip. + +A create's own `--once` still puts `default` in the scan set, and that scan is still answered from the inherited `HERDR_SESSION_NAME`. A row whose server is the real default is compared with that redirected scan for that one pass. The commit leaves the scan and drift A as they were. The row this commit writes is looked up on the following daemon pass by the server name stored on it. + +## What was run + +On this Mac, with the pr-56-fix venv (Python 3.9.6), `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider`, `--basetemp` under `/tmp/m515bt-s4`, `/tmp/m515bt-lock`, and `/tmp/m515bt-skill`. Inherited `HERDR_*`, `MAM_*`, `MQTT_*`, `WORKSPACE_ROOT`, `CLAUDE_PROJECT_DIR`, `HOME_DIR`, and `CODEX_HOME` were unset. + +- `tests/test_tier4_e2e.py::test_e2e_scenario4_parallel_flock_locking`: 1 passed in 43.01s. Six parallel creates in one sandbox all exited 0, and the yaml held those six names. The mock herdr drops `-L` and `--session`, so this is the regression the full suite hit at `a9ffeda` (rc 4, "already running"). The shim routing above is the separate check that a no-`-L` call reaches `HERDR_SESSION_NAME`. +- `tests/test_issue50_pane_lock.py`: 11 passed in 54.30s. That includes the adoption of a monitor row of this server and the refusal of another server's row. +- `tests/test_issue42_skill_blocks.py::test_exit_table_agrees_with_script`: 3 passed in 0.05s. The passthrough for exit code 4 resolves to `create_session.sh:550`, and that line raises `SystemExit(4)`. + +User `~/.config/herdr/config.toml` mtime stayed `1784456394`. The herdr `sessions` listing hash stayed `0e4d261c4faae218c1e6ec7f2914c0c766df456e0c5e460cb28e2dcb20385177`. The control-plane loop marker mtime stayed `1791769101`. The throwaway server was stopped and its socket removed. + +[VERDICT: PASS] diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index 744559f..d9927d9 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -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.8.9" +MAM_VERSION="4.9.0" export MAM_VERSION AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}" diff --git a/.agents/skills/lib_py/loop/eager.py b/.agents/skills/lib_py/loop/eager.py index 005d426..2cfebef 100644 --- a/.agents/skills/lib_py/loop/eager.py +++ b/.agents/skills/lib_py/loop/eager.py @@ -13,7 +13,7 @@ import sys from typing import List, Sequence # Framework version locked with framework constants -FRAMEWORK_VERSION = "4.8.9" +FRAMEWORK_VERSION = "4.9.0" CORE_MODULES: Sequence[str] = ( # Standard library modules diff --git a/.agents/skills/multi-agent-mux-create/SKILL.md b/.agents/skills/multi-agent-mux-create/SKILL.md index 996e0af..60b81e2 100644 --- a/.agents/skills/multi-agent-mux-create/SKILL.md +++ b/.agents/skills/multi-agent-mux-create/SKILL.md @@ -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.8.9 +version: 4.9.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md index e163688..e905716 100644 --- a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md +++ b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md @@ -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.8.9 +version: 4.9.0 author: godopu license: MIT platforms: [linux, macos, windows] diff --git a/.agents/skills/multi-agent-mux-loop/SKILL.md b/.agents/skills/multi-agent-mux-loop/SKILL.md index 4fc301f..dcadc03 100644 --- a/.agents/skills/multi-agent-mux-loop/SKILL.md +++ b/.agents/skills/multi-agent-mux-loop/SKILL.md @@ -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.8.9 +version: 4.9.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-monitor/SKILL.md b/.agents/skills/multi-agent-mux-monitor/SKILL.md index a61f4f1..506d20e 100644 --- a/.agents/skills/multi-agent-mux-monitor/SKILL.md +++ b/.agents/skills/multi-agent-mux-monitor/SKILL.md @@ -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.8.9 +version: 4.9.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md b/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md index 2f85b09..2bc6cd0 100644 --- a/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md +++ b/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md @@ -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.8.9 +version: 4.9.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-resume/SKILL.md b/.agents/skills/multi-agent-mux-resume/SKILL.md index 74ad08a..fc05914 100644 --- a/.agents/skills/multi-agent-mux-resume/SKILL.md +++ b/.agents/skills/multi-agent-mux-resume/SKILL.md @@ -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 ` or `agy --conversation ` 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.9 +version: 4.9.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-status/SKILL.md b/.agents/skills/multi-agent-mux-status/SKILL.md index 231a17b..a98e264 100644 --- a/.agents/skills/multi-agent-mux-status/SKILL.md +++ b/.agents/skills/multi-agent-mux-status/SKILL.md @@ -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.8.9 +version: 4.9.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-stop/SKILL.md b/.agents/skills/multi-agent-mux-stop/SKILL.md index 24b46f9..816cddb 100644 --- a/.agents/skills/multi-agent-mux-stop/SKILL.md +++ b/.agents/skills/multi-agent-mux-stop/SKILL.md @@ -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.8.9 +version: 4.9.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/BOOTSTRAP.ko.md b/BOOTSTRAP.ko.md index c4e132a..f113038 100644 --- a/BOOTSTRAP.ko.md +++ b/BOOTSTRAP.ko.md @@ -2,7 +2,7 @@ 본 문서는 `herdr_agent_orchestration` 오케스트레이션 및 메시징 백플레인 워크플로우를 새로운 프로젝트에 도입하여 이식하고, 새로운 개발자/에이전트가 초기 가동을 시작할 때 수행해야 하는 환경 설정 및 구축 절차를 안내합니다. -[![Version: v4.8.9](https://img.shields.io/badge/version-v4.8.9-blue.svg)](VERSIONS.md) +[![Version: v4.9.0](https://img.shields.io/badge/version-v4.9.0-blue.svg)](VERSIONS.md) 새로운 에이전트는 이 안내서에 기술된 절차를 순차적으로 실행하여 초기 환경을 안정적으로 설정할 수 있습니다. diff --git a/BOOTSTRAP.md b/BOOTSTRAP.md index d984d8e..8ffd482 100644 --- a/BOOTSTRAP.md +++ b/BOOTSTRAP.md @@ -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. -[![Version: v4.8.9](https://img.shields.io/badge/version-v4.8.9-blue.svg)](VERSIONS.md) +[![Version: v4.9.0](https://img.shields.io/badge/version-v4.9.0-blue.svg)](VERSIONS.md) A new agent can follow the steps in this guide sequentially to establish a stable and reliable initial environment. diff --git a/MESSAGING.md b/MESSAGING.md index bafda9c..4484c97 100644 --- a/MESSAGING.md +++ b/MESSAGING.md @@ -1,6 +1,6 @@ # Messaging System Technical Analysis & Architecture Report -Applies to: MAM v4.8.9 +Applies to: MAM v4.9.0 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. diff --git a/README.ko.md b/README.ko.md index 926ce17..2bb49f2 100644 --- a/README.ko.md +++ b/README.ko.md @@ -2,7 +2,7 @@ Herdr와 MQTT 브로커를 기반으로 구축된 고신뢰성 **다중 에이전트 오케스트레이션 및 메시징 백플레인** 프레임워크입니다. `claude`, `agy`, `hermes`, `grok`, `opencode`, `codex` 등 다중 LLM 백엔드 에이전트 전반에 걸쳐 장시간 수행되는 작업(코드 생성, 리팩토링, 보안 검토 등)을 조정, 격리 및 감사할 수 있도록 설계되었습니다. -[![Version: v4.8.9](https://img.shields.io/badge/version-v4.8.9-blue.svg)](VERSIONS.md) +[![Version: v4.9.0](https://img.shields.io/badge/version-v4.9.0-blue.svg)](VERSIONS.md) --- diff --git a/README.md b/README.md index e606864..505a0de 100644 --- a/README.md +++ b/README.md @@ -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`). -[![Version: v4.8.9](https://img.shields.io/badge/version-v4.8.9-blue.svg)](VERSIONS.md) +[![Version: v4.9.0](https://img.shields.io/badge/version-v4.9.0-blue.svg)](VERSIONS.md) --- diff --git a/VERSIONS.md b/VERSIONS.md index 41b92b5..3addae5 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -6,8 +6,8 @@ ## 📌 현재 버전 개요 (Current Release) -- **프레임워크 버전**: `v4.8.9` -- **최신 릴리스 일시**: 2026-10-09 (KST) +- **프레임워크 버전**: `v4.9.0` +- **최신 릴리스 일시**: 2026-10-12 (KST) - **기준 브랜치**: `main` - **핵심 아키텍처**: - **Terminal 상태 단방향 불변성 (`engine.py`, Issue #6 Phase 1)**: `completed`, `error`, `cancelled` 터미널 상태 진입 후 후속 이벤트나 업데이트에 의한 덮어쓰기를 엄격히 차단(Terminal Immutability). `registry.py close` 서브커맨드 및 fail-closed 조기 종료 도입. @@ -24,23 +24,38 @@ ## 🧭 스킬 패키지 버전 매트릭스 (Skills Version Matrix) -모든 8개 스킬은 YAML frontmatter 메타데이터(`author`, `version`, `platforms`, `environments`) 표준화를 통해 `v4.8.9`로 동기화되어 배포됩니다. +모든 8개 스킬은 YAML frontmatter 메타데이터(`author`, `version`, `platforms`, `environments`) 표준화를 통해 `v4.9.0`로 동기화되어 배포됩니다. | 스킬명 | 버전 | 역할 및 주요 책임 | 상태 | | :--- | :---: | :--- | :---: | -| **`multi-agent-mux-create`** | `4.8.9` | 에이전트 세션 신규 생성 및 Herdr 컨테이너 격리 스폰 | ✅ 배포 | -| **`multi-agent-mux-stop`** | `4.8.9` | 대화 UUID 원자적 캡처 및 세션 안전 종료 (Graceful Stop) | ✅ 배포 | -| **`multi-agent-mux-resume`** | `4.8.9` | 온디스크 대화 컨텍스트 기반 Tier-1 초고속 세션 복원 | ✅ 배포 | -| **`multi-agent-mux-status`** | `4.8.9` | 실시간 Herdr 세션 및 레지스트리 드리프트 스냅샷 조회 | ✅ 배포 | -| **`multi-agent-mux-monitor`** | `4.8.9` | YAML ↔ 런타임 상태 간 자율 조정자 (Reconciler Loop) | ✅ 배포 | -| **`multi-agent-mux-delegate-job`** | `4.8.9` | MQTT 이벤트 채널 기반 비동기 단위 작업 위임 | ✅ 배포 | -| **`multi-agent-mux-loop`** | `4.8.9` | Planner-Creator-Reviewer 3자 자율 계획·실행·피어리뷰 루프 | ✅ 배포 | -| **`multi-agent-mux-orc-onboard`** | `4.8.9` | 오케스트레이터 UUID 격리 등록 및 서브 세션 오염 방지 | ✅ 배포 | +| **`multi-agent-mux-create`** | `4.9.0` | 에이전트 세션 신규 생성 및 Herdr 컨테이너 격리 스폰 | ✅ 배포 | +| **`multi-agent-mux-stop`** | `4.9.0` | 대화 UUID 원자적 캡처 및 세션 안전 종료 (Graceful Stop) | ✅ 배포 | +| **`multi-agent-mux-resume`** | `4.9.0` | 온디스크 대화 컨텍스트 기반 Tier-1 초고속 세션 복원 | ✅ 배포 | +| **`multi-agent-mux-status`** | `4.9.0` | 실시간 Herdr 세션 및 레지스트리 드리프트 스냅샷 조회 | ✅ 배포 | +| **`multi-agent-mux-monitor`** | `4.9.0` | YAML ↔ 런타임 상태 간 자율 조정자 (Reconciler Loop) | ✅ 배포 | +| **`multi-agent-mux-delegate-job`** | `4.9.0` | MQTT 이벤트 채널 기반 비동기 단위 작업 위임 | ✅ 배포 | +| **`multi-agent-mux-loop`** | `4.9.0` | Planner-Creator-Reviewer 3자 자율 계획·실행·피어리뷰 루프 | ✅ 배포 | +| **`multi-agent-mux-orc-onboard`** | `4.9.0` | 오케스트레이터 UUID 격리 등록 및 서브 세션 오염 방지 | ✅ 배포 | --- ## 📋 버전별 상세 변경 내역 (Changelog) +### 🚀 `v4.9.0` — Issue #50·#55: 온보딩·위임이 레지스트리로 기다리고, pane 배치는 서버마다 하나씩, 테스트는 실제 에이전트 홈에 쓰지 않는다 (2026-10-12) + +> **마이너 (MINOR Release)**: 새 종료 코드(`create --onboard` 의 44·45)와 새 대기 동작(위임 래퍼의 레지스트리 대기, pane 배치 잠금). PR #56. + +#### 🌟 주요 개선 사항 +1. **#55 테스트 격리**: lib.sh 가 에이전트 pane 에 내보내는 `CLAUDE_PROJECT_DIR` 등을 테스트마다 지우는 autouse 픽스처(`tests/conftest.py`)와, 실행 중 실제 에이전트 홈에 새 항목이 생기면 세션을 실패시키는 읽기 전용 가드(`tests/_agent_home_guard.py`). 운영용 lib.sh export 는 그대로다. +2. **#50 A — `create --onboard` 가 온보딩 잡의 종결을 기다린다**(`worktree_prepare.sh --onboard` 도): 상한 `MAM_ONBOARD_WAIT_S`(기본 600 s). 새 종료 코드 **44**(온보딩이 error/cancelled 로 끝남)·**45**(시간 안에 끝나지 않음); 둘 다 세션을 남기고 행에 `last_visible_status: onboarding-` 를 쓴다. +3. **#50 C — 위임 래퍼가 주입 전에 레지스트리로 기다린다**: 대상 세션의 앞선 열린 잡(온보딩 포함)이 끝날 때까지 `MAM_DISPATCH_WAIT_S` 만큼; 끝나지 않으면 전처럼 `cancelled`(session_busy), 종료 코드 5. 세션당 한 번에 한 잡만 승인(레지스트리 잠금 아래 기록), herdr idle 대기는 보조 확인으로만 남는다. `submit --dry-run` 레코드는 주입되지 않으므로 pending 인 동안 세션을 잡지 않는다(pick-pending 이 가져가면 잡는다). +4. **같은 폴더 동시 create**: pane 배치를 herdr 서버마다 `lib_py/pane_lock.py`(fd 9 의 `fcntl.flock`, macOS 에는 flock(1) 이 없음)로 직렬화한다. 대기 상한 `MAM_PANE_CLAIM_WAIT_S`, 보유자가 죽으면 풀린다. create 는 monitor 가 자기 서버에 등록해 둔 행만 이어받고, monitor 는 `default` 조회에 실제로 답한 서버(상속된 `HERDR_SESSION_NAME`)를 행에 적는다. + +#### ✅ 품질 검증 +- **PR 작성자 검증 (Linux·Python 3.14·herdr 0.9.3)**: 320aa9f 전체 스위트 1864 passed / 12 failed(#28 의 알려진 `test_issue10_13_purge.py` 기준선) / 20 skipped, 같은 폴더 동시 `create --onboard` 3 개 실제 시험 모두 exit 0. 리뷰 루프 3 개, 320aa9f 에서 만장일치 PASS. +- **병합 전 macOS 검증 (macOS·Python 3.9·bash 3.2·herdr 0.9.3)**: 320aa9f 에서 macOS 전용 실패를 고침(a9ffeda: AF_UNIX 경로 104 바이트, tty canonical 1024 바이트, bash 3.2 의 마지막 명령 미-exec, `_adopted_own` 의 서버 확인, 경합만 재시도하는 flock, dry-run 레코드). 리뷰 1회차 4명 모두 NOT PASS(macOS 실패 수정·dry-run·서버 확인·flock 범위), 2회차 codex F1(가져간 dry-run 잡은 세션을 잡는다, efd27de), 3회차 4/4 PASS(리뷰 중 워크트리 수정으로 루프가 폐기), @21f0c12(monitor 서버 표기) **4/4 PASS**: 4회차에서 claude·agy·grok PASS, codex 는 스트림 끊김으로 판정 없이 중단되어 5회차 단독 재리뷰에서 PASS. 리포트는 `.agents/reports//`(루프가 승격한 codex 리포트만 identity 사이드카가 있고, 나머지 셋은 4회차 잡의 report-final 을 복사). +- **전체 스위트 (21f0c12, MQTT 환경변수 없이, `CLAUDE_PROJECT_DIR` 설정)**: **1860 passed / 0 failed / 38 skipped**(75 분). 실행 뒤 `~/.claude/projects` 에 새 항목 없음. + ### 🚀 `v4.8.9` — 테스트가 체크아웃의 실제 루프 잠금을 잡지 않는다 (2026-10-09) > **패치 (PATCH Release, 테스트 전용)**: v4.8.8 센티널 실행 뒤 리뷰 워크트리에 빈 `.mam/` 이 남은 원인. 제품 코드(`.agents/skills`)는 버전 줄 외에 바뀌지 않았다.