release(v4.10.0): same-type agents in one cwd never take each other's conversation id; same-cwd creation order (Issue #63) - archive the review reports, bump the lockstep version
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
1 parent
1614af5b67
commit
0454fe968b
24 files changed
+425
-26
No files matched your search
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"loop_id": "loop-20261023T035928Z-2ccc02",
|
||||||
|
"base_sha": "dc886ecd6b38c01032a55a2d1c230c73362ae630",
|
||||||
|
"head_sha": "1614af5b673d7519d3a38f63e3636055592b4f14",
|
||||||
|
"tree_sha": "f1af6fdd548ee5d6f6b81e2202833edfaca98188",
|
||||||
|
"review_commit": "1614af5b673d7519d3a38f63e3636055592b4f14",
|
||||||
|
"reviewer": "planner-reviewer-claude-01",
|
||||||
|
"job_id": "af549b13",
|
||||||
|
"verdict": "PASS",
|
||||||
|
"transport": "herdr",
|
||||||
|
"evidence": "review-tree",
|
||||||
|
"isolation": "worktree",
|
||||||
|
"verified_at": "2026-10-23T05:16:58Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# PR #69 (Issue #63) 리뷰 — head 1614af5
|
||||||
|
|
||||||
|
- 리뷰어: planner-reviewer-claude-01 (macOS, bash 3.2, /usr/bin/python3 3.9.6)
|
||||||
|
- 검증 경로: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261023T035928Z-2ccc02/1`
|
||||||
|
- identity 확인: HEAD 1614af5b673d7519d3a38f63e3636055592b4f14, tree f1af6fdd548ee5d6f6b81e2202833edfaca98188, `git status --porcelain --ignored` 비어 있음, `.mam` 없음 (시작·종료 시 모두). main HEAD dc886ec 그대로.
|
||||||
|
- 범위: base dc886ec..1614af5 전체(커밋 13개).
|
||||||
|
- ede9c62 까지는 이전 리뷰(잡 8445af7f, 55f86b49)에서, 8f64174·7cac6f6 은 잡 1c6eca2b 에서 봤다. 그 결론을 다시 확인했다.
|
||||||
|
- 이번에는 1614af5 와 세 수정 사이의 정합성을 중점으로 봤다.
|
||||||
|
- 참고: 같은 head 를 맡은 잡 906427d1 은 관련 스위트 도중 검증 경로(`…a646cb/3`)가 사라져 끝내지 못했다. 그 잡에서 얻은 head 결과(issue63 27 passed, 변이 결과)는 같은 tree 의 스크래치 사본에서 나온 것이다. `diff -rq` 로 이번 검증 경로와 같음을 확인했다.
|
||||||
|
|
||||||
|
## 1. 1614af5 가 바꾼 것
|
||||||
|
|
||||||
|
- `.agents/skills/lib_py/agents/id_order.py:223`: `elif sess == me or not sess:` 를 `elif not sess:` 로 바꿨다.
|
||||||
|
- 그래서 호출자 자신의 세션 이름에 남은 죽은 owner 마커도 고아 규칙을 따른다. 이번 시도의 행이 등록되면 지우고(:225), herdr 가 정상 목록으로 "없음"이라 답하면 지우고(:228-229), 그 밖에는 남긴다.
|
||||||
|
- `_read_and_prune_markers` 의 `me` 인자를 지웠다(:183, :308).
|
||||||
|
- `blockers` 는 ede9c62 부터 `m_sess == me` 를 건너뛰지 않는다(:136-144). 그래서 남은 자기 이름 마커가 실제로 막는다.
|
||||||
|
|
||||||
|
## 2. 세 수정이 각 지적을 닫는가, 서로 어긋나지 않는가
|
||||||
|
|
||||||
|
1. **8f64174 — 조회 실패를 "없음"으로 읽던 문제**
|
||||||
|
- `herdr_session_state` (:58-86) 는 정상 파싱한 `agent list` 에 이름이 없을 때만 False 를 돌려준다. 그 밖에는 None 이다.
|
||||||
|
- prune 은 `drop = live is False` (:228) 다.
|
||||||
|
- 변이 m1(`not live`)과 m2(rc≠0 → False)가 테스트에 잡혔다(잡 1c6eca2b).
|
||||||
|
2. **7cac6f6 — 상한 0 에서 30 초 조회, server_not_running**
|
||||||
|
- 조회 시간은 `deadline - now` 다(:288). timeout≤0 이면 None 을 돌려준다(:64-65).
|
||||||
|
- 변이 m4(고정 30 s)와 m7(timeout≤0 에도 조회)이 잡혔다.
|
||||||
|
- 상한 0 테스트는 0.3 s 안에 끝나는 것을 확인한다(test :1222-1224).
|
||||||
|
3. **1614af5 — 같은 이름의 죽은 owner 마커를 확인 없이 지우던 문제**
|
||||||
|
- 옛 규칙(`sess == me` 이면 drop)을 되돌린 변이 사본에서는 두 테스트가 실패한다: `test_id_order_dead_owner_marker_blocks_while_its_unregistered_herdr_session_is_alive`, `test_id_order_real_probe_against_mock_herdr_fails_closed`.
|
||||||
|
- 커밋 메시지가 말한 "재시도의 실패 정리가 살아 있는 pane 을 죽임"도 막힌다. create 의 대기(`create_session.sh:287`)는 롤백 trap 설치(:367)보다 앞서 있어서, 대기에서 거부(exit 1)되면 `cleanup_herdr_on_error` 가 돌지 않는다.
|
||||||
|
4. **정합성**: 세 수정은 모두 `_read_and_prune_markers` 의 "죽은 owner 마커" 분기 하나를 쓰고, 조회는 `bounded_probe` 하나를 거친다. 자기 이름 마커만 다른 규칙을 쓰던 예외가 사라져서 규칙이 하나로 모였다. 어긋나는 곳은 찾지 못했다.
|
||||||
|
|
||||||
|
## 3. 흔한 사용을 600 초 대기로 막지 않는가
|
||||||
|
|
||||||
|
스크래치에서 `id_order.wait` 를 프로세스 안에서 직접 불렀다. 조회 함수를 주입하고, cap 2 s(일부 0 s)로 돌렸다.
|
||||||
|
|
||||||
|
**정상 사용 (실패 없음)**: 막지 않는다.
|
||||||
|
- create 마커가 남은 채로, 그 세션이 다음 상태일 때 같은 이름으로 resume 했다:
|
||||||
|
- stopped 이고 own id 가 기록됨
|
||||||
|
- stopped 이고 id 없음 (herdr-dead stop)
|
||||||
|
- reconcile 이 terminated 로 바꿈
|
||||||
|
- 세 경우 모두 herdr 가 **답하지 못해도**(None), cap 0 에서도 rc=0 이었다.
|
||||||
|
- `blockers` 의 `pinned_for_attempt` 와 `ended_after_attempt` (:158-171) 가 이 경우들을 걸러낸다.
|
||||||
|
- 정상 create/resume 이 끝난 뒤의 마커는 행이 running 인 동안에는 `_registered_for_attempt` (:177-180) 로 지워진다.
|
||||||
|
|
||||||
|
**실패 뒤 재시도**:
|
||||||
|
|
||||||
|
| 경우 | herdr 가 "없음" 응답 (서버 살아 있음) | herdr 무응답 (서버 꺼짐 등) |
|
||||||
|
|---|---|---|
|
||||||
|
| create X 가 행 쓰기 전에 실패(exit 42 롤백) → create X 재시도 | rc=0, 0.0 s | rc=1, cap 만큼 대기 |
|
||||||
|
| resume X 가 spawn 에서 실패(행 그대로) → resume X 재시도 | rc=0, 0.0 s | rc=1, cap 만큼 대기 |
|
||||||
|
| 위 resume 실패 → 같은 cwd 에서 create Y | rc=0, 0.0 s | rc=1, cap 만큼 대기 |
|
||||||
|
|
||||||
|
- 7cac6f6 사본으로 같은 시뮬레이션을 돌리면 무응답 열의 위 두 줄(같은 이름 재시도)이 rc=0 이다.
|
||||||
|
- 즉 1614af5 는 서버가 살아 있는 흔한 재시도는 그대로 두고, herdr 가 답하지 못하는 재시도만 막는다. 이는 아래 R1 이 같은 이름까지 넓어진 것이다.
|
||||||
|
|
||||||
|
## 4. 그 밖의 확인
|
||||||
|
|
||||||
|
브리프 항목 1·2·4·5·6·8(완료 조건 테스트, autopin 재사용, resume·stop, 지운 심볼, issue34 무관)은 1614af5 가 `session_ids.py`, `lib.sh`, 셸 스크립트, 문서를 건드리지 않으므로 이전 리뷰의 결론 그대로다.
|
||||||
|
|
||||||
|
**macOS**:
|
||||||
|
- `/usr/bin/python3` 3.9.6 으로 `lib_py.agents.id_order` import 가 성공한다(잡 1c6eca2b; 1614af5 는 문법 요소를 더하지 않는다).
|
||||||
|
- 잠금은 `fcntl.flock` 이다(:294-295).
|
||||||
|
|
||||||
|
**실제 herdr 를 건드리지 않았는가**:
|
||||||
|
- 기존 서버 두 개(pid 3407 `herdr server`, 8323 `herdr --session multi-agent-mux server`)는 스위트 전후로 같다(시작 시각 08:21).
|
||||||
|
- 스위트 중 13:22 에 `herdr --session mam-pylecturenotes server` 가 새로 보였다. 하지만 이 프로세스는 실제 HOME 과 `WORKSPACE_ROOT=/Users/godopu16/PuKi/lac/curriculum/01_python_getting_started_lecturenotes` 로 떠 있고, 사용자의 attach 클라이언트(13:27)가 붙어 있다. 테스트는 HOME 을 스크래치로 바꿔 돌므로 다른 작업 공간의 것이다.
|
||||||
|
|
||||||
|
## 5. 실행한 테스트
|
||||||
|
|
||||||
|
모두 `env -i`, 임시 HOME/XDG, `PYTHONDONTWRITEBYTECODE=1 -p no:cacheprovider --basetemp=<scratchpad>` 로 돌렸다. 테스트에서 .mam.env 를 export 하지 않았다.
|
||||||
|
|
||||||
|
| 대상 | 결과 |
|
||||||
|
|---|---|
|
||||||
|
| 검증 경로: `tests/test_issue63_same_cwd_ids.py` + 관련 19개 모듈 (issue40_auto_pin, issue64_ro_connect, issue10_13_purge, issue31_purge_residual, issue42_skill_blocks, tier1_unit, tier2_component, b4_session_created, issue24_resume_dialogs, issue30_stall_watch_stop, issue23_24_startup_path, issue45_restore, issue50_pane_lock, c1_tui_readiness, issue6_fail_closed, issue60_opencode_v2, issue60_linux_fixes, uuid_target, issue17_workspace_root_isolation) | **597 passed, 2 skipped, 0 failed** (32 분) |
|
||||||
|
| 같은 tree 스크래치 사본: issue63 단독 | 27 passed |
|
||||||
|
| base 사본 + head 테스트 | 27 errors (`ImportError: id_order`). 조건별 base 실패는 1차에서 22 failed + 1 passed 로 확인했다 |
|
||||||
|
| 변이: 1614af5 를 되돌림(자기 이름 마커를 drop) | 2 failed (위 두 테스트) |
|
||||||
|
|
||||||
|
## 6. 차단 결함
|
||||||
|
|
||||||
|
없음.
|
||||||
|
|
||||||
|
## 7. 비차단
|
||||||
|
|
||||||
|
### R1 (잡 1c6eca2b 의 R1, 범위가 넓어짐): herdr 가 답하지 못하는 고아 마커는 같은 폴더·같은 종류의 create/resume 을 매번 cap 동안 붙잡는다
|
||||||
|
|
||||||
|
- 이제 같은 이름 재시도도 포함된다(위 표).
|
||||||
|
- 서버가 꺼진 상태(server_not_running)가 대표적이다.
|
||||||
|
- 조회는 서버를 띄우지 않는다(:67).
|
||||||
|
- create/resume 은 대기를 통과해야 spawn 으로 서버를 띄운다(`create_session.sh:287`→:289, `resume_session.sh:243`→:260).
|
||||||
|
- 그래서 다른 무언가가 서버를 켜기 전까지는 기본 600 s 를 기다린 뒤 거부가 반복된다.
|
||||||
|
- 회복 경로는 있다: 메시지의 `HERDR_SESSION_NAME=<srv> herdr kill-session -t <s>` 를 shim 으로 실행하면 서버가 기동된다(잡 1c6eca2b 의 가짜 herdr 재현에서 `--session srv-down server` 1회).
|
||||||
|
- 생기는 조건은 "행 등록 전 실패나 kill"에 "서버가 꺼짐"이 겹치는 경우다. 정상 사용과 서버가 살아 있는 재시도는 막지 않는다(3절).
|
||||||
|
- 권고 (둘 중 하나):
|
||||||
|
- (a) server_not_running 만은 "없음"으로 읽는다. MAM 서버는 restore 가 꺼져 있어서 꺼진 서버 위에는 살아 있는 pane 이 없다. `lib.sh:204-207` 의 ISSUE-9 규약과도 맞는다.
|
||||||
|
- (b) 그 경우는 cap 을 기다리지 말고 바로 거부한다. 메시지는 "서버가 꺼져 있음 → 위 명령으로 정리"처럼 사실대로 쓴다.
|
||||||
|
- fail-closed 를 택한 것은 루프에서 정한 설계 선택이라 차단으로 두지 않는다.
|
||||||
|
|
||||||
|
### 그 밖의 비차단 (잡 1c6eca2b 와 같음)
|
||||||
|
|
||||||
|
- **N1**: 조회가 `MAM_NO_AUTOSTART=1` 을 쓴다는 것(:67)을 고정하는 테스트가 없다.
|
||||||
|
- **N2**: sanitize 한 이름 비교(:85)를 고정하는 테스트가 없다.
|
||||||
|
- **N3**: `flock(LOCK_EX)` 대기(:295)는 시간 제한이 없다. 다른 호출자가 앞 대기의 남은 시간만큼 더 기다릴 수 있다. 상한은 있다.
|
||||||
|
- **N4**: create·resume SKILL.md 의 exit 1 설명이 고아 마커로 거부되는 경우와 그 정리 방법을 말하지 않는다.
|
||||||
|
- **N5**: 이전 라운드의 비차단 항목이 남아 있다: resume `mam_id_order_wait` 제거가 테스트로 고정되지 않음, c6(b) 의 1.5 s 타이밍 flake, lib.sh `--herdr-server` 전달이 테스트되지 않음.
|
||||||
|
|
||||||
|
## 8. 결론
|
||||||
|
|
||||||
|
- 1614af5 는 codex 의 3차 지적(같은 이름의 죽은 owner 마커를 확인 없이 지움)을 닫는다. 8f64174·7cac6f6 과 같은 고아 규칙 하나로 모였다.
|
||||||
|
- 정상 create/resume 과 서버가 살아 있는 재시도는 막지 않는다.
|
||||||
|
- herdr 가 답하지 못할 때 cap 동안 기다리는 비용(R1)은 회복 경로가 있는 설계 선택이므로 비차단이다.
|
||||||
|
|
||||||
|
[VERDICT: PASS]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"loop_id": "loop-20261023T035928Z-2ccc02",
|
||||||
|
"base_sha": "dc886ecd6b38c01032a55a2d1c230c73362ae630",
|
||||||
|
"head_sha": "1614af5b673d7519d3a38f63e3636055592b4f14",
|
||||||
|
"tree_sha": "f1af6fdd548ee5d6f6b81e2202833edfaca98188",
|
||||||
|
"review_commit": "1614af5b673d7519d3a38f63e3636055592b4f14",
|
||||||
|
"reviewer": "planner-reviewer-codex-01",
|
||||||
|
"job_id": "12669b52",
|
||||||
|
"verdict": "PASS",
|
||||||
|
"transport": "herdr",
|
||||||
|
"evidence": "review-tree",
|
||||||
|
"isolation": "worktree",
|
||||||
|
"verified_at": "2026-10-23T05:16:58Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Reviewer report — PR #69 / Issue #63
|
||||||
|
|
||||||
|
Reviewed the required snapshot at `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261023T035928Z-2ccc02/1`, head `1614af5b673d7519d3a38f63e3636055592b4f14`, against base `dc886ecd6b38c01032a55a2d1c230c73362ae630`. The snapshot matches the expected tree `f1af6fdd548ee5d6f6b81e2202833edfaca98188` and is clean. I read Issue #63 and PR #69. The latest delta is limited to `id_order.py` and its regression tests.
|
||||||
|
|
||||||
|
## Previously blocking finding is closed
|
||||||
|
|
||||||
|
At the previous head (`7cac6f6`), `_read_and_prune_markers` dropped a dead-owner marker when its session name matched the caller, without checking herdr. The new code removes that exception: `.agents/skills/lib_py/agents/id_order.py:219-230` now keeps a dead-owner marker until its row is registered for that attempt or herdr returns an authoritative `False`; live and unknown answers both retain the orphan. `blockers()` includes a marker without a row even when its name is the caller's (`id_order.py:145-148`), so a retry is refused rather than replacing its marker.
|
||||||
|
|
||||||
|
The tests now cover same-name retries with a live owner, a dead owner and live herdr session, unknown herdr state, and a confirmed-gone session (`tests/test_issue63_same_cwd_ids.py:1117-1148`). The real-probe integration test also retries using the orphan's own name and checks that the marker is retained (`tests/test_issue63_same_cwd_ids.py:1287-1290`) and then safely replaced once the session is confirmed gone (`1312-1315`). The test fixture uses a mocked herdr and in-process probes; it does not contact a real herdr server.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `PYTHONDONTWRITEBYTECODE=1 .../python -m pytest -p no:cacheprovider --basetemp=/tmp/mam-review-12669b52 tests/test_issue63_same_cwd_ids.py` — **27 passed** on macOS, Python 3.9.6 (230.59 s). This includes the orphan liveness, unknown-result, zero-wait-cap, and same-name retry cases.
|
||||||
|
- Changed shell scripts checked with `/bin/bash -n` — passed.
|
||||||
|
- `git diff --check dc886ecd6b38c01032a55a2d1c230c73362ae630..HEAD` — passed.
|
||||||
|
- The earlier review covered the broader unchanged stop/purge/resume implementation and adjacent compatibility checks. This re-review did not repeat the base checkout or all adjacent test groups because the new delta is confined to dead-owner marker pruning and its regression tests.
|
||||||
|
- No real agent-home databases or real herdr sessions were accessed.
|
||||||
|
|
||||||
|
The latest change fixes the remaining blocking same-name orphan path. The earlier probe-failure and wait-cap fixes remain covered by the passing Issue #63 tests. I found no blocking issue in this revision.
|
||||||
|
|
||||||
|
[VERDICT: PASS]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"loop_id": "loop-20261023T035928Z-2ccc02",
|
||||||
|
"base_sha": "dc886ecd6b38c01032a55a2d1c230c73362ae630",
|
||||||
|
"head_sha": "1614af5b673d7519d3a38f63e3636055592b4f14",
|
||||||
|
"tree_sha": "f1af6fdd548ee5d6f6b81e2202833edfaca98188",
|
||||||
|
"review_commit": "1614af5b673d7519d3a38f63e3636055592b4f14",
|
||||||
|
"reviewer": "reviewer-agy-01",
|
||||||
|
"job_id": "cae1ea73",
|
||||||
|
"verdict": "PASS",
|
||||||
|
"transport": "herdr",
|
||||||
|
"evidence": "review-tree",
|
||||||
|
"isolation": "worktree",
|
||||||
|
"verified_at": "2026-10-23T05:16:58Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# PR #69 (Issue #63) Round 3 Code Review Report — `agy` (`reviewer-agy-01`)
|
||||||
|
|
||||||
|
- **Job ID**: `cae1ea73`
|
||||||
|
- **Branch / Range**: `fix/issue-63` (`dc886ecd6b38c01032a55a2d1c230c73362ae630..1614af5b673d7519d3a38f63e3636055592b4f14`, 13 commits)
|
||||||
|
- **Tree**: `f1af6fdd548ee5d6f6b81e2202833edfaca98188`
|
||||||
|
- **Verification Worktree**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261023T035928Z-2ccc02/1`
|
||||||
|
- **Environment**: macOS (`Darwin arm64`), `/bin/bash 3.2.57(1)-release`, `/usr/bin/python3 3.9.6`, `.venv/bin/python 3.9.6`
|
||||||
|
- **Summary**: **PASS** (`P0: 0`, `P1: 0`, `P2: 0`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 2·3차 리뷰 후속 커밋 (`ede9c62`, `8f64174`, `7cac6f6`, `1614af5`) 집중 검증 결과
|
||||||
|
|
||||||
|
### 1.1 권위 있는 "세션 없음" 응답에서만 고아 마커 삭제 (`8f64174`, `id_order.py:58-90,225-227`)
|
||||||
|
- `herdr_session_state(server, session, timeout)` (`id_order.py:58-90`)는 `MAM_NO_AUTOSTART="1"` 및 `HERDR_SESSION_NAME=server or "default"` 환경에서 `source "$1" && _herdr agent list`를 실행합니다.
|
||||||
|
- `res.returncode == 0`이고 `json.loads(res.stdout)["result"]["agents"]`가 리스트이며, 그 안에 `session` 및 `sanitize_herdr_agent_name(session)`이 모두 없을 때에만 `False`를 반환합니다 (`id_order.py:74-83`).
|
||||||
|
- `_read_and_prune_markers` (`id_order.py:225-227`)는 `drop = live is False`로 판정하여, 조회 실패·타임아웃·파싱 실패(`live is None`) 시에는 마커를 삭제하지 않고 `_orphan = "unknown"`으로 유지합니다.
|
||||||
|
|
||||||
|
### 1.2 `server_not_running` 불확실 처리 및 `MAM_ID_ORDER_WAIT_S=0` 무프로브 보장 (`7cac6f6`, `id_order.py:58-90,280-291,330-346`)
|
||||||
|
- `server_not_running`을 포함한 모든 비정상 종료(`res.returncode != 0`)에서 `herdr_session_state`가 `None`을 반환하므로 서버 다운 상태에서도 고아 마커가 삭제되지 않고 fail-closed로 차단됩니다.
|
||||||
|
- `bounded_probe` (`id_order.py:285-290`)가 `deadline - time.monotonic()`을 `timeout`으로 전달하고, `herdr_session_state` 첫 줄(`if timeout <= 0: return None`, `id_order.py:64-65`)에서 즉시 반환하므로:
|
||||||
|
- 다수의 죽은 마커가 있더라도 전체 프로브 시간이 `MAM_ID_ORDER_WAIT_S` 상한 안에 엄격히 묶입니다 (`test_id_order_herdr_probes_stay_within_the_wait_cap`).
|
||||||
|
- `MAM_ID_ORDER_WAIT_S=0`(`cap == 0`)일 때는 herdr 서브프로세스를 전혀 호출하지 않고 즉시 거부하며 `(MAM_ID_ORDER_WAIT_S=0 leaves no time to ask herdr)` 안내를 출력합니다 (`id_order.py:336-338`).
|
||||||
|
- `answers: Dict[str, bool]` (`id_order.py:283,287-289,333`)가 이전 폴링에서 확인된 확정 응답을 보존하여 마지막 폴링 시점에 예산이 소진되더라도 `"its herdr session is alive"` 메시지를 정확히 유지합니다.
|
||||||
|
|
||||||
|
### 1.3 동일 세션 이름(`sess == me`)의 죽은 owner 마커에 동일한 고아 규칙 적용 (`1614af5`, `id_order.py:180-235,305`)
|
||||||
|
- `_read_and_prune_markers`에서 `sess == me`일 때 무조건 삭제하던 분기를 제거하여(`id_order.py:217-227`), 이전 `create`/`resume`이 pane을 띄운 직후 죽어 고아 세션을 남긴 상태에서 **같은 세션 이름**으로 재시도하더라도:
|
||||||
|
- 고아 herdr 세션이 살아 있거나 확인 불가(`True`/`None`)이면 마커를 유지하고 차단합니다 (`blockers()`는 `m_sess == me`를 건너뛰지 않음, `id_order.py:130-145`).
|
||||||
|
- herdr가 해당 세션이 사라졌음을 확인(`False`)하면 죽은 마커를 삭제하고 새 시도의 마커로 교체(`owner_pid == my_pid`)하여 정상 진입합니다 (`test_id_order_dead_owner_marker_blocks_while_its_unregistered_herdr_session_is_alive`, `test_id_order_real_probe_against_mock_herdr_fails_closed`).
|
||||||
|
|
||||||
|
### 1.4 세 수정 간 정합성 및 일반 사용(정상 create/resume, 재시도) 600초 대기 미발생 검증
|
||||||
|
- **정상 `create` 후 진행 중**: 첫 세션이 YAML 행을 `status: "running"` 및 `herdr_session_epoch > prev_epoch`로 등록하면 `_registered_for_attempt` (`id_order.py:174-177`)가 `True`를 반환해 herdr 프로브 없이 마커가 즉시 삭제되고, 이후에는 YAML 행의 `own_id` 기록 여부에 따라 대기가 결정됩니다.
|
||||||
|
- **정상 `stop` 후 후속 `create`/`resume`**: 세션이 정상 중지되면 `own_id`가 기록되거나(`pinned_for_attempt == True`) `stopped_at_epoch > max(m_at, prev_end_epoch)`(`ended_after_attempt == True`)가 성립하므로 `blockers()` (`id_order.py:150-163`)가 이를 차단 목록에서 즉시 제외하여 600초 대기가 발생하지 않습니다.
|
||||||
|
- **`create` 실패·롤백(`exit 42`) 후 재시도**: `create_session.sh`의 `cleanup_herdr_on_error` 트랩(`create_session.sh:337-368`)이 실행 중인 워크스페이스 herdr 서버에서 세션을 즉시 종료하므로, 재시도 시 첫 폴링의 `_herdr agent list`가 빈 에이전트 목록(`False`)을 반환해 죽은 마커를 즉시 정리하고 대기 없이 통과합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 전체 체크리스트 (1~8) 점검 요약
|
||||||
|
|
||||||
|
| # | 점검 항목 | 결과 | 근거 파일 및 라인 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | 완료 조건 1~8 및 테스트 커버리지 | **PASS** | `session_ids.py:116-297`, `id_order.py:1-382`, `tests/test_issue63_same_cwd_ids.py` (총 27개 테스트) |
|
||||||
|
| 2 | `#40` `autopin.capture` 재사용 (판정 단일화) | **PASS** | `session_ids.py:19,176,272` (`from lib_py.agents.autopin import OWN_KEYS, capture, claimed_elsewhere`), `lib.sh` 구 `capture_conversation_id` 제거 |
|
||||||
|
| 3 | `id_order.py` 잠금·만료·비차단 조건·`MAM_ID_ORDER_WAIT_S` | **PASS** | `fcntl.flock` (`id_order.py:293`), `wait_s` (`id_order.py:36-41`), `herdr_session_state` (`id_order.py:58-83`), `_read_and_prune_markers` (`id_order.py:180-235`) |
|
||||||
|
| 4 | `resume`: `--drop-stale-id`, 사전 거부(exit 1), 이미 실행 중 분기, `SKILL.md` | **PASS** | `resume_session.sh:132-170,243,270-276`, `update_yaml_resumed.sh:21,66-197`, `multi-agent-mux-resume/SKILL.md:59-83,165-171` |
|
||||||
|
| 5 | `stop`: kill 전 ID 판정, 죽은 herdr 미기록+이유 기록, purge 재검증 | **PASS** | `stop_session.sh:104-179,233-286,308-358`, `multi-agent-mux-stop/SKILL.md:38-55,98-115` |
|
||||||
|
| 6 | 삭제된 심볼 잔존 참조 없음 (`capture_conversation_id`, `CAPTURE_ID`) | **PASS** | `git grep` 결과 코드·문서·테스트 내 잔존 참조 0건 |
|
||||||
|
| 7 | macOS 호환성 (`/bin/bash 3.2.57`, `/usr/bin/python3 3.9.6`) | **PASS** | 수정된 셸 6개 `/bin/bash -n` 통과, Python 3.9 AST 파싱 및 모듈 import(`PY39_IMPORT_OK`) 통과 |
|
||||||
|
| 8 | `test_issue34_job_tree.py::test_t5b_a_*[False]` 관련성 여부 | **PASS** | 본 브랜치는 루프·잡 트리(`lib_py/loop`, `delegate-job`)를 전혀 수정하지 않으며, 해당 테스트는 본 변경과 무관함 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 실행한 검증 명령 및 결과
|
||||||
|
|
||||||
|
1. **정적 검사 (`git diff --check`, `/bin/bash -n`, `/usr/bin/python3` 3.9.6 import)**:
|
||||||
|
- Exit `0` (`PY39_IMPORT_OK`).
|
||||||
|
2. **Issue #63 `id_order` 및 이식성 테스트 + 실제 `herdr` 호출 감시(Spy) 검증**:
|
||||||
|
- `pytest tests/test_issue63_same_cwd_ids.py -k "test_id_order or test_c6_id_order_markers or test_portability" -v`
|
||||||
|
- 결과: **`8 passed`**, `SPY_LOG` 비어 있음 (`0` calls).
|
||||||
|
3. **Herdr 격리 및 Skill 블록 인벤토리 정합성 테스트**:
|
||||||
|
- `pytest tests/test_herdr_real_server_leak.py tests/test_issue42_skill_blocks.py -v`
|
||||||
|
- 결과: **`40 passed, 1 skipped in 1.52s`**.
|
||||||
|
4. **Negative Regression 검증**:
|
||||||
|
- Base(`dc886ec`): `test_issue63_same_cwd_ids.py` 12개 실패 확인.
|
||||||
|
- `ede9c62` 대비: `test_id_order_real_probe_against_mock_herdr_fails_closed`가 `protocol_mismatch`에서 `assert 0 == 1`로 실패하고 `HEAD`에서 통과함을 확인.
|
||||||
|
- `7cac6f6`(`HEAD~1`) 대비: `1614af5`의 동일 세션 이름 고아 마커 검증(`assert wait_same() == 1`)이 `HEAD~1`에서 `assert 0 == 1`로 실패하고 `1614af5`(`HEAD`)에서 통과함을 확인.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 발견 사항
|
||||||
|
|
||||||
|
- 차단 결함(P0/P1) 및 비차단 결함(P2) 없음.
|
||||||
|
|
||||||
|
[VERDICT: PASS]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"loop_id": "loop-20261023T035928Z-2ccc02",
|
||||||
|
"base_sha": "dc886ecd6b38c01032a55a2d1c230c73362ae630",
|
||||||
|
"head_sha": "1614af5b673d7519d3a38f63e3636055592b4f14",
|
||||||
|
"tree_sha": "f1af6fdd548ee5d6f6b81e2202833edfaca98188",
|
||||||
|
"review_commit": "1614af5b673d7519d3a38f63e3636055592b4f14",
|
||||||
|
"reviewer": "reviewer-grok-01",
|
||||||
|
"job_id": "dee00bc0",
|
||||||
|
"verdict": "PASS",
|
||||||
|
"transport": "herdr",
|
||||||
|
"evidence": "review-tree",
|
||||||
|
"isolation": "worktree",
|
||||||
|
"verified_at": "2026-10-23T05:16:58Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# Review dee00bc0 — PR #69 / Issue #63, fourth Mac pass
|
||||||
|
|
||||||
|
PASS. Head `1614af5` keeps a dead-owner marker whose session name is the caller's own until herdr returns a parsed agent list that omits that name, or until the row is registered for the attempt. A live owner of that name is kept. An empty session name is still dropped. A failed lookup, `server_not_running` included, and a wait cap of 0 leave the marker in place. A normal create or resume whose `has-session` already sees the name returns before this wait. A retry whose probe says the session is gone returns on that probe.
|
||||||
|
|
||||||
|
Reviewed only the snapshot `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261023T035928Z-2ccc02/1` (`loop-20261023T035928Z-2ccc02`). Head `1614af5b673d7519d3a38f63e3636055592b4f14`, tree `f1af6fdd548ee5d6f6b81e2202833edfaca98188`. Base `dc886ecd6b38c01032a55a2d1c230c73362ae630` (main, v4.9.9) is an ancestor and is the control-plane HEAD. Thirteen commits, 14 files, +2234/−102. Against the previous review head `7cac6f6` the delta is 2 files, +42/−16: `.agents/skills/lib_py/agents/id_order.py` and `tests/test_issue63_same_cwd_ids.py`. No uncommitted changes. `git diff --check` is clean. Snapshot porcelain is empty and the snapshot has no `.mam`. Control-plane porcelain is empty. No product file was edited. The source worktree `mam-wt-pr69` was not run.
|
||||||
|
|
||||||
|
The new commit is `1614af5` `fix(id_order): a dead-owner marker on the caller's own session follows the orphan rules`. It closes the residual recorded on `7cac6f6`, where a dead owner of the waiter's own session name was dropped before herdr was asked. `8f64174` and `7cac6f6` stay underneath it. HEAD still prunes only on an authoritative gone, still treats `server_not_running` as unknown, and still asks herdr nothing at cap 0. The same-name case now uses that branch.
|
||||||
|
|
||||||
|
Host tools: control-plane venv Python 3.9.6, sqlite 3.51.0, `/bin/bash` 3.2.57(1)-release. The real agent databases were not opened.
|
||||||
|
|
||||||
|
## Same-name orphan
|
||||||
|
|
||||||
|
`_read_and_prune_markers` no longer takes the waiter session name (`id_order.py:183-186`, call at `308-309`). A dead owner's marker (`id_order.py:217-229`):
|
||||||
|
|
||||||
|
- A missing owner pid, or an owner pid equal to this waiter, drops the marker (`219-220`).
|
||||||
|
- A live owner keeps it, including a live attempt on the caller's session (`221-222`).
|
||||||
|
- An empty session name drops it (`223-224`).
|
||||||
|
- A row registered for the attempt drops it without a probe. Registered means status `running` and `herdr_session_epoch` greater than the marker's `prev_epoch` (`177-180`, `225-226`).
|
||||||
|
- Otherwise the probe runs. `drop` is true only when it returns false. True and `None` keep the marker, and `_orphan` is `"alive"` or `"unknown"` (`227-229`). A marker left by an earlier attempt on the caller's own session name takes this path.
|
||||||
|
|
||||||
|
`herdr_session_state` (`id_order.py:58-86`) is unchanged in this commit. False is only a successful, parsed `_herdr agent list` whose agents omit the session name and `sanitize_herdr_agent_name` of that name (`84-86`). A non-zero exit, a timeout, an `OSError`, or JSON that has no `result.agents` list returns `None` (`75-83`). `timeout <= 0` returns `None` before any subprocess (`64-65`). Measured on this snapshot: `herdr_session_state(..., 0)` and `herdr_session_state(..., -1)` are both `None`. `wait_s({})` is 600, `wait_s` of `"0"` is 0, and `wait_s` of `"abc"` is 600 (`36-41`).
|
||||||
|
|
||||||
|
The child environment sets `MAM_NO_AUTOSTART=1` and `HERDR_SESSION_NAME` to the marker's server, or `default` when that field is empty (`67`). The shim skips autostart when that variable is `1` (`lib.sh:203-208`). `bounded_probe` passes the time left on the deadline (`287-291`). The call sits inside `fcntl.flock(LOCK_EX)`. macOS has no `flock(1)`. The ordering tests passed with `fcntl`.
|
||||||
|
|
||||||
|
`blockers` includes a kept marker of the caller's own session when that row is absent (`133-147`). The waiter then holds until the marker is pruned or the cap expires (`325-347`). On cap expiry the message uses the last definite answer from this wait (`333-344`). An unknown orphan at cap 0 adds `MAM_ID_ORDER_WAIT_S=0 leaves no time to ask herdr`.
|
||||||
|
|
||||||
|
## Ordinary create, resume, and retry
|
||||||
|
|
||||||
|
Create calls `has-session` and exits 3 when the name is already present (`create_session.sh:168-170`). The id-order wait is later (`287`). Resume sets `HERDR_ALIVE` from `has-session` (`resume_session.sh:125-128`) and, when the session is alive, updates the row and exits (`158-169`) before the wait (`243`). The mock `has-session` exits 0 when the agent name is in its state (`conftest.py:1345-1355`). A retry that herdr still lists under that name therefore stops at `has-session` and does not enter the 600-second cap.
|
||||||
|
|
||||||
|
The wait itself returns 0 on the first lock hold when `blockers` is empty (`310-323`). That is a create or resume with no live same-cwd marker and no running row that still lacks an id. The default 600 is the cap for a marker that stays, and the function does not sleep it out on an empty blocker list.
|
||||||
|
|
||||||
|
When the caller has already passed `has-session` and a dead-owner marker of the same name remains:
|
||||||
|
|
||||||
|
- A parsed list that omits the name removes the marker and returns 0 on that pass (`228-229`, then `310-323`). The real-probe test does this with cap 2 on the marker's own session name and checks that the replacement marker's owner pid is the waiter (`test_issue63_same_cwd_ids.py:1315-1317`).
|
||||||
|
- A parsed list that contains the name keeps the dead owner's pid and returns 1 inside the cap (`1290-1292`). The unit test at `wait_s_val=0` with an injected `pane_state` prints `its herdr session is alive` and `kill-session -t same-agy` (`1133-1140`).
|
||||||
|
- `None`, which covers a failed lookup, `server_not_running`, and a real cap of 0, keeps the marker and the waiter uses the cap. That is the same rule `7cac6f6` already applies to every other orphan. The real-probe cap-0 assertion is on the other session name in the same test (`1306-1310`); the same-name branch calls the same `herdr_session_state`.
|
||||||
|
|
||||||
|
The head suite finished in 1301.73 s, in line with the previous pass on `7cac6f6` (1263.90 s). A common create or resume that now slept out 600 seconds would have stretched that run. It did not.
|
||||||
|
|
||||||
|
## Tests for this commit
|
||||||
|
|
||||||
|
`no_herdr_probe` (`test_issue63_same_cwd_ids.py:32-36`) makes an in-process `herdr_session_state` return false unless the test passes `pane_state`. The shell tests start a child, and that child runs the real function.
|
||||||
|
|
||||||
|
`test_id_order_dead_owner_marker_blocks_while_its_unregistered_herdr_session_is_alive` (`1047`) still covers another session's orphan: alive keeps and prints the kill line, unknown keeps, a registered running row drops the marker with an empty probe list, and a false answer admits the next session. The same-name block (`1120-1148`) keeps a live owner's pid, keeps a dead owner's pid while the injected probe returns true or `None`, and on false replaces the owner pid with the waiter's pid.
|
||||||
|
|
||||||
|
`test_id_order_real_probe_against_mock_herdr_fails_closed` (`1250`) sources the sandbox `lib.sh` and calls `mam_id_order_wait`. A listed agent returns 1 for both the other session name and the marker's own name, and the dead owner's pid stays (`1287-1292`). `protocol_mismatch` and `server_not_running` return 1 with `herdr could not tell whether its session is gone` (`1294-1304`). The mock exits 1 for `force_error_code` before the subcommand (`conftest.py:486-488`). Cap 0 returns 1 with `MAM_ID_ORDER_WAIT_S=0 leaves no time to ask herdr` and keeps the marker (`1306-1310`). An unlisted agent returns 0 for the other name, and a following wait on the marker's own name returns 0 with a new owner pid (`1312-1317`). The mock `agent list` prints `{"result": {"agents": [...]}}` with each agent's `name` (`conftest.py:794-806`). A parsed list that contains the name is what prints `its herdr session is alive`.
|
||||||
|
|
||||||
|
`mock_herdr` prepends its bin (`conftest.py:1411`). These shell tests passed in the head run below. The unlisted own-name case returning 0, and the listed own-name case returning 1, are the evidence the probe reached the mock.
|
||||||
|
|
||||||
|
`test_id_order_create_waits_for_orphan_herdr_session_of_a_killed_create` (`1160`) and `test_id_order_herdr_probes_stay_within_the_wait_cap` (`1207`) are unchanged by this commit and passed. The create test uses a different session name and cap 3. The cap test's cap 0 finishes with every budget `<= 0` and both markers still on disk.
|
||||||
|
|
||||||
|
## Rest of base..head
|
||||||
|
|
||||||
|
The twelve commits through `7cac6f6` are the tree reviewed in job 737239ab. This commit does not change `session_ids.py`, `lib.sh`, the stop, resume, or create scripts, or the skill exit tables. `session_ids.py` still uses `#40`'s `capture` and `claimed_elsewhere`. Stop still decides the id before kill. Resume still resolves the id and refuses a claimed id before spawn. `git grep` for `capture_conversation_id` and `CAPTURE_ID`, excluding `.agents/reports` and `IMPROVEMENTS.md`, finds no caller. The diff names no loop or job-tree file, so the author's one-off `tests/test_issue34_job_tree.py::test_t5b_a_*[False]` failure is outside this change. `/bin/bash -n` accepts the snapshot `lib.sh`. Python 3.9.6 imports `id_order`. The annotations sit behind `from __future__ import annotations`.
|
||||||
|
|
||||||
|
## Results
|
||||||
|
|
||||||
|
Pytest used the control-plane venv, `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider`, and `--basetemp` `/tmp/mamdee-dee00bc0-pytest` (head) and `/tmp/mamdee-dee00bc0-base-pytest` (base). Neither path contains `mam-review` or `shim`. Inherited `HERDR_*`, `MAM_*`, `MQTT_*`, `SKS_*`, `DELEGATE_JOB_LOGS_DIR`, `HOME_DIR`, `CLAUDE_*`, `CODEX_HOME`, `PYTHONPATH`, `WORKSPACE_ROOT`, `MAM_ENV_FILE`, `MAM_JOBS_DIR`, `AGENT_SESSIONS_YAML`, the four `XDG_*` homes, and `AGENT_PYTHON_BIN` were dropped.
|
||||||
|
|
||||||
|
Head, cwd the snapshot: `tests/test_issue63_same_cwd_ids.py`, `test_issue40_auto_pin.py`, `test_issue64_ro_connect.py`, `test_issue10_13_purge.py`, `test_issue31_purge_residual.py`, `test_issue42_skill_blocks.py`, `test_tier1_unit.py`, `test_tier2_component.py`, `test_uuid_target.py`, `test_issue24_resume_dialogs.py`. **359 passed, 1 skipped** in 1301.73 s. Exit 0. The log closed at 2026-10-23 14:09:03. `.claude/skills` is absent in this snapshot, and `test_claude_skills_link_resolves_to_agents_skills` skips in that case (`test_issue42_skill_blocks.py:266`). That file is outside this delta. The same-name assertions are inside the two existing probe tests, so the pass count matches the previous head.
|
||||||
|
|
||||||
|
Base, this turn: a throwaway `git clone --shared` checked out at `dc886ecd6b38c01032a55a2d1c230c73362ae630`, with only `tests/test_issue63_same_cwd_ids.py` copied from head. `id_order.py` is absent there. **27 errors** in 0.11 s. Exit 1. Each error is `ImportError: cannot import name 'id_order' from 'lib_py.agents'` at setup of `no_herdr_probe`, including `test_id_order_dead_owner_marker_blocks_while_its_unregistered_herdr_session_is_alive`, `test_id_order_real_probe_against_mock_herdr_fails_closed`, and `test_c4`. The snapshot was not checked out. Snapshot porcelain stayed empty.
|
||||||
|
|
||||||
|
The behavioral base result for this same base, on the test file from before that fixture imported `id_order`, remains the one recorded by job ba3eeb7b: **22 failed, 1 passed** in 181.15 s. The pass was `test_c4_pinned_post_discovered_and_preassigned_agents_unchanged`. On that run the second stop row received A's id, purge printed `purged:`, the unpinned resume carried A's id, and cap 0 still spawned. Those tests passed on this head inside the 359.
|
||||||
|
|
||||||
|
1. Stop order A→B and B→A, including herdr already dead, records only a verified own id. `test_c1_stop_same_cwd_agy_sessions_records_only_verified_owner` passed on head.
|
||||||
|
2. Purge of B leaves A's files. `test_c2_purge_never_deletes_another_rows_conversation` passed on head.
|
||||||
|
3. Resume of an unpinned B stays off A's conversation, and the already-running branch keeps the epoch. `test_c3_resume_unpinned_second_agy_row_starts_fresh_and_running_branch_keeps_epoch` passed on head.
|
||||||
|
4. A row that already has an own id, and claude/grok, stay on the previous path. `test_c4_pinned_post_discovered_and_preassigned_agents_unchanged` passed on head, along with `test_issue40_auto_pin.py`, `test_issue64_ro_connect.py`, and `test_issue10_13_purge.py`.
|
||||||
|
5. A same-cwd create waits while another post-discovered row of the same agent has no id. `test_c6_create_waits_or_refuses_while_same_cwd_post_discovered_row_has_no_id` passed on head. The orphan tests cover a dead owner, including one on the caller's own session name, whose herdr session is alive, unknown, or gone.
|
||||||
|
6. Resume refuses before spawn when the resolved id belongs to another row. `test_c7_resume_refuses_when_recorded_id_is_claimed_by_another_row` passed on head.
|
||||||
|
|
||||||
|
## Blocking defects
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Non-blocking
|
||||||
|
|
||||||
|
- While herdr keeps failing the lookup, the orphan marker stays, and every later create or resume that reaches the wait — a retry of the marker's own session name included — waits out `MAM_ID_ORDER_WAIT_S` again (`id_order.py:75-76`, `227-229`). The wait is bounded. Create and resume still return at `has-session` when that command already sees the name (`create_session.sh:168-170`, `resume_session.sh:158-169`). The cap message names `herdr kill-session`.
|
||||||
|
- The probe holds the id-order lock until it returns. The budget is the time left on the deadline, and at most 30 seconds (`id_order.py:73`, `287-309`).
|
||||||
|
- An empty `herdr_server` is probed on the `default` server (`id_order.py:67`). Create and resume export `HERDR_SESSION_NAME` before they write a new marker (`create_session.sh:185-186`, `resume_session.sh:111-114`). The cap line prints `default` for an empty server and leaves the server and session unquoted (`id_order.py:335-344`).
|
||||||
|
- A successful agent list that is briefly empty while a pane is still registering would prune the marker. The tests cover a list that already contains the name, and a list that does not.
|
||||||
|
- One remaining same-type row with herdr dead still uses `find_workspace_uuid` (`session_ids.py:274-275`).
|
||||||
|
- A running row with an empty own id and a missing pane pid blocks until the cap (`id_order.py:128-131`). The unit test asserts that return of 1 (`test_issue63_same_cwd_ids.py:1150-1154`).
|
||||||
|
- A marker whose owner pid has been reused by an unrelated live process stays until that process exits (`id_order.py:221-222`).
|
||||||
|
- With herdr alive, a recorded id that fails revalidation is still returned (`session_ids.py:240-241`).
|
||||||
|
- An unparsable `MAM_ID_ORDER_WAIT_S` becomes 600 (`id_order.py:36-41`).
|
||||||
|
- `stop_session.sh:220` uses `eval "$(mam_session_ids stop ...)" || true`. A normal decision returns 0. A crashed helper leaves the captured fields empty and the stop continues.
|
||||||
|
- `--no-onboard` waits out the cap. Presence-lock id discovery is not in this PR. Both are the deferrals named by the issue and the PR body.
|
||||||
|
- `resolve_session_id.sh:4-5` still describes an empty line and exit 0. The script calls `mam_session_ids` under `set -euo pipefail` (line 10). The resume skill exit table documents exit 1 when another row claims the id.
|
||||||
|
- Read-only connect residuals from #64 (immutable race, `mode=ro` sidecar rewrite, purge DELETE staying read-write) are outside this diff. `_ro_connect` is unchanged.
|
||||||
|
|
||||||
|
## Host check
|
||||||
|
|
||||||
|
No process has a first command token of `opencode` or a path ending in `/opencode`. `lsof` on `~/.local/share/opencode/opencode.db` exited 1. That database was not opened. Its mtime is 1792641027 and its size is 36712448. `opencode.log` is absent. `~/.config/opencode/service.json` mtime is 1792246838 and its size is 64. `~/.local/state/opencode/service.json` is absent. The herdr config mtime is 1784456394. `~/.local/state/mam` mtime is 1791810241. `~/.local/state/mam/id-order` is absent. These stats match the check taken before the suite.
|
||||||
|
|
||||||
|
Eight herdr processes were running immediately before the suite. After the suite log closed at 2026-10-23 14:09:03, the 14:09:37 check found seven. Two pids from the before-set were gone: one `/opt/homebrew/bin/herdr` whose session argument was `multi-agent-mux` (about 40 seconds old at suite start) and one whose session argument was `mam-pylecturenotes` (about 3 seconds old at suite start). The new process is `/opt/homebrew/bin/herdr` with `--session multi-agent-mux`, elapsed time about 54 seconds at 14:09:37, so it started near 14:08:43, during the last half-minute of the suite. No herdr command line contained the review basetemp, the snapshot path, or the test session names `probe-ws` and `orphan-ws`. This review did not start or stop a herdr server.
|
||||||
|
|
||||||
|
After this report file was written, the same check was repeated at 2026-10-23 14:11:38. Snapshot porcelain is empty and the snapshot has no `.mam`. Control-plane porcelain is empty at `dc886ecd6b38c01032a55a2d1c230c73362ae630`. No process has a first command token of `opencode` or a path ending in `/opencode`, and `lsof` still reports no database holder. The database mtime is still 1792641027 and its size is still 36712448. `opencode.log` is still absent. The config service file mtime is still 1792246838. The state service file is still absent. The herdr config mtime is still 1784456394. `~/.local/state/mam` mtime is still 1791810241, and `id-order` is still absent. Seven herdr processes were present. The pid set differs from the 14:09:37 set by one homebrew herdr process whose arguments include `--session multi-agent-mux`; its elapsed time was about 54 seconds, so that process had turned over again. No herdr command line contained the review basetemp, the snapshot path, or `probe-ws` or `orphan-ws`. This review did not start or stop it.
|
||||||
|
|
||||||
|
[VERDICT: PASS]
|
||||||
@@ -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.9.9"
|
MAM_VERSION="4.10.0"
|
||||||
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}"
|
||||||
|
|
||||||
|
|||||||
@@ -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.9.9"
|
FRAMEWORK_VERSION = "4.10.0"
|
||||||
|
|
||||||
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.9.9
|
version: 4.10.0
|
||||||
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.9.9
|
version: 4.10.0
|
||||||
author: godopu
|
author: godopu
|
||||||
license: MIT
|
license: MIT
|
||||||
platforms: [linux, macos, windows]
|
platforms: [linux, macos, windows]
|
||||||
|
|||||||
@@ -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.9.9
|
version: 4.10.0
|
||||||
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.9.9
|
version: 4.10.0
|
||||||
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.9.9
|
version: 4.10.0
|
||||||
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.9.9
|
version: 4.10.0
|
||||||
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.9.9
|
version: 4.10.0
|
||||||
author: godopu
|
author: godopu
|
||||||
license: MIT
|
license: MIT
|
||||||
platforms: [linux, macos]
|
platforms: [linux, macos]
|
||||||
|
|||||||
@@ -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.9.9
|
version: 4.10.0
|
||||||
author: godopu
|
author: godopu
|
||||||
license: MIT
|
license: MIT
|
||||||
platforms: [linux, macos]
|
platforms: [linux, macos]
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
본 문서는 `herdr_agent_orchestration` 오케스트레이션 및 메시징 백플레인 워크플로우를 새로운 프로젝트에 도입하여 이식하고, 새로운 개발자/에이전트가 초기 가동을 시작할 때 수행해야 하는 환경 설정 및 구축 절차를 안내합니다.
|
본 문서는 `herdr_agent_orchestration` 오케스트레이션 및 메시징 백플레인 워크플로우를 새로운 프로젝트에 도입하여 이식하고, 새로운 개발자/에이전트가 초기 가동을 시작할 때 수행해야 하는 환경 설정 및 구축 절차를 안내합니다.
|
||||||
|
|
||||||
[](VERSIONS.md)
|
[](VERSIONS.md)
|
||||||
|
|
||||||
새로운 에이전트는 이 안내서에 기술된 절차를 순차적으로 실행하여 초기 환경을 안정적으로 설정할 수 있습니다.
|
새로운 에이전트는 이 안내서에 기술된 절차를 순차적으로 실행하여 초기 환경을 안정적으로 설정할 수 있습니다.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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.
|
||||||
|
|
||||||
[](VERSIONS.md)
|
[](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
@@ -1,6 +1,6 @@
|
|||||||
# Messaging System Technical Analysis & Architecture Report
|
# Messaging System Technical Analysis & Architecture Report
|
||||||
|
|
||||||
Applies to: MAM v4.9.9
|
Applies to: MAM v4.10.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.
|
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
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[herdr](https://herdr.dev)와 MQTT 위에 만든 **다중 에이전트 오케스트레이션·메시징 백플레인**입니다. 장시간 실행되는 코딩 에이전트(`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`)를 격리된 herdr pane 에서 돌리고, 작업을 추적되는 잡으로 위임하며, 모든 리뷰어가 통과시킬 때까지 planner / creator / reviewer 루프를 돌립니다.
|
[herdr](https://herdr.dev)와 MQTT 위에 만든 **다중 에이전트 오케스트레이션·메시징 백플레인**입니다. 장시간 실행되는 코딩 에이전트(`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`)를 격리된 herdr pane 에서 돌리고, 작업을 추적되는 잡으로 위임하며, 모든 리뷰어가 통과시킬 때까지 planner / creator / reviewer 루프를 돌립니다.
|
||||||
|
|
||||||
[](VERSIONS.md)
|
[](VERSIONS.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
A **Multi-Agent Orchestration & Messaging Backplane** built on [herdr](https://herdr.dev) and MQTT. It runs long-lived coding agents (`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`) in isolated herdr panes, delegates work to them as tracked jobs, and drives planner / creator / reviewer loops until every reviewer passes the change.
|
A **Multi-Agent Orchestration & Messaging Backplane** built on [herdr](https://herdr.dev) and MQTT. It runs long-lived coding agents (`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`) in isolated herdr panes, delegates work to them as tracked jobs, and drives planner / creator / reviewer loops until every reviewer passes the change.
|
||||||
|
|
||||||
[](VERSIONS.md)
|
[](VERSIONS.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+43
-11
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
## 📌 현재 버전 개요 (Current Release)
|
## 📌 현재 버전 개요 (Current Release)
|
||||||
|
|
||||||
- **프레임워크 버전**: `v4.9.9`
|
- **프레임워크 버전**: `v4.10.0`
|
||||||
- **최신 릴리스 일시**: 2026-10-21 (KST)
|
- **최신 릴리스 일시**: 2026-10-23 (KST)
|
||||||
- **기준 브랜치**: `main`
|
- **기준 브랜치**: `main`
|
||||||
- **핵심 아키텍처**:
|
- **핵심 아키텍처**:
|
||||||
- **Terminal 상태 단방향 불변성 (`engine.py`, Issue #6 Phase 1)**: `completed`, `error`, `cancelled` 터미널 상태 진입 후 후속 이벤트나 업데이트에 의한 덮어쓰기를 엄격히 차단(Terminal Immutability). `registry.py close` 서브커맨드 및 fail-closed 조기 종료 도입.
|
- **Terminal 상태 단방향 불변성 (`engine.py`, Issue #6 Phase 1)**: `completed`, `error`, `cancelled` 터미널 상태 진입 후 후속 이벤트나 업데이트에 의한 덮어쓰기를 엄격히 차단(Terminal Immutability). `registry.py close` 서브커맨드 및 fail-closed 조기 종료 도입.
|
||||||
@@ -24,23 +24,55 @@
|
|||||||
|
|
||||||
## 🧭 스킬 패키지 버전 매트릭스 (Skills Version Matrix)
|
## 🧭 스킬 패키지 버전 매트릭스 (Skills Version Matrix)
|
||||||
|
|
||||||
모든 8개 스킬은 YAML frontmatter 메타데이터(`author`, `version`, `platforms`, `environments`) 표준화를 통해 `v4.9.9`로 동기화되어 배포됩니다.
|
모든 8개 스킬은 YAML frontmatter 메타데이터(`author`, `version`, `platforms`, `environments`) 표준화를 통해 `v4.10.0`로 동기화되어 배포됩니다.
|
||||||
|
|
||||||
| 스킬명 | 버전 | 역할 및 주요 책임 | 상태 |
|
| 스킬명 | 버전 | 역할 및 주요 책임 | 상태 |
|
||||||
| :--- | :---: | :--- | :---: |
|
| :--- | :---: | :--- | :---: |
|
||||||
| **`multi-agent-mux-create`** | `4.9.9` | 에이전트 세션 신규 생성 및 Herdr 컨테이너 격리 스폰 | ✅ 배포 |
|
| **`multi-agent-mux-create`** | `4.10.0` | 에이전트 세션 신규 생성 및 Herdr 컨테이너 격리 스폰 | ✅ 배포 |
|
||||||
| **`multi-agent-mux-stop`** | `4.9.9` | 대화 UUID 원자적 캡처 및 세션 안전 종료 (Graceful Stop) | ✅ 배포 |
|
| **`multi-agent-mux-stop`** | `4.10.0` | 대화 UUID 원자적 캡처 및 세션 안전 종료 (Graceful Stop) | ✅ 배포 |
|
||||||
| **`multi-agent-mux-resume`** | `4.9.9` | 온디스크 대화 컨텍스트 기반 Tier-1 초고속 세션 복원 | ✅ 배포 |
|
| **`multi-agent-mux-resume`** | `4.10.0` | 온디스크 대화 컨텍스트 기반 Tier-1 초고속 세션 복원 | ✅ 배포 |
|
||||||
| **`multi-agent-mux-status`** | `4.9.9` | 실시간 Herdr 세션 및 레지스트리 드리프트 스냅샷 조회 | ✅ 배포 |
|
| **`multi-agent-mux-status`** | `4.10.0` | 실시간 Herdr 세션 및 레지스트리 드리프트 스냅샷 조회 | ✅ 배포 |
|
||||||
| **`multi-agent-mux-monitor`** | `4.9.9` | YAML ↔ 런타임 상태 간 자율 조정자 (Reconciler Loop) | ✅ 배포 |
|
| **`multi-agent-mux-monitor`** | `4.10.0` | YAML ↔ 런타임 상태 간 자율 조정자 (Reconciler Loop) | ✅ 배포 |
|
||||||
| **`multi-agent-mux-delegate-job`** | `4.9.9` | MQTT 이벤트 채널 기반 비동기 단위 작업 위임 | ✅ 배포 |
|
| **`multi-agent-mux-delegate-job`** | `4.10.0` | MQTT 이벤트 채널 기반 비동기 단위 작업 위임 | ✅ 배포 |
|
||||||
| **`multi-agent-mux-loop`** | `4.9.9` | Planner-Creator-Reviewer 3자 자율 계획·실행·피어리뷰 루프 | ✅ 배포 |
|
| **`multi-agent-mux-loop`** | `4.10.0` | Planner-Creator-Reviewer 3자 자율 계획·실행·피어리뷰 루프 | ✅ 배포 |
|
||||||
| **`multi-agent-mux-orc-onboard`** | `4.9.9` | 오케스트레이터 UUID 격리 등록 및 서브 세션 오염 방지 | ✅ 배포 |
|
| **`multi-agent-mux-orc-onboard`** | `4.10.0` | 오케스트레이터 UUID 격리 등록 및 서브 세션 오염 방지 | ✅ 배포 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 버전별 상세 변경 내역 (Changelog)
|
## 📋 버전별 상세 변경 내역 (Changelog)
|
||||||
|
|
||||||
|
### 🚀 `v4.10.0` — 같은 폴더의 같은 종류 에이전트가 서로의 대화 ID 를 쓰지 않는다 (Issue #63) (2026-10-23)
|
||||||
|
|
||||||
|
> **마이너 (MINOR Release)**: 같은 폴더에 ID 가 나중에 생기는 같은 종류 에이전트(agy·codex·hermes·opencode)가 둘 있으면, stop·purge·resume 이 `find_workspace_uuid` 의 추측으로 다른 행의 대화 ID 를 기록하거나 지우거나 이어받을 수 있었다. 이제 세 경로가 #40 의 판정을 같이 쓰고, 같은 폴더의 생성 순서를 정하는 새 대기 동작(`MAM_ID_ORDER_WAIT_S`)과 resume 의 새 거부(exit 1)가 추가되어 MINOR 로 올린다. PR #69.
|
||||||
|
|
||||||
|
#### 🌟 주요 개선 사항
|
||||||
|
1. **ID 판정 (`lib_py/agents/session_ids.py`, 새 파일)**: stop·purge·resume 이 같은 판정을 쓴다. 행의 own id 가 우선이고, 없으면 #40 의 `autopin.capture` 를 쓴다. 다른 행이 기록한 ID 는 쓰지도 지우지도 않는다. claude·grok 은 그대로다.
|
||||||
|
2. **stop·purge**:
|
||||||
|
- stop 은 프로세스를 끄기 전에 ID 를 정한다. herdr 가 이미 죽은 행은 확인할 수 없으므로 ID 를 기록하지 않고 이유를 남긴다.
|
||||||
|
- purge 는 own id 이거나 확인을 통과한 ID 만 지우고, 레지스트리를 쓰는 시점에 다른 행이 기록했는지 다시 확인한다.
|
||||||
|
3. **resume**:
|
||||||
|
- own id 가 없고 같은 폴더에 같은 종류 행이 둘 이상이면 추측하지 않고 새 대화로 띄운다.
|
||||||
|
- 해결한 ID 를 다른 행이 이미 기록했으면 띄우기 전에 거부한다(exit 1).
|
||||||
|
- 기록된 ID 가 더 이상 유효하지 않으면 새 대화로 띄우고 그 ID 를 지운다(`update_yaml_resumed.sh --drop-stale-id`).
|
||||||
|
4. **생성 순서 (`lib_py/agents/id_order.py`, 새 파일)**:
|
||||||
|
- 같은 폴더에서 같은 종류 에이전트가 생성 중이거나 ID 없이 실행 중이면, 새 create·resume 은 그 ID 가 기록될 때까지 기다린다. 폴더·에이전트 단위 잠금(`fcntl`)과 "생성 중" 표시를 쓰고, 폴더는 실제 경로로 비교한다.
|
||||||
|
- 상한은 `MAM_ID_ORDER_WAIT_S`(기본 600 s, 0 이면 기다리지 않음)다. 넘으면 아무것도 띄우지 않고 기다린 세션 이름을 알리며 거부한다(exit 1).
|
||||||
|
- create·resume 이 띄운 뒤 행을 등록하기 전에 죽어 "생성 중" 표시만 남으면, 그 herdr 세션이 살아 있는 동안 계속 막는다. herdr 가 세션이 없다고 확실히 답하거나 그 시도의 행이 등록되면 푼다. 조회 실패는 "모름"으로 보고 막으며, 조회도 대기 상한 안에서만 한다(상한 0 이면 묻지 않음).
|
||||||
|
5. **정리**: `find_workspace_uuid()` 가 ID 를 반환하고 다른 행의 ID 를 후보에서 뺀다(`exclude_ids`). 쓰이지 않게 된 `capture_conversation_id`(lib.sh)와 `CAPTURE_ID`(stop)를 지웠다.
|
||||||
|
|
||||||
|
#### ✅ 품질 검증
|
||||||
|
- **회귀**: `tests/test_issue63_same_cwd_ids.py`(27개). 완료 조건의 회귀 테스트는 base(v4.9.9)에서 실패하고 head 에서 통과한다.
|
||||||
|
- **리뷰 (`--all-reviewer`, 작성자 PR 의 리뷰 전용 루프, 수정은 claude creator)**: codex 가 고아 "생성 중" 표시 처리에서 차단 결함을 회차마다 찾았고 ede9c62·8f64174·7cac6f6·1614af5 에서 고쳤다. 마지막 루프 1회차(@1614af5) **4/4 PASS**. 리포트는 `.agents/reports/<session>/` 에 있다.
|
||||||
|
- **전체 스위트 (macOS, Python 3.9.6, @1614af5)**: 2097 passed, 56 skipped, 1 failed.
|
||||||
|
- 실패는 `tests/test_issue50_dispatch_herdr.py::test_c11_…` 하나다. 이 브랜치는 위임 코드를 건드리지 않고, 단독으로 세 번 돌리면 매번 통과했다(시간 의존).
|
||||||
|
- 실제 `~/.claude`·`/private/tmp/claude-<uid>`·herdr 서버 pane 목록·opencode 의 data·state·config 에 이 실행이 남긴 것은 없었다.
|
||||||
|
- **작성자 확인 (Linux)**: 전체 스위트와 실제 agy 두 개 시험(같은 폴더 동시 create·stop·동시 resume·B purge 뒤 A 의 대화 파일 보존).
|
||||||
|
- **알려진 비차단**:
|
||||||
|
- herdr 조회가 계속 실패하면 고아 표시가 남아 이후 create·resume 이 매번 상한까지 기다린다. 거부 메시지가 `herdr kill-session` 명령을 알려 준다.
|
||||||
|
- 등록 중에 herdr 목록이 잠깐 비어 있으면 표시가 풀릴 수 있다.
|
||||||
|
- `--no-onboard` 로 만든 세션은 첫 ID 가 기록될 때까지 다음 create 가 상한까지 기다린다(#68).
|
||||||
|
- presence lock 으로 ID 를 찾는 것과, MAM 밖에서 사람이 같은 폴더에 띄운 에이전트는 범위 밖이다.
|
||||||
|
|
||||||
### 🚀 `v4.9.9` — completed 를 확인한 쪽이 세션 ID 를 등록하고, agy 는 다른 작업 공간의 대화를 받지 않는다 (Issue #40) (2026-10-21)
|
### 🚀 `v4.9.9` — completed 를 확인한 쪽이 세션 ID 를 등록하고, agy 는 다른 작업 공간의 대화를 받지 않는다 (Issue #40) (2026-10-21)
|
||||||
|
|
||||||
> **패치 (PATCH Release)**: 세션 ID 가 생성 뒤에야 생기는 에이전트(agy·opencode·codex·hermes)는 지금까지 모니터 데몬의 reconcile 이 찾아 등록했다. 데몬이 없는 워크트리 creator 는 등록되지 않았고, agy 의 drift C 는 호스트 공용 대화 폴더의 모든 `*.db` 를 후보로 보아 다른 작업 공간의 대화 ID 를 등록할 수 있었다. 이제 잡의 completed 를 확인한 쪽이 같은 판정으로 한 번 등록하고, reconcile 도 그 판정을 쓴다.
|
> **패치 (PATCH Release)**: 세션 ID 가 생성 뒤에야 생기는 에이전트(agy·opencode·codex·hermes)는 지금까지 모니터 데몬의 reconcile 이 찾아 등록했다. 데몬이 없는 워크트리 creator 는 등록되지 않았고, agy 의 drift C 는 호스트 공용 대화 폴더의 모든 `*.db` 를 후보로 보아 다른 작업 공간의 대화 ID 를 등록할 수 있었다. 이제 잡의 completed 를 확인한 쪽이 같은 판정으로 한 번 등록하고, reconcile 도 그 판정을 쓴다.
|
||||||
|
|||||||
Reference in new issue
Block a user