release(v4.8.3): bump framework and eight skills to 4.8.3 in lockstep; archive PR #46 reviews (two rounds)

Issue #42 stages 1-2: the SKILL.md code-block inventory as test data and
the removal of blocks the entry scripts already do. Merge-workspace review:
round 1 (@8d26106) claude agy grok PASS, codex NOT PASS (R1-R3); fixed in
17eba09 (docs, inventory data and the guard test only); round 2 4/4 PASS.
Full suite at 8d26106 1408 passed / 0 failed. Reports and the round-2
identity sidecars go to .agents/reports/<session>/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-04 08:36:15 +09:00
1 parent 17eba0904e
commit 44999ff2fb
28 files changed
+824 -26

No files matched your search

@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261003T230658Z-e7866a",
"base_sha": "8d26106195a0a24621f140e76389d206332f84c0",
"head_sha": "17eba0904ebbf54c1d2243ad61a6f2ba17263889",
"tree_sha": "a0f1ffede44be7c86267a56b25871e1d081feafb",
"review_commit": "17eba0904ebbf54c1d2243ad61a6f2ba17263889",
"reviewer": "planner-reviewer-codex-01",
"job_id": "52dc1a69",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-03T23:35:18Z"
}
@@ -0,0 +1,81 @@
# PR #46 2회차 델타 리뷰 — 52dc1a69
**PASS**. 이번 한 커밋에서 이전 R1~R3가 해결됐다. 지정 테스트 결과는 아래에 기록한다. 제품 코드 변경은 없으며 새 반려 사유는 발견하지 못했다.
## 검증 identity와 범위
- loop: `loop-20261003T230658Z-e7866a`
- base: `8d26106195a0a24621f140e76389d206332f84c0`
- head: `17eba0904ebbf54c1d2243ad61a6f2ba17263889`
- tree: `a0f1ffede44be7c86267a56b25871e1d081feafb`
- 검증 cwd: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261003T230658Z-e7866a/1`
- interpreter: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-46/.venv/bin/python`
브리프의 HEAD/tree와 clean 상태를 확인했다. `git diff 8d26106 17eba09` 한 커밋만 재검토했다. 변경은 create/resume/stop SKILL.md, JSON 목록, 문서 가드 테스트의 5개 파일(93 insertions, 19 deletions)이다. 제품 스크립트·Python·버전 변경은 없다. `git diff --check 8d26106 HEAD`도 통과했다.
## 이전 지적 판정
| 지적 | 판정 | 현재 근거 |
|---|---|---|
| R1: attach의 격리 서버 선택 유실 | **종결** | `.agents/skills/multi-agent-mux-resume/SKILL.md:30`, `:63`, `:139`가 모두 `herdr --session <server> agent attach <name>`을 안내한다. :63은 child의 export가 caller에 전파되지 않는 점과 native herdr의 환경변수 라우팅 부재를 설명한다. :106에서 row의 서버를 읽고 :109/:126의 get/read에 전달한다. |
| R2: dry-run이 아무것도 쓰지 않는다는 설명 | **종결** | resume SKILL.md:73 및 create SKILL.md:164가 spawn·registry 갱신 생략으로 범위를 좁히고 macOS quarantine 속성 제거를 명시한다. 제품의 기존 xattr 호출과 dry-run 반환 순서에 맞는다. |
| R3: inventory 갱신으로 삭제 (a) 본문 복귀를 숨길 수 있음 | **종결** | `tests/test_issue42_skill_blocks.py:137`–`:153`이 현재 문서를 직접 파싱해 삭제 sha/fingerprint와 비교한다. `DATA["blocks"]`의 class 선언에 의존하지 않는다. 아래 독립 재삽입 탐침을 거부했다. |
| C1: base create의 호출 블록 하나 미추적 | **종결** | `tests/data/skill_code_blocks.json`의 새 `superseded` 항목이 base create SKILL.md:263–266, sha `ceb96a8c1293`, class c/kind invocation, replacement를 기록한다. base 실물과 위치/hash/fingerprint를 대조했다. |
| C2: 종료 코드 스캐너의 일반적인 heredoc 한계 | **후속 유지, 비차단** | 이 커밋에서 스캐너를 바꾸지 않았다. 1회차에서도 현재 세 스크립트의 표가 틀렸다고 판정하지 않은 개선 제안이다. 이번 델타의 반려 사유로 재사용하지 않는다. |
## 독립 검증
### 서버 라우팅과 Verification 경계 사례
실제 `/opt/homebrew/bin/herdr --help`를 임시 HOME 환경에서 실행해 전역 `--session` 옵션을 확인했다(서버 접속 없음). 이후 임시 herdr argv 기록 대역을 사용했다. caller의 `HERDR_SESSION_NAME=caller-server`와 무관하게 두 경우 모두 정확히 다음 argv를 전달했다.
```text
native: --session probe-isolated agent attach probe-codex
lib.sh shim: --session probe-isolated agent attach probe-codex
```
shim 경로는 실제 lib.sh를 source하되 WORKSPACE_ROOT/HOME/registry를 모두 임시 경로로 두고 MAM_NO_AUTOSTART=1로 실행했다. 실제 herdr 서버나 agent에는 접속하지 않았다. `.agents/skills/lib.sh:255`–`:269`의 명시 `--session` 보존 분기와도 일치한다.
문서의 SERVER 대입 행을 그대로 추출해 합성 YAML로 확인했다.
| row 상태 | 실제 결과 |
|---|---|
| `herdr_session: probe-isolated` | rc 0, SERVER=probe-isolated |
| 해당 row는 있지만 `herdr_session` 없음 | rc 0, SERVER=default |
| 해당 row 없음 | 대입 rc 1, IndexError, SERVER 비어 있음 |
| legacy `herdr_server`만 존재 | rc 0, SERVER=default |
마지막 두 경우는 Verification 예제의 한계로 남는다. row가 없으면 traceback을 내며 명확히 실패하지만, 블록에 `set -e`/명시적 guard가 없어 그대로 복사 실행한 shell이 후속 조회를 시도할 수 있다. legacy alias만 있는 row는 alias를 읽지 않는다. 다만 이 블록은 **성공한 resume 뒤의 검증**이고, `update_yaml_resumed.sh:140` 및 `:154`–`:156`이 새 row와 기존 legacy row에 `herdr_session`을 채운다. 따라서 정상 resume 경로의 R1은 해결됐으며 이를 새 반려 사유로 삼지 않는다. 이 예제를 실패 복구용 범용 조회로 확대하려면 row 부재 시 즉시 중단하고 `herdr_server` alias도 고려하는 것이 좋다.
### 삭제 본문 가드와 정상 예제 허용
1. base `45d1621`에서 `removed` 다섯 개와 `superseded` 하나의 실제 본문을 읽었다. 여섯 항목 모두 base start/end, exact sha, whitespace fingerprint가 데이터와 일치했다.
2. 여섯 본문 각각을 원문 및 들여쓰기 변경 형태로 되살려 `_returned_bodies()`가 모두 검출하는 것을 확인했다.
3. 1회차와 동일하게 삭제 resume Workflow를 현재 문서 끝에 추가하고 inventory도 정확한 위치/hash 및 class c로 갱신했다. 코드/문서 파일을 수정하지 않고 모듈의 입력과 DATA에 메모리상으로만 적용했다. 기존 inventory 일치와 class 가드는 통과했지만 **새 `test_no_deleted_body_came_back()`는 예상대로 AssertionError**였다.
4. 경로·agent·role을 구체적으로 지정한 다른 정상 create 호출 예제는 검출되지 않았다. 부분 명령이나 단어가 겹친다는 이유로 거부하지 않고 전체 본문 identity를 비교한다.
`fingerprint()`(:132–134)는 whitespace를 하나로 접고 전체 본문을 해시한다. 이로써 들여쓰기/reflow로 동일 본문을 되살리는 경우를 잡는다. 의미를 분석하는 parser는 아니므로 문자열 안 whitespace까지 같게 취급하며, comment 추가 등 모든 의미적 중복을 잡는다고 보장하지 않는다. 현재 여섯 개의 제한된 삭제 identity를 보존하는 가드로는 합당하다. `superseded`의 같은 옛 호출도 되살리지 않는 정책이므로 그 예제를 의도적으로 다시 쓸 때에는 삭제 이력 정책도 함께 재검토해야 한다.
새 자체 테스트(:156–166)는 helper의 exact/fingerprint 경로를 확인한다. 이름과 달리 DATA를 실제로 갱신하는 테스트는 아니지만 helper가 DATA blocks를 읽지 않고, 이번 독립 탐침이 전체 가드의 inventory 갱신 우회를 직접 검증했다. 이 차이는 현재 동작 결함이 아니다.
### inventory·보완 문구·범위
현재 33개 블록의 `(skill, class, kind)`를 이전 JSON과 순서대로 비교해 모두 동일함을 확인했다. resume Verification의 end/sha만 바뀌었고, 기존 removed 항목에 fingerprint, 새 superseded 항목이 추가됐다. 분류 변경은 없다.
create exit 42는 `MAM_KILL_ON_READY_TIMEOUT=1`일 때 살아 있는 process도 rollback한다는 조건을 추가했고, stop exit 0의 no-op은 purge에는 적용되지 않는다고 좁혔다. 각각 `create_session.sh:392`–`:398`, `stop_session.sh:184`–`:198`과 일치한다. 이 문구 보완도 제품 동작 변경을 요구하지 않는다.
## 테스트 결과
**132 passed, 1 skipped in 296.61s (0:04:56)**, 종료 코드 0. skip은 워크트리에 untracked `.claude/skills` 링크가 없는 정상 조건이다. 기존 stop 예제 최소 3개/--agent 가드도 통과했다. 로그: `/tmp/52dc1a69-selected.log`. 테스트 종료 후 git status는 clean이며 HEAD/tree도 브리프와 그대로 일치한다.
실행 명령(위 검증 cwd):
```sh
AGENT_PYTHON_BIN=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-46/.venv/bin/python PYTHONDONTWRITEBYTECODE=1 /Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-46/.venv/bin/python -m pytest -p no:cacheprovider -q --basetemp=/tmp/52dc1a69-selected tests/test_issue42_skill_blocks.py tests/test_tier2_component.py tests/test_orc_onboard.py tests/test_loop_cli.py tests/test_o1_rebuttal.py tests/test_version_consistency.py
```
독립 탐침: `/tmp/52dc1a69-probes.py`; 결과: `/tmp/52dc1a69-probes.log`. 검증 cwd에서 `PYTHONDONTWRITEBYTECODE=1 <위 Python> /tmp/52dc1a69-probes.py`로 재현한다. 임시 python3 대역은 venv 인터프리터를 exec해 PyYAML을 유지한다. 초기 대역을 단순 symlink로 만들었을 때는 venv 패키지를 찾지 못해 SERVER 검증이 실행되지 않았고, 이를 수정한 최종 실행의 네 결과만 위에 기록했다.
전체 스위트와 purge 파일은 이번 델타에서 반복하지 않았다. 제품/purge 코드가 그대로이며 1회차의 purge 23/23 통과 결과를 이번 실행 수치에 합산하지 않았다. 실제 `.mam.env`를 읽거나 export하지 않았고, 코드/검증 트리를 수정하지 않았다. 실제 `.mam` 쓰기는 지시된 잡 lifecycle 및 리포트에 한정했다.
[VERDICT: PASS]
@@ -0,0 +1,121 @@
# PR #46 단계 1·2 리뷰 — fbd52b0b
**NOT PASS**. 제품 코드 변경은 없고 선택 테스트는 모두 통과했다. 다만 새 문서대로 실행할 때 격리 서버의 attach 대상이 사라지는 문제, dry-run의 부작용을 부정하는 설명, 삭제 블록 복귀 가드의 누락이 남아 있다. 문서와 테스트의 국소 수정으로 해결할 수 있으므로 재계획 승격은 필요하지 않다.
## 검증 대상과 결과
- loop: `loop-20261003T222707Z-a0de27`
- base: `45d1621c93504e6cfa347b31995f70da868f4172`
- head: `8d26106195a0a24621f140e76389d206332f84c0`
- tree: `b579380959e08f721492386da904972092bf8d7f`
- 검증 cwd: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261003T222707Z-a0de27/1`
- HEAD/tree 일치, 시작·종료 시 git status clean. `git diff --check 45d1621 HEAD` 통과.
- 변경 파일은 SKILL.md 4개, JSON 목록, 새 테스트 파일뿐이다. 제품 스크립트/Python 및 버전 변경 없음.
검증 cwd에서 다음을 실행했다. **152 passed, 1 skipped in 314.72s**, 종료 코드 0. 로그: `/tmp/fbd52b0b-selected.log`.
```sh
AGENT_PYTHON_BIN=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-46/.venv/bin/python PYTHONDONTWRITEBYTECODE=1 /Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-46/.venv/bin/python -m pytest -p no:cacheprovider -q --basetemp=/tmp/fbd52b0b-selected tests/test_issue42_skill_blocks.py tests/test_tier2_component.py tests/test_orc_onboard.py tests/test_loop_cli.py tests/test_o1_rebuttal.py tests/test_version_consistency.py tests/test_issue10_13_purge.py
```
이 중 purge 파일은 수집 결과 **23건**이며 전부 통과했다. 나머지 지정 6개 모듈은 129 passed, 1 skipped다. skip은 브리프에 명시된 untracked `.claude/skills` 링크 부재다. 전체 스위트는 실행하지 않았다.
## 수정 필요 사항
### R1 — P2: 분리한 attach 단계에 격리 서버 선택을 보존해야 한다
- 변경 근거: `.agents/skills/multi-agent-mux-resume/SKILL.md:30`, `:60`, `:63`.
- 동작 근거: `resume_session.sh:133`–`:144`는 자식 bash 안에서 서버/워크스페이스를 export하고, `:147`–`:158`은 attach 없이 반환한다. `.agents/skills/lib.sh:255`–`:269`의 shim은 서버 환경값을 실제 CLI의 `--session`으로 바꾼다. native herdr가 환경변수 자체로 라우팅하지 않는다는 사실은 create SKILL.md:147에도 명시돼 있다.
- 재현: lib.sh를 source하지 않은 새 셸에서 격리 서버의 세션에 대해 새 Workflow의 `bash .../resume_session.sh ... --herdr-session probe-isolated`를 실행한 뒤, 새 안내대로 `herdr agent attach probe-codex`를 실행한다. resume의 export는 부모 셸에 전달되지 않는다. 이미 caller가 다른 서버의 shim을 쓰고 있어도 자식의 설정은 caller의 서버를 바꾸지 않는다.
- 기대: 방금 resume한 `probe-isolated` 서버의 agent에 attach한다.
- 실제: 안내된 native 호출 argv에는 `--session probe-isolated`가 없으므로 기본/호출자 서버로 간다. 대상이 없으면 실패하고 동명 대상이 있으면 다른 대상으로 연결될 수 있다. 실제 서버에 접속하지 않고 임시 herdr argv 기록 대역으로 확인했다: `agent attach probe-codex`. 수정 호출은 `--session probe-isolated agent attach probe-codex`였다.
- 수정 방향: 별도 attach는 유지하되 `herdr --session <row.herdr_session 또는 명시한 서버> agent attach <name>`처럼 서버를 명시한다. default와 격리 서버의 방법을 구분하거나, 현재 셸에서 lib.sh와 정확한 서버 값을 설정하는 방법을 안내한다. child script가 parent 환경을 설정한다고 가정하지 않는다. 새 Workflow에서 source/export를 지웠으므로 verification의 bare `herdr agent get/read`도 같은 관점에서 확인한다.
### R2 — P2: dry-run을 무조건 무쓰기라고 설명하면 안 된다
- 변경 근거: `.agents/skills/multi-agent-mux-resume/SKILL.md:73`의 “write nothing”, `.agents/skills/multi-agent-mux-create/SKILL.md:164`의 “spawn and write nothing”.
- 동작 근거: resume `resume_session.sh:168`–`:169`는 macOS에서 agent binary의 quarantine 속성을 제거한다. dry-run 반환은 그보다 뒤인 `:225`–`:227`이다. create도 `create_session.sh:195`–`:196`에서 같은 동작을 하고 `:278`–`:281`에서 dry-run으로 반환한다.
- 재현: 임시 HOME/WORKSPACE_ROOT/AGENT_SESSIONS_YAML, 빈 합성 레지스트리, mock codex/herdr 및 호출을 기록하는 mock xattr를 준비한다. 실제 resume 진입점에 `--workspace <임시 root> --agent codex --session probe-codex --herdr-session probe-isolated --dry-run`을 전달한다.
- 기대: 새 옵션 표를 그대로 해석하면 파일/메타데이터 변경 작업도 호출하지 않는다.
- 실제: rc 0 및 `[dry-run] would spawn: ...`을 반환하면서 **xattr -d가 호출됐다**. 실제 사용자 binary의 속성은 수정하지 않았다. create의 동일 경로는 정적 대조로 확인했다.
- 수정 방향: 제품 코드를 바꾸지 말고 보장을 “agent spawn 및 session YAML/DB 갱신 생략”처럼 실제 범위로 한정하고, preflight가 binary 메타데이터 등 부수효과를 가질 수 있음을 적는다. 절대적인 무쓰기 보장을 원하면 별도 제품 변경이 필요하며 이 PR의 범위는 아니다.
### R3 — P2: 삭제된 (a) 블록의 복귀를 검사하는 가드가 없다
- 변경 근거: `tests/test_issue42_skill_blocks.py:121`–`:127`. `test_no_duplicate_block_left()`는 JSON의 class 문자열만 확인한다. `:108`–`:113`은 현행 문서와 현행 목록의 일치만 검사한다. 저장한 `removed[].base_sha`를 현재 블록과 비교하는 코드가 없다.
- 재현: 테스트 모듈을 가져오고, base resume SKILL.md:59–118의 삭제된 Workflow 본문(sha `63abe1bb3542`)을 현행 문서 끝에 다시 추가한 문자열을 만든다. 목록에도 해당 블록의 start/end/lang/sha를 정상 갱신하고 class=c, kind=example으로 기재한다. 파일을 수정하지 않고 모듈의 `skill_text`와 DATA에 메모리상으로 적용한 뒤 inventory, no-duplicate, replacement, exit-table, evidence 가드를 실행한다.
- 기대: 알려진 삭제 (a) 본문이 그대로 돌아왔으므로 inventory 갱신 여부와 무관하게 거부한다.
- 실제: 관련 가드가 전부 통과했다. 새 파일 설명의 “when an (a) block comes back” 보장과 다르다. inventory를 갱신하지 않은 일반 문서 변경은 잘 거부하지만, 그것은 별도의 보장이다.
- 수정 방향: 알려진 삭제 본문의 hash가 현재 파싱된 본문에 없는지 명시적으로 검사한다. 필요하면 공백 정규화 fingerprint도 추가하되 의미가 다른 예시까지 막지 않는다. 위 재삽입 + inventory 갱신을 회귀 사례로 둔다. 제품 코드 수정은 필요 없다.
## 추가 개선 및 가드의 한계
### C1 — baseline 37개에 대한 추적 하나가 빠졌다
base의 8개 SKILL.md를 실제로 파싱하면 **37개**, 현재는 **33개**다(현재 b=5, c=28). `removed`의 다섯 항목은 base 위치·종료 줄·hash가 모두 맞았다. 그러나 base create SKILL.md:263–266의 `create_session.sh --workspace "$WORKSPACE" ... --session "$SESSION_NAME"` 호출 블록도 삭제됐는데 historical 항목이 없다. 현재 JSON:87의 새 Workflow 항목은 base 168–220만 대체한다고 적고 있고, `prose_mismatches`의 C4는 없는 스크립트 이름을 고쳤다는 설명뿐이다.
이 호출은 (c)에 해당하며 삭제 자체가 기능 유실은 아니다. 다만 “37개를 모두 분류했다”는 단계 1의 감사 근거를 정확히 하려면 해당 base 블록을 (c)로 기록하고 새 호출과 통합됐다는 매핑을 추가해야 한다. 전체 baseline 목록과 현행 목록/삭제 이력을 구분하면 `removed`를 무조건 class=a로 강제하는 현재 스키마와도 충돌하지 않는다. 현재 가드는 DATA의 base/base_sha 자체를 검증하지 않는다.
### C2 — 종료 코드 스캐너는 현재 세 스크립트의 표에는 맞지만 일반 규칙으로는 부정확하다
`tests/test_issue42_skill_blocks.py:68`–`:78`은 heredoc 본문에서 주석을 거르기 전에 SystemExit를 수집하고, heredoc 명령의 `|| true` 여부를 보지 않는다. 반대로 unguarded Python의 `sys.exit(N)`은 수집하지 않는다. 임시 입력에 대한 `_scan_exits()` 탐침 결과:
| 입력 | 실제 shell 종료 의미 | 스캐너 결과 |
|---|---|---|
| heredoc 안 `# raise SystemExit(9)` | 주석: 9 아님 | passthrough 9 포함 |
| unguarded heredoc 안 `sys.exit(9)` + set -e | 9 전달 | passthrough 비어 있음 |
| heredoc 안 `raise SystemExit(9)` + 명령 뒤 `|| true` | 9 흡수, 다음 exit 0 | passthrough 9 포함 |
현재 파일의 `SystemExit(4)`/`SystemExit(1)`은 실제 unguarded atomic write라 표가 맞고, stop의 `sys.exit(7)`은 `|| { exit 1; }`로 감싸져 있으므로 7을 제외한 것도 맞다. 따라서 이 부분에서 **현재 종료 코드 표가 틀렸다고 판정하지는 않는다**. 다만 정상적인 주석 추가가 가드를 깨거나 동등한 `sys.exit` 변경을 놓칠 수 있다. Python 본문은 주석/문자열을 제외하고 두 exit 형태를 처리하며, passthrough 여부는 해당 호출의 처리 방식으로 제한하는 작은 fixture 테스트를 권장한다. 범용 shell 파서를 만들 필요는 없다.
숫자 행 가드는 설명 열의 의미까지 검증하지 않으므로 아래 종료 경로 검토는 별도로 수행했다. 줄 번호/hash를 고정하는 inventory는 prose 편집으로 블록 위치만 바뀌어도 함께 갱신해야 한다. 이는 설계상 비용이며 판정 사유로 삼지 않았다.
## 요청 항목별 대조
### 1. 삭제 분류와 resume의 단계별 대응
짧은 제품 경로는 `.agents/skills/` 아래다.
| 삭제된 resume 단계 | 실제 근거 / 판단 |
|---|---|
| session ID 해석 | resume_session.sh:111–112 → resolve_session_id.sh |
| ID 없는 Class A fresh-spawn | :122–130, :172–184; Class B는 :127–128에서 실패 |
| 서버/label 선택 | :133–144 |
| herdr alive면 attach | :147–158은 row 갱신 + 종료이며 attach 없음. 불일치 R1/R2가 JSON에 기록됐고 별도 attach로 정정한 방향은 맞지만 서버 선택 보완 필요(위 R1) |
| agent command / 기동 | :172–197의 adapter/fallback, :200–211 binary 검사, :237 spawn |
| handle_startup_dialogs | :241, 이어서 :244 sleep |
| update_yaml_resumed.sh | :247–250; agent/workspace/server/cmd-full 등 옵션 전달. 이미 살아 있는 경로는 :154–157 |
| 마지막 attach | 스크립트에 없음. 사람이 별도 실행해야 하는 동작을 prose로 남겼다. 순수한 “모든 줄이 그대로 중복”은 아니며 JSON mismatch가 그 차이를 명시한다 |
나머지 삭제 항목: create preflight는 create_session.sh:103–158, Workflow는 :160–168 / :207 / :249–274 / :361–412, YAML append는 :491–610에 구현돼 있다. stop preflight는 stop_session.sh:99 / :125–143 / :184–188에 있다. 기본 stop의 no-op은 purge가 아닐 때만 적용된다. 기존의 잘못된 양 agent 동시 체크, 고정 sleep, 없는 yaml-edit 안내 등을 제거한 것은 타당하다.
남겨진 (b) 다섯 개도 타당하다: create/resume/stop의 사후 검증 블록은 운영자가 결과를 확인하는 절차이고, monitor runbook에는 새 drift만 사람에게 보고하기·중지 판단·hard rules가 있다. status preflight에는 status.sh가 하지 않는 `has_real_herdr` 체크가 섞여 있다. 이런 사람/agent 측 절차를 이번 단계에서 지우지 않은 판단은 맞다. 단, 새 진입점 호출은 caller 셸의 변수나 서버 선택을 설정하지 않는다는 R1을 반영해야 한다.
### 2. 옵션, 종료 코드, 다섯 정정
create parser :74–93 및 resume parser :35–47와 새 옵션을 대조했다. stop의 세 호출 예제는 `--agent`를 유지하고 purge에 `--yes`가 추가됐다. 임시 HOME/WORKSPACE_ROOT/AGENT_SESSIONS_YAML로 create/resume/stop 실제 `--help`를 실행해 모두 rc 0을 확인했다. stop help의 짧은 agent 목록 등 기존 제품 help 불일치는 이 PR에서 새로 만든 문제가 아니다.
- resume Case 3: no-attach/permission flag 미검사 정정은 스크립트와 일치. 별도 attach의 라우팅만 R1.
- purge: stop_session.sh:191–198에서 --yes 없으면 rc 3. 문서 :57은 사용자 확인 후에만 --yes를 허용하므로 판단 단계도 보존됐다.
- status JSON: status.sh:209–239의 sessions_detail 필드 및 reconcile.sh:1083–1091의 상위 필드와 일치. resume_state 값도 일치한다.
- create cmd_full: spawn-spec 경로와 일치. 없는 yaml-edit 스크립트 참조 제거는 올바르다.
- create exit 4: 단순 header 숫자가 아니라 :515의 SystemExit(4)가 전달된다. trap :320–352가 rollback한다. exit 43은 kill 실패 후 살아 있는 경우다. exit 42는 ready timeout의 abort 경로이며 `MAM_KILL_ON_READY_TIMEOUT=1`로 살아 있는 agent도 rollback 대상으로 삼을 수 있다는 추가 설명이 있으면 더 정확하다.
- resume의 post-spawn update rc 1/2, stop의 post-stop SystemExit(1) 구분을 표에 남긴 점은 맞다. `other`는 set -e를 통해 전달되는 실패를 포함한다.
### 3–5. 문서 사용성·가드·범위
사용자 purge 확인은 유지됐고 호출 예제는 실행 진입점으로 통일됐다. 기존 `test_comp_docs_stop_examples_pass_agent`도 선택 스위트에서 통과했다(최소 3개, 모두 --agent). 일반 inventory drift를 잡는 효과는 확인했다: 메모리상으로 create/resume/stop/status 각각을 base 문서로 되돌리면 현행 inventory 가드는 네 경우 모두 실패한다. 다만 알려진 삭제 본문 복귀는 R3처럼 누락된다.
제품 코드 변경·버전 bump·push/merge/이슈 상태 변경 없음. 실제 세션·herdr 서버·실제 .mam.env 값은 접근하지 않았고, 탐침은 합성 env/임시 HOME/임시 registry/mock binary만 사용했다. 검증 트리와 source/main 코드는 수정하지 않았다. 실제 .mam 쓰기는 이 잡의 지시된 lifecycle 및 최종 리포트에만 한정했다.
### 6. “purge 12개 기준 실패” 주장
이번 macOS 실행에서 해당 파일은 23/23 통과했다. base..head에서 그 테스트와 purge 구현은 변경되지 않았다. 따라서 이 PR이 purge 회귀를 만들었다는 증거는 없지만, 이 호스트에서도 12개 실패가 당연하다는 주장은 뒷받침되지 않는다. PR의 전체 실행 환경·실패 로그·동일 환경 base 비교가 있어야 Linux의 알려진 실패와 동일하다고 결론 낼 수 있다. 이번 리뷰에서는 외부 PR을 수정하거나 전체 스위트를 재실행하지 않았다.
## 독립 탐침 재현 자료
- `/tmp/fbd52b0b-probes.py`: safe --help/--dry-run, attach argv, 삭제 Workflow 복귀 가드 탐침. 결과 `/tmp/fbd52b0b-probes.log`.
- `/tmp/fbd52b0b-guard-probes.py`: base 문서 거부 확인, 종료 코드 스캐너의 합성 입력. 결과 `/tmp/fbd52b0b-guard-probes.log`.
- 실행: 위 검증 cwd에서 `PYTHONDONTWRITEBYTECODE=1 <위 Python> /tmp/fbd52b0b-probes.py "$PWD"`, 이어서 같은 인터프리터로 guard-probes.py 실행. 모두 종료 코드 0이며, 이는 탐침 실행 성공을 뜻한다. 발견한 문서/가드 문제의 해결을 뜻하지 않는다.
[VERDICT: NOT PASS]