release(v4.7.0): bump framework and eight skills to 4.7.0 in lockstep; archive PR #29 reviews
Issue #21 items 4, 5, 7 (worktree helpers, review identity, acceptance A1-A8), #20 test isolation, #27 .gitignore managed block, #28 host portability. VERSIONS.md carries the entry; the eight review reports of PR #29 (two rounds plus the codex re-review) are archived under .agents/reports/<session>/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
1 parent
80a5614f7c
commit
cf5d1142e9
24 files changed
+849
-26
No files matched your search
@@ -0,0 +1,170 @@
|
||||
# 🔍 Review Report — PR #29 병합 전 독립 재검토 (job `291e38b5`)
|
||||
|
||||
- **Reviewer**: `planner-reviewer-claude-01` (role: planner,reviewer) · 2026-09-20
|
||||
- **대상**: PR #29 `feat/issue-21-items-4-5-7` **@ `46ea4cc`**, base `main @ 1134d74` (v4.6.0). 20커밋, `git diff --stat 1134d74...HEAD` = **25 files, +3229/−108**.
|
||||
- **검토 트리**: 워크트리 `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` (detached @ `46ea4cc`, 자체 `.venv`).
|
||||
- **안전 규칙 준수**: 워크트리에 파일 수정·커밋·checkout 없음. 본 체크아웃은 `main @ 1134d74` 그대로(checkout/switch/merge 없음). `worktree_remove.sh` 를 실제 워크트리에 실행하지 않았다. 실제 `.mam`·monitor 데몬·에이전트 세션 조작 없음, push·merge·이슈/PR 상태 변경 없음. 재현 실험은 scratchpad 의 임시 저장소(uuid4 이름)에서만 했다.
|
||||
- **heartbeat**: 브리프 직후 `started --heartbeat 30` 을 가장 먼저 발행했다.
|
||||
|
||||
## 결론 요약
|
||||
|
||||
브리프의 8개 항목을 모두 확인했다. **병합을 막을 결함은 찾지 못했다.** 항목 5(리뷰 identity)의 설계·구현 품질은 높고, 기본 경로(옵션 없는 루프)의 호환성은 코드상 보장된다. 아래 **비차단 지적 9건**(N1–N9)을 남긴다. 그중 N1 은 재현 실험으로 확인한 실제 소실 가능성(판정 증거의 gc 소멸)이므로 후속 이슈 등록을 권한다.
|
||||
|
||||
## 1. 항목 4 — 워크트리 준비·해체 헬퍼
|
||||
|
||||
### 1-1. `.venv` — 브리프가 물은 "실제 venv" 는 아니다 (N2, 비차단)
|
||||
|
||||
`worktree_prepare.sh:111-118` 은 여전히 **심링크**를 만든다(`ln -s "$MAIN/.venv" "$WT/.venv"`). 실제 venv 생성도, 인터프리터 경로 전달도 하지 않는다. 다만 이번 PR·base 에서 **커밋 사고 경로는 3중으로 닫혀 있다**:
|
||||
|
||||
| 층 | 근거 |
|
||||
|---|---|
|
||||
| 저장소 `.gitignore` | base `1134d74` 의 `.gitignore:15` 가 `.venv`(슬래시 없음) — 파일·링크·디렉터리 모두 무시 |
|
||||
| 공유 `info/exclude` | `worktree_prepare.sh:120-129` 가 `/.venv`·`.pytest_cache/` 를 `$GIT_COMMON_DIR/info/exclude` 에 추가 |
|
||||
| 배포 관리 블록 | `deploy/install.sh:549`·`deploy/install_mam.sh:249` 가 `/.venv/` → `/.venv` (#27 `fadd38c`) |
|
||||
|
||||
남는 위험은 두 가지이며, 둘 다 헬퍼가 유발하지는 않는다.
|
||||
- `rm -rf "$WT/.venv/"` 처럼 **끝에 슬래시를 붙이면 링크를 따라가** 제어 루트의 venv 내용을 지운다. 헬퍼는 그런 호출을 하지 않지만(스크립트 2개의 `rm -rf` 는 `$gd/modules*` 뿐), 사람·에이전트의 수작업에서는 그대로 남는 함정이다.
|
||||
- 워크트리와 본 체크아웃이 venv 를 공유하므로 브랜치별 의존성 분기가 불가능하다.
|
||||
|
||||
→ 권고: 헬퍼에 `--venv link|create` 같은 선택지를 두거나, 최소한 SKILL.md 에 "`.venv` 는 링크이므로 워크트리에서 `pip install`·`rm -rf .venv/` 금지"를 명시.
|
||||
|
||||
### 1-2. `info/exclude` 는 공유물이다 (N3, 비차단)
|
||||
|
||||
linked worktree 의 `info/exclude` 는 `$GIT_COMMON_DIR` 의 것이므로 prepare 가 **본 체크아웃의 무시 규칙도 바꾼다**. 코드 주석(`:120-122`)과 설계 문서가 이를 명시하고, 추가되는 두 패턴은 본 체크아웃에서도 산출물이다. 수용 가능하나, 되돌리는 경로(remove 시 정리)는 없다.
|
||||
|
||||
### 1-3. 서브모듈 opt-in·롤백
|
||||
|
||||
`worktree_prepare.sh:131-204`: `--submodules` 일 때만 초기화하고, 초기화 전 상태(각 서브모듈의 git dir 존재 여부, 공용 `.git/config` 의 `submodule.<n>.url|active`)를 **인덱스 배열**(bash 3.2 호환)로 기록한다. 초기화 안 된 경로에 파일이 이미 있으면 **아무것도 바꾸기 전에** exit 3 으로 거부한다(R4) — 롤백이 "이번 시도가 만든 것"만 지울 수 있다는 전제를 실제로 성립시키는 올바른 순서다. 실패 시 deinit → `.git` 파일 제거 → `modules/<name>` 제거 → 없던 `modules/` 제거 → config 복원 순으로 되돌리고, 마지막에 `git status` 로 워크트리가 여전히 쓸 수 있는 체크아웃인지 확인해 메시지를 갈라 준다. 사람이 강제 제거로 더 망가뜨리지 말라는 안내까지 있다.
|
||||
|
||||
### 1-4. `--force` 가 작업을 잃을 수 있는가 — 잃지 않는다
|
||||
|
||||
`worktree_remove.sh` 의 판정은 **변경을 가하기 전에** 모두 끝난다(`:57-121`): 잠금, 실행 중 세션(`--stop` 없으면 거부), 열린 잡, 트리 청결(서브모듈 포함), 서브모듈 소견. `--force` 는 워크트리 git dir 에 `modules/` 가 있어 git 이 plain remove 를 거부하는 경우에만, 그것도 **단 한 번** 쓴다(`METHOD="single --force"`).
|
||||
|
||||
- **R1(정지 중 쓰기)**: `judge_tree()` 를 `--stop` **이후 다시** 실행하고, 달라졌으면 `:154` 에서 거부한다.
|
||||
- **R6(tag/notes 로만 보존된 커밋)**: `lib_py/worktree.py:UNPUSHED_REVS = ("HEAD","--exclude=refs/stash","--all","--not","--remotes")` 로 모든 로컬 ref 기준 미푸시 커밋을 본다. stash 는 별도 확인.
|
||||
- **R5(deinit 저장소)**: `submodule foreach` 로 닿지 않는 `modules/` 아래 저장소까지 `--git-dir` 로 직접 검사하고, 판단 불가한 "부분 저장소"는 오류로 남겨 거부한다.
|
||||
|
||||
즉 `--force` 시점에는 "미커밋·미푸시·stash 없음"이 증명돼 있다. 한 가지만 적어 둔다(N8): plain/force 어느 쪽이든 git 은 **무시된 파일**을 말없이 지운다. 워크트리에 테스트가 남긴 `.mam/delegate_job_logs` 같은 산출물은 제거와 함께 사라진다(제어 평면이 본 체크아웃이므로 설계상 무해).
|
||||
|
||||
또 하나(N7): 열린 잡 검사는 `registry_dirs`(`MAM_JOBS_DIR` → `WORKSPACE_ROOT/.mam/jobs` → 행들의 `control_plane.mam_jobs_dir`)만 본다. 행이 없는 워크트리 로컬 `.mam/jobs` 의 잡은 보이지 않는다. prepare 가 만든 creator 는 제어 루트에 묶이므로 모델상 발생하지 않는 경로지만, 수작업으로 만든 워크트리에는 해당한다.
|
||||
|
||||
## 2. 항목 4 — resume 계약 변경(`2679ddf`)은 기존 사용자를 깨지 않는다
|
||||
|
||||
`resume_session.sh:50-70` 이 행의 `pane.cwd` 와 `--workspace` 를 비교해 다르면 exit 2, 경로가 사라졌으면 exit 2 로 안내한다. 기존 단일 워크스페이스 사용자에게 영향이 없는 이유:
|
||||
|
||||
1. `--workspace` 는 원래부터 **필수 인자**이고(`:42`), 기존 행의 `pane.cwd` 는 그 워크스페이스 경로다. 본 체크아웃의 실제 `.mam/agent-sessions.yaml` 5개 행 모두 `cwd: /Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux` 로, 평소 호출과 일치한다.
|
||||
2. 비교는 `mam_abs_workspace`(`lib.sh:1606-1609`, `cd -P … pwd -P`)로 정규화한 뒤 하므로 심링크·상대경로 표기 차이는 통과한다.
|
||||
3. `pane.cwd` 가 없는 구(舊) 행은 `ROW_CWD` 가 비어 검사 자체를 건너뛴다.
|
||||
|
||||
바인딩 영속은 **이미 실행 중인 경로에도** 적용된다: `:145` 가 `CONTROL_PLANE_JSON="$CP_JSON"` 를 `update_yaml_resumed.sh` 에 넘기고, 그쪽 `:168` 이 이를 읽어 행에 기록한다(A13). 계산 실패 시 워크트리면 exit 2, 제어 루트면 경고 후 무기록 — create 쪽(`create_session.sh:211-221`)과 같은 규칙이다.
|
||||
|
||||
## 3. 항목 5 — 리뷰 identity
|
||||
|
||||
- **기록**: `_record_round`(`runner.py`)가 라운드마다 `iteration/base_sha/head_sha/tree_sha/review_commit/isolation/review_path/jobs/verdicts/reviewers(evidence)/stale` 를 남기고 체크포인트에 `loop_id` 와 `review_rounds` 를 싣는다. 승격 시에는 리포트 옆에 `report-<jid>.identity.json` 을 원자적으로(`.tmp` → `replace`) 쓴다.
|
||||
- **스냅샷**: `_worktree_tree()` 가 임시 `GIT_INDEX_FILE` 로 `read-tree HEAD` → `add -A` → `write-tree` 하여 **실제 인덱스·status·ref 를 건드리지 않는다**. 미커밋이 있으면 `commit-tree` 로 ref 없는 스냅샷 커밋을 만든다. untracked 중첩 저장소는 gitlink 만 남아 고정 불가이므로 거부하고(`_snapshot`), dirty 서브모듈도 거부한다(`_dirty_submodules` 는 `--ignore-submodules=none` 으로 물어 `submodule.*.ignore` 설정이 숨기지 못하게 한다). 판정 불가(`None`)는 전부 실패로 떨어지는 fail-closed 다.
|
||||
- **격리**: `_open_review_tree` 는 `TMPDIR/mam-review/<loop_id>/<n>` 에 detached 워크트리를 만들고, **실패하면 격리를 낮추지 않고 exit 1**(로그 문구까지 그렇게 적혀 있다). `_close_review_tree` 는 remove 실패 시에만 `prune` 을 부르고, 부모 디렉터리는 비었을 때만 지운다. `run()` 이 `_run_body()` 를 `finally: _close_all_review_trees()` 로 감싸고 `release_guard` 도 같은 정리를 한다.
|
||||
- **stale 재리뷰**: 라운드 판정 뒤 `_round_is_stale`(현재 head·tree 비교)로 소스 변경을 잡아 승인 전량 폐기 후 재리뷰, 마지막 라운드면 exit 1. 승격 직전에도 `_identity_now() != (accepted head, tree)` 면 "source changed after approval; not promoting" 으로 exit 1(B10).
|
||||
- **원격 리뷰어**: `_check_remote_recipients` 가 **어떤 잡도 위임하기 전에** 판정 수신자 전원을 검사해 기본 거부, `--remote-review diff-only` 만 허용하며 그때 diff 가 truncated/unavailable 이면 증거가 없다고 exit 1.
|
||||
- **기본 동작 동일성**: `_context_active()` 가 거짓이면 `_worktree_context()` 는 빈 문자열을 반환하고 `collect_changes_diff(None)` 은 기존 호출과 동일하다. `_dispatch` 의 새 인자는 기본값(`remote=False, purpose="work"`)이라 프롬프트는 바이트 동일하다. 다만 옵션과 무관하게 `Loop id:` 로그 1줄과 체크포인트의 `loop_id` 필드가 늘었다(N9, 무해).
|
||||
|
||||
### N1 (비차단, 후속 권고) — 스냅샷 커밋이 gc 로 사라진다
|
||||
|
||||
`_snapshot` 의 스냅샷 커밋은 ref 가 없고, 유일한 도달 경로였던 리뷰 워크트리 HEAD 는 `_close_review_tree` 가 라운드 판정 직후 없앤다. 그래서 `identity.json` 의 `review_commit`·`tree_sha` 는 **나중에 검증하려 할 때 이미 없을 수 있다**. scratchpad 임시 저장소에서 재현했다(워크트리 제거 → `reflog expire --expire=now --all` → `gc --prune=now` → `git cat-file -e <snap>` 실패). 일상적으로도 unreachable 객체는 기본 2주 뒤 대상이다. 미커밋이 있던 라운드에만 해당하며(클린 트리면 `review_commit == head_sha`), 판정 자체의 정합성에는 영향이 없다.
|
||||
|
||||
→ 권고: `refs/mam/review/<loop_id>/<n>` 같은 ref 로 스냅샷을 고정하고, 루프 종료 시 정책적으로 정리(또는 보존).
|
||||
|
||||
### N6 (비차단) — 강제 종료 시 리뷰 워크트리 잔존
|
||||
|
||||
`finally`/`release_guard`/시그널 핸들러로 정상·TERM·INT 경로는 덮이지만, SIGKILL 이면 `TMPDIR/mam-review/...` 디렉터리와 소스 저장소의 worktree 항목이 남는다. `prune` 은 remove 실패 시에만 부른다(의도된 선택, 주석에 근거 있음).
|
||||
|
||||
## 4. #20 격리 — 설계와 실측
|
||||
|
||||
- `tests/conftest.py` 의 `pytest_configure` 가 **수집 이전에** ambient 바인딩 4개를 기록하고 `AGENT_SESSIONS_YAML`·`MAM_JOBS_DIR`·`DELEGATE_JOB_LOGS_DIR` 를 제거한다. `WORKSPACE_ROOT` 는 **다른 체크아웃을 가리킬 때만** 제거한다(이 체크아웃을 가리키면 lib.sh 가 어차피 유도하는 값). import 시점에 `os.environ` 을 복사하는 모듈(`test_orc_onboard.SCRUBBED_ENV`) 때문에 세션 fixture 로는 늦다는 설명이 정확하다. `pytest_unconfigure` 가 되돌려 in-process `pytest.main()` 호출자를 배려한다.
|
||||
- autouse `_isolate_mam_state_dirs` 는 **env 만** 설정하고 디렉터리를 만들지 않으며, 숨김 이름(`.mam-test-state`)이라 `tmp_path` 를 나열하는 기존 테스트를 깨지 않는다. `mam_sandbox` 와 테스트 자신의 `setenv/delenv` 가 뒤에 실행돼 이긴다.
|
||||
- 세션 가드 `mam_state_leak_guard` + `_mam_state_guard.py` 는 **보고만 하고 절대 지우지 않는다**(실제 제어 평면이므로 옳다). 감시 대상은 이 체크아웃의 `.mam` 과 기록된 바인딩이 가리킨 제어 평면이며, 동시에 도는 실제 위임과는 "레코드 먼저 → 등록된 세션" 규칙으로 구분한다.
|
||||
- #15 옵트아웃(N4, 비차단): `test_issue15_registry_isolation.py` 의 누수 단언이 "전체 목록 동일"에서 "자기 잡 id 가 실제 루트에 없음 + 그 사이 늘어난 항목 중 자기 세션 이름이 없음"으로 **약해졌다**. 동시 실행되는 실제 위임 때문에 전체 비교가 오탐을 냈다는 근거가 타당하고, 세션 이름 스캔이 핵심 보장을 유지하므로 수용한다.
|
||||
|
||||
## 5. acceptance5 안정화(`7bc3e1a`·`371099a`·`e6d2168`) — 계약을 약화하지 않는가
|
||||
|
||||
| 변경 | 판단 |
|
||||
|---|---|
|
||||
| 세션 이름·HMAC secret 을 실행마다 유니크하게(`tag = os.urandom(4).hex()`) | **개선**. 공개 브로커에 retain 된 과거 dispatch 가 이번 실행의 잡 대신 배달되던 교차 오염을 없앤다 |
|
||||
| `--heartbeat-misses 3 → 10` | 허용. 이 시험의 목적은 "원격 러너가 브로커만으로 잡을 끝낸다"이고, K×N 무응답 임계 자체는 AC1·AC3 가 고정한다. 원격 러너의 `started --heartbeat` 계약은 그대로다 |
|
||||
| 단언에 `advanced to 'completed'` 추가 | 허용. retain 된 종단 이벤트만 배달된 경우도 프로토콜상 정당한 배달 확인이다 |
|
||||
| `_skip_if_broker_did_not_deliver` | 조건부 허용(N5). skip 은 "러너가 started 를 발행했고, 구독자가 **맞는 토픽**에 SUBSCRIBED 했고, 그 잡에 대해 **아무것도**(드롭 로그조차) 받지 못했다"는 세 조건이 동시에 성립할 때만이다. HMAC·identity 드롭, 잘못된 토픽, 발행 실패는 여전히 실패한다. 다만 구독자 배달 경로의 실제 회귀가 같은 증상을 내면 skip 으로 가려질 수 있다 |
|
||||
|
||||
## 6. 수용 사례·신규 테스트의 결정성·격리
|
||||
|
||||
- `tests/test_issue21_acceptance.py`(A1–A8, 278줄): 실제 herdr·실제 브로커를 쓰지 않는다. mock herdr / stub 위임 + `mam_sandbox`·`tmp_path` 기반이며, 브로커 관련 언급은 주석과 "브로커 도달 불가 조건에서 래퍼를 돌린다"는 A3 한 곳(`:102`)뿐이다.
|
||||
- `tests/test_issue21_loop_b.py`(526줄, L1–L21): 러너를 직접 구동하되 위임·대기를 스텁으로 바꾸고 임시 git 저장소에서 돈다. 브로커 의존 0, `time.sleep` 없음. L1/L12/L14b/L18b 가 **옵션 없는 기존 동작 불변**을 별도로 고정하는 점이 좋다.
|
||||
- `tests/test_issue21_phase2_loop_a.py`(649줄): `time.sleep(0.2)` 1회(`:312`) 외에 시간 의존 없음. `deploy/install.sh` 를 임시 타깃에 실행하는 경로가 있으나 모두 `tmp_path` 안이다.
|
||||
- `tests/test_issue20_state_isolation.py`(202줄): 순수 함수 단위 + 중첩 pytest 1건(설계된 skip).
|
||||
- uuid 사용은 `uuid.uuid4()`(issue15 의 `_unique`)로 브리프 규칙과 맞는다.
|
||||
|
||||
## 7. 문서 정합성과 권고의 타당성
|
||||
|
||||
- `docs/design/worktree-creator-model.md`: 항목 표가 1·2·3·6 = 완료(v4.6.0), 4·5·7 = 이 브랜치, 8 = 보류로 갱신됐고, 실행 예시가 `worktree_prepare.sh`/`worktree_remove.sh` 기준으로 다시 쓰였다. 호환성 표에 resume 의 exit 2 조건, remote 리뷰어 거부/opt-in, "실제 herdr pane 값 확인은 단계 C" 라는 한계가 명시돼 과장 없이 정확하다.
|
||||
- `multi-agent-mux-loop/SKILL.md` 가 `--review-isolation`·`--remote-review` 를 표에 추가하고 기본값·거부 시점(위임 이전)·evidence 기록까지 적는다. create/resume/stop SKILL.md 도 헬퍼를 반영한다.
|
||||
- PR 본문의 **비차단 권고 4건**을 코드에서 확인했고 모두 사실이다: ① A4 의 `_main_state` 비교가 untracked `.agents/`·`.mam/` 변화를 보지 못함, ② A4 가 `info/exclude` 추가를 **선반영**한 뒤 비교함(`test_issue21_acceptance.py:121-122`), ③ `_close_review_tree` 의 remove 실패 시 `prune` 이 전역, ④ `worktree_remove.sh` 의 서브모듈 거부 출력 중복. 넷 다 병합을 막을 성질이 아니며 후속으로 적절하다.
|
||||
- #30(멈춘 에이전트 감지) 등록은 타당하다. #18 이 "프로세스 생존"에 결속된 설계라 "살아 있지만 멈춘" 상태는 그 계약 밖이며, 해결은 알림·운영 수준이므로 별도 이슈가 맞다.
|
||||
|
||||
## 8. 전체 스위트 실행 (이 리뷰어가 직접, 워크트리에서)
|
||||
|
||||
```
|
||||
cd <pr-29 워크트리> && PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q -rfE tests/
|
||||
→ 2 failed, 1011 passed, 2 skipped in 2266.28s (0:37:46) (HEAD 46ea4cc, 실행 전후 동일)
|
||||
```
|
||||
|
||||
| 실패 | 판정 |
|
||||
|---|---|
|
||||
| `test_issue14_delivery_ack.py::test_wrapper_idle_grace_never_outlives_the_wall_clock` | **PR 회귀 아님**. 이 시험은 PR 이 건드리지 않았다(`git diff -U0` 의 issue14 hunk 는 전부 `_job_for_inprocess_publish` 뒤쪽 `:520` 이후). 단독 재실행에서도 close_delay 28.5 s(임계 20+8)로 재현되며, **다른 브랜치에 이미 `90a289d test(issue-14): widen the wall-clock close margin under suite load` 가 있다** — 임계 자체가 좁다는 기지 문제다 |
|
||||
| `test_issue19_subscriber_daemon_lifecycle.py::test_mqtt_loop_sigterm_exits_cleanly` | **브로커 flake**. 파일 자체가 PR 변경 0줄이고, 로그가 `broker did not accept connection; falling back to polling` 이다. 단독 재실행 **통과** |
|
||||
|
||||
- PR 본문의 "이 호스트 기준 실패 33건(#28)" 주장은 **내 실행으로는 확인되지 않는다**(내 조건에서는 2건). 제어 평면 바인딩이 실린 creator pane 조건에서 재현된 수치로 보이며, 내가 재현할 수 있는 조건이 아니므로 그 수치의 참·거짓을 주장하지 않는다. 내가 말할 수 있는 것은 **내 조건에서 새 회귀가 0 이라는 것**이다.
|
||||
|
||||
### 격리·위생 실측 (#20, 브리프 항목 4)
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| 실제 `.mam/jobs`(본 체크아웃) 전후 | **차이 0** (레코드 추가 0건) |
|
||||
| 실제 `.mam/delegate_job_logs` 전후 | **차이 0** |
|
||||
| 워크트리 `git status --short --ignored` 전후 | **완전 동일**(무시 파일 포함) — 스위트가 워크트리에 아무것도 남기지 않았다 |
|
||||
| 워크트리 HEAD | 실행 전후 `46ea4cc` |
|
||||
| 본 체크아웃 | `main @ 1134d74`, 클린 |
|
||||
|
||||
이전 회차(PR #22)에서 내가 지적했던 "리뷰어 pytest 가 워크트리에 `.mam/delegate_job_logs`·`.mam/shim` 을 남긴다" 가 이번 실행에서는 **재현되지 않았다**. #20 수정이 의도대로 동작한다.
|
||||
|
||||
## 9. 병합 차단 여부 (브리프 항목 8)
|
||||
|
||||
알려진 버전 bump 미포함(오케스트레이터가 v4.7.0 으로 처리) 외에 **병합을 막을 결함은 찾지 못했다**. N1–N9 는 모두 비차단이며, 후속 순위는 N1 > N2 > N5 순으로 본다.
|
||||
|
||||
| # | 지적 | 성격 |
|
||||
|---|---|---|
|
||||
| N1 | 스냅샷 커밋·tree 가 ref 없이 남아 gc 로 소멸 → `identity.json` 사후 검증 불가(재현 확인) | 후속 이슈 권고 |
|
||||
| N2 | prepare 의 `.venv` 는 여전히 심링크(실제 venv/인터프리터 경로 아님); 커밋 경로는 3중으로 닫혔으나 `rm -rf .venv/`·의존성 공유 위험 잔존 | 문서화 또는 옵션화 권고 |
|
||||
| N3 | prepare 가 공유 `info/exclude` 를 바꿔 본 체크아웃에도 적용(되돌림 경로 없음) | 기지·문서화됨 |
|
||||
| N4 | issue15 누수 단언이 전체 목록 비교에서 자기 잡 id + 세션 스캔으로 약화(근거 타당) | 수용 |
|
||||
| N5 | acceptance5 의 skip 경로가 구독자 배달 회귀를 가릴 여지(조건은 매우 좁음), `--heartbeat-misses 3→10` | 수용 |
|
||||
| N6 | SIGKILL 시 리뷰 워크트리·worktree 항목 잔존(prune 은 실패 시에만) | 사소 |
|
||||
| N7 | remove 의 열린 잡 검사가 행 없는 워크트리 로컬 `.mam/jobs` 를 못 봄 | 모델 밖 경로 |
|
||||
| N8 | remove 는 plain/force 모두 무시 파일을 말없이 삭제(추적 작업 손실은 없음) | 정보 |
|
||||
| N9 | 옵션 없는 루프에도 `Loop id:` 로그 1줄과 체크포인트 `loop_id` 필드가 추가(프롬프트는 바이트 동일) | 정보 |
|
||||
|
||||
## 10. 교차검증 가이드
|
||||
|
||||
```bash
|
||||
WT=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29
|
||||
(cd "$WT" && PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q -rfE tests/) # 2 failed / 1011 passed
|
||||
(cd "$WT" && PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q \
|
||||
tests/test_issue14_delivery_ack.py::test_wrapper_idle_grace_never_outlives_the_wall_clock \
|
||||
tests/test_issue19_subscriber_daemon_lifecycle.py::test_mqtt_loop_sigterm_exits_cleanly) # issue19 통과, issue14 재현
|
||||
git -C "$WT" diff 1134d74...HEAD -U0 -- tests/test_issue14_delivery_ack.py | grep '^@@' # 실패 시험은 변경 밖
|
||||
# N1 재현 (임시 저장소에서만)
|
||||
R=$(mktemp -d); cd "$R"; git init -q .; git config user.email t@t; git config user.name t
|
||||
echo a > a; git add a; git commit -qm base; H=$(git rev-parse HEAD); echo dirty > b
|
||||
GIT_INDEX_FILE=$R/idx git read-tree HEAD; GIT_INDEX_FILE=$R/idx git add -A; T=$(GIT_INDEX_FILE=$R/idx git write-tree)
|
||||
C=$(git commit-tree "$T" -p "$H" -m snap); git worktree add -q --detach "$R/wt" "$C"; git worktree remove --force "$R/wt"
|
||||
git reflog expire --expire=now --all; git gc --prune=now -q; git cat-file -e "$C^{commit}" || echo "snapshot gone"
|
||||
```
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,189 @@
|
||||
# 🔍 Review Report — PR #29 델타 리뷰 2회차 (job `f1174fc0`)
|
||||
|
||||
- **Reviewer**: `planner-reviewer-claude-01` (role: planner,reviewer) · 2026-09-20
|
||||
- **대상 델타**: `46ea4cc..08db1da` (3커밋, `git diff --stat` = **16 files, +159/−34**), base `main 1134d74` (v4.6.0).
|
||||
- **검토 트리**: 워크트리 `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` (detached @ `08db1da`).
|
||||
- **1회차 내 판정**: PASS (job `291e38b5`). 이번에는 **델타가 그 판정을 뒤집는지**를 본다.
|
||||
- **안전 규칙 준수**: 워크트리 읽기 전용(수정·커밋·checkout 없음, `git status --short` 클린 유지). 본 체크아웃 `main @ 1134d74` 그대로. `worktree_prepare.sh`·`worktree_remove.sh` 를 실제 워크트리·본 체크아웃에 실행하지 않았다 — 모든 실행은 scratchpad 의 임시 복사본/임시 저장소(uuid4 이름)에서만 했다. 실제 `.mam`·데몬·세션 조작 없음, push·merge·PR 상태 변경 없음, purge·scrub 실행 없음.
|
||||
|
||||
## 결론 요약
|
||||
|
||||
**R1·R2·R3 은 실제로 닫혔다.** 특히 R1/R2 는 (a) 결함을 임시 저장소에서 직접 재현했고, (b) 워크트리 복사본에서 수정만 되돌렸을 때 새 회귀 4건이 정확히 실패하는 것까지 확인했다. 1회차 PASS 를 뒤집을 사유는 없다. 비차단 4건(D1–D4)만 남긴다. 그중 D1 은 이번 델타가 새로 만든 작은 구멍이다.
|
||||
|
||||
## 1. R1/R2 — `--untracked-files=all` (항목 1)
|
||||
|
||||
### 1-1. 결함 재현 (임시 저장소)
|
||||
|
||||
```
|
||||
git config status.showUntrackedFiles no ; echo secret > untracked.txt
|
||||
git status --porcelain --ignore-submodules=none → (빈 출력) ← 옛 검사: "클린"
|
||||
git status --porcelain --untracked-files=all --ignore-submodules=none → ?? untracked.txt ← 수정본
|
||||
```
|
||||
|
||||
표시 설정 하나로 "강제 제거해도 되는가"라는 판정이 뒤집혔다는 codex 의 지적은 사실이다.
|
||||
|
||||
### 1-2. 세 검사 모두에 들어갔는가 — 그렇다
|
||||
|
||||
| 파일:라인 | 검사 |
|
||||
|---|---|
|
||||
| `.agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh:93` | `judge_tree()` 의 워크트리 청결 검사 |
|
||||
| `.agents/skills/lib_py/worktree.py:170` | `submodule_findings()` 의 `dirty` 검사(`modules/` 아래 모든 저장소) |
|
||||
| `.agents/skills/lib_py/loop/runner.py:718` | `_dirty_submodules()` (리뷰 스냅샷 승인 전 서브모듈 검사) |
|
||||
|
||||
스킬 트리 전체에서 `status --porcelain` 을 부르는 나머지 두 곳도 확인했다.
|
||||
- `worktree_prepare.sh:209`: 롤백 후 "이 워크트리가 아직 쓸 수 있는 체크아웃인가"만 보는 용도(stdout 은 버리고 stderr 만 본다). 파괴 판정이 아니다 — 플래그 불필요.
|
||||
- `runner.py:581`: 프롬프트의 `미커밋 변경: 있음/없음` **표시용**. 판정에 쓰이지 않는다(→ D3 참조).
|
||||
|
||||
### 1-3. 소스 트리 스냅샷은 별도 조치가 필요 없는가 — 필요 없다
|
||||
|
||||
`_worktree_tree()` 의 `git add -A` 는 `status.showUntrackedFiles` 를 **보지 않는다**. 임시 저장소에서 `showUntrackedFiles=no` 로 설정한 뒤 `read-tree HEAD` → `add -A` → `write-tree` 한 트리에 `untracked.txt` 가 그대로 들어갔다. 즉 스냅샷은 이미 untracked 를 고정하고 있었고, 구멍은 "서브모듈 검사"와 "제거 판정"에만 있었다. 수정 범위가 정확하다.
|
||||
|
||||
`_change_set_state()`(`runner.py`)와 `diff_collect.sh` 의 untracked 수집은 `git ls-files -o --exclude-standard` 라 이 설정의 영향을 받지 않는다. 빈 변경분 fail-closed 가드도 안전하다.
|
||||
|
||||
### 1-4. 새 회귀가 수정 없이는 실패하는가 — 확인함
|
||||
|
||||
워크트리를 `.git`·`.venv` 제외하고 scratchpad 에 복사한 뒤, **세 곳의 `--untracked-files=all` 만 제거**하고 돌렸다(워크트리 자체는 건드리지 않았다).
|
||||
|
||||
```
|
||||
4 failed, 5 passed in 14.69s
|
||||
FAILED test_issue21_phase2_loop_a.py::test_w6e2_untracked_submodule_file_hidden_by_config_blocks_the_forced_removal
|
||||
FAILED test_issue21_phase2_loop_a.py::test_w6e_work_written_while_stopping_is_not_force_removed[True]
|
||||
FAILED test_issue21_loop_b.py::test_l17_dirty_submodule_is_refused_or_makes_the_round_stale[untracked-hidden]
|
||||
FAILED test_issue21_loop_b.py::test_l17_dirty_submodule_is_refused_or_makes_the_round_stale[during-hidden]
|
||||
```
|
||||
|
||||
기존 변형(`[False]`, `before`, `before-ignored`, `untracked`, `during`)은 그대로 통과했다. 즉 새 회귀는 **정확히 이 수정만** 검증하며, 회귀 방지력이 실재한다. `test_w6e2` 가 단언 안에서 `assert _git(wt,"status","--porcelain","--ignore-submodules=none").stdout == "", "git hides it by default"` 로 전제 자체를 고정한 것도 좋다.
|
||||
|
||||
## 2. R3 — 워크트리 자체 venv (항목 2)
|
||||
|
||||
`worktree_prepare.sh:111-134` 의 분기를 읽고 문서·테스트와 대조했다(스크립트는 실제 트리에 실행하지 않았다).
|
||||
|
||||
| 요구 | 구현 | 판정 |
|
||||
|---|---|---|
|
||||
| 심링크가 아닌 실제 venv | `"$MAIN/.venv/bin/python" -m venv "$WT/.venv"` + `requirements.txt`·`requirements-dev.txt` 설치 | **충족**. 1회차 내 지적 N2 가 닫혔다 |
|
||||
| 멱등성 | `[ -x "$WT/.venv/bin/python" ]` → `VENV_STATE="kept"` | 충족(`test_w1` 이 `VENV=created` → `VENV=kept` 순서를 고정) |
|
||||
| 남은 링크 거부 | `[ -L "$WT/.venv" ]` → exit 1, **아무것도 바꾸지 않음** | 충족(`test_w1b` 가 링크 보존까지 단언) |
|
||||
| 인터프리터 부재 | `WARNING: ... not found; no venv created` + `VENV=skipped` | 충족 |
|
||||
| 설치 실패 시 exit 1, 워크트리 유지 | `pip install` 실패 → 메시지 `(the worktree is kept)` + exit 1. `git worktree add` 는 이미 끝난 뒤라 실제로 남는다 | 충족 |
|
||||
| 문서 정합 | `create/SKILL.md:246`, `stop/SKILL.md:123`, 설계 문서 4장이 모두 "자체 venv·링크 거부·`VENV=` 요약"으로 갱신됨 | 충족 |
|
||||
| ignore 규칙 정합 | `.gitignore` 관리 블록의 `/.venv`(#27) 와 공유 `info/exclude` 의 `/.venv` 는 **디렉터리에도 그대로 적용**된다. `test_w1` 이 준비 후 `status --porcelain` 이 비는지 단언 | 충족 |
|
||||
|
||||
`bash -n` 은 변경된 두 스크립트 모두 통과한다. `req=()` 배열 + `[ -f ... ] && req+=(...)` 가 `set -euo pipefail` 아래서 조기 종료하지 않는지 macOS `/bin/bash` 3.2.57 로 직접 확인했다(두 파일 모두 없을 때도 루프 끝까지 도달, rc 0).
|
||||
|
||||
### D1 (비차단, 이번 델타가 새로 만든 구멍) — 설치 실패 뒤 재실행이 조용히 `VENV=kept`
|
||||
|
||||
`pip install` 이 실패하면 exit 1 이지만 `$WT/.venv/bin/python` 은 **이미 만들어져 있다**(`:121` 에서 venv 생성이 먼저 끝난다). 그래서 사용자가 그대로 재실행하면 `:117-118` 의 `-x` 검사에 걸려 `VENV=kept` 로 넘어가고, **요구사항이 설치되지 않은 환경**을 정상으로 보고한다. 메시지는 "fix it and re-run" 이라 재실행을 유도한다.
|
||||
|
||||
→ 권고: 설치 성공을 표시하는 마커(예: `$WT/.venv/.mam-requirements-ok`)를 두고 `kept` 판정에 함께 쓰거나, `kept` 경로에서도 `requirements*.txt` 설치를 한 번 더 시도.
|
||||
|
||||
### D2 (비차단, 정보) — 준비 비용·네트워크 의존
|
||||
|
||||
이제 워크트리마다 venv 생성 + PyPI 설치가 필요하다. 오프라인이거나 PyPI 가 막힌 호스트에서는 prepare 가 exit 1 로 끝난다(이전에는 링크 한 번이라 즉시 끝났다). SKILL.md 에 설치 동작은 적혀 있으나 "네트워크가 필요하다"는 명시는 없다.
|
||||
|
||||
### D3 (비차단, 사소) — 프롬프트의 `미커밋 변경` 표시는 여전히 설정에 취약
|
||||
|
||||
`runner.py:581` 의 `git status --porcelain` 에는 플래그가 없어, `status.showUntrackedFiles=no` 인 소스에서는 untracked 만 있을 때 `미커밋 변경: 없음` 으로 표시된다. 판정·스냅샷·서브모듈 검사는 모두 안전하므로 **표시 오차**일 뿐이지만, R1 의 취지("표시 설정이 판단 재료를 바꾸면 안 된다")를 생각하면 같은 플래그를 주는 편이 일관적이다.
|
||||
|
||||
## 3. #28 이식성 수정(`7a94ba2`)이 의도를 약화시키는가 (항목 3)
|
||||
|
||||
### 3-1. `ps -eo pid=,ppid=,args=` — 양쪽에서 같은 출력인가
|
||||
|
||||
이 호스트(macOS 15, arm64)에서 **두 형태를 같은 프로세스에 대해 직접 비교**했다(파이프로 캡처, 코드와 동일 조건).
|
||||
|
||||
```
|
||||
['-axo','pid=,ppid=,command='] → 길이 575, 인자 X 400개 그대로
|
||||
['-eo','pid=,ppid=,args='] → 길이 575, 인자 X 400개 그대로
|
||||
```
|
||||
|
||||
- **구분자·필드**: 동일(`pid ppid args`), 잘림 없음. 400자 argv 가 온전히 나왔다 — 파이프 출력일 때는 폭 제한이 걸리지 않는다.
|
||||
- **이식성**: `-e`(모든 프로세스), `-o`(포맷), `args`(전체 명령행)는 POSIX 다. 반대로 `-a`+`-x` 조합과 `command=` 는 BSD 관용이고 procps 에서는 경고·다른 해석이 나올 수 있다. purge 12건의 원인 가설로 타당하며, **macOS 동작은 그대로**다(위 실측).
|
||||
- 남는 차이: Linux 의 `args` 도 커널 `/proc/<pid>/cmdline` 기준이라 매우 긴 argv 는 환경에 따라 잘릴 수 있다. 다만 이 코드의 판정(에이전트 CLI 여부, UUID 포함 여부)은 앞쪽 토큰에 의존하므로 실질 영향은 없다.
|
||||
|
||||
### 3-2. `_normalize_help`
|
||||
|
||||
`optional arguments:` → `options:` 치환 + 공백 런 축약이다. 즉 **레이아웃(줄바꿈·들여쓰기) 동등성은 포기하고 내용 동등성만** 본다. 골든 캡처가 3.9 에서 떠졌고 대상 호스트가 3.14 라는 사정상 타당하며, 플래그·인자·설명 문구가 바뀌면 여전히 실패한다(시험의 목적은 "shim 이 구현과 CLI 상 구분 불가"이므로 유지). 약화는 제한적이고 대안(버전별 골든 캡처)은 유지비가 크다 — 수용.
|
||||
|
||||
### 3-3. `test_workspace_scope.py` 의 `bash -c`
|
||||
|
||||
`shell=True` 는 `/bin/sh` 를 쓰므로 dash 에서 `source` 가 없다. 문자열을 그대로 `["bash","-c", cmd]` 로 옮긴 것은 최소 변경이며 검증 내용(=`resolve_herdr_session` 의 출력)은 그대로다. 수용.
|
||||
|
||||
### 3-4. conftest 의 `MAM_ENV_FILE` 고정
|
||||
|
||||
`tests/conftest.py:173-177` 이 autouse 픽스처에서 `MAM_ENV_FILE` 을 `tmp_path/.mam-test-state/.mam.env.absent` 로 고정한다. 방향은 #20·#15 와 같은 **격리 강화**이고, 실제 `.mam.env` 를 읽어야 하는 시험은 스스로 `MAM_ENV_FILE` 을 설정한다(`test_issue14/18/5_phase1/21_worktree_prereqs/20_state_isolation` 모두 자체 설정을 갖고 있다). "브로커 없음" 계열 시험이 호스트의 실 설정을 주워 읽던 것이 오히려 결함이었으므로 계약을 약화시키지 않는다.
|
||||
|
||||
## 4. `90a289d` 벽시계 여유 8 → 15 s (항목 4)
|
||||
|
||||
이 시험이 고정하는 결함은 "idle grace 가 wall clock 을 **무시**해 `2 × TIMEOUT`(≈40 s) 에 닫히는 것"이다. 임계는 `20 + 15 = 35 s` 이므로 결함 시나리오(40 s)는 여전히 실패한다. 정상 종료는 내가 1회차 전체 스위트에서 실측한 값이 28.49 s·28.50 s(단독 재실행 포함 2회 일치)였다.
|
||||
|
||||
```
|
||||
정상 28~29 s < 새 임계 35 s < 결함 40 s
|
||||
```
|
||||
|
||||
여유는 5 s 로 좁아졌지만 계약(“wall clock 을 넘기지 않는다”)의 판별력은 남는다. 1회차에서 내가 유일한 비-flake 실패로 보고했던 항목이 이 커밋으로 해소된다. 다만 `timeout_s` 가 더 커지면 `2 × TIMEOUT` 과 `TIMEOUT + 15` 의 간격은 벌어지고 작아지면 좁아지므로, **고정 여유(+15)보다 비율(예: `timeout_s * 1.5`)** 이 계약을 더 정확히 표현한다(D4, 비차단 권고).
|
||||
|
||||
## 5. 테스트 실행 (워크트리, 이 리뷰어가 직접)
|
||||
|
||||
```
|
||||
cd <pr-29 워크트리> && PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q -rfEs \
|
||||
tests/test_issue21_phase2_loop_a.py tests/test_issue21_loop_b.py tests/test_issue21_worktree_prereqs.py \
|
||||
tests/test_issue21_acceptance.py tests/test_workspace_scope.py tests/test_issue5_phase1_delegation.py \
|
||||
tests/test_deploy_freshness.py tests/test_version_consistency.py
|
||||
→ 208 passed in 322.94s (failed 0 · errors 0 · skipped 0)
|
||||
```
|
||||
|
||||
- 브리프가 지적한 **`tests/test_issue28_*.py` 는 이 트리에 없다**(#28 수정은 기존 파일에 들어갔다) — 규칙대로 건너뛰었다.
|
||||
- **skip 0 건**: `test_deploy_freshness.py` 의 새 `skipif`(D-11~D-19, D-22~D-30)는 이 호스트에서 **전부 실행**됐다(서브모듈 자산이 있으므로 의도대로다). 브리프 항목 3 의 우려("로컬 자산이 있는 호스트에서는 실행되어야 한다")는 실측으로 해소.
|
||||
- 가장 느린 시험 10건은 모두 herdr mock·정지/재개 경로이며 브로커 타임아웃 계열 실패는 없었다.
|
||||
|
||||
### 격리·위생 실측
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| 실제 `.mam/jobs` 전후 | **차이 0** |
|
||||
| 워크트리 `git status --short --ignored` 전후 | **완전 동일** |
|
||||
| 워크트리 HEAD / 본 체크아웃 | `08db1da` / `main 1134d74` (둘 다 변화 없음) |
|
||||
| 샌드박스 실험 | scratchpad 임시 디렉터리에서만(복사본 리버트 실행 포함). 실제 스크립트·워크트리에 prepare/remove 실행 없음 |
|
||||
|
||||
## 6. 병합 차단 여부 (항목 5)
|
||||
|
||||
버전 락스텝 bump 부재(`lib.sh:32`·`eager.py:16` 모두 `4.6.0`, 오케스트레이터가 4.7.0 으로 처리 예정) 외에 **병합을 막을 결함은 없다**. 1회차 PASS 판정은 이번 델타로 뒤집히지 않으며, 1회차에 내가 남긴 비차단 지적 중 **N2(.venv 심링크)는 이 델타로 해소**됐다. 1회차의 N1(스냅샷 커밋이 ref 없이 남아 gc 로 소멸)은 그대로 남아 있으니 후속 이슈로 유지하기를 권한다.
|
||||
|
||||
| # | 이번 회차 지적 | 성격 |
|
||||
|---|---|---|
|
||||
| D1 | pip 설치 실패 후 재실행이 `VENV=kept` 로 조용히 통과 | 후속 권고(이번 델타가 만든 것) |
|
||||
| D2 | prepare 가 워크트리마다 venv 생성 + PyPI 설치를 요구(오프라인 exit 1) | 정보·문서화 |
|
||||
| D3 | `runner.py:581` 의 표시용 status 에는 `--untracked-files=all` 이 없음 | 사소(일관성) |
|
||||
| D4 | 벽시계 여유를 고정값(+15 s)이 아니라 비율로 두는 편이 계약에 충실 | 사소 |
|
||||
|
||||
## 7. 교차검증 가이드
|
||||
|
||||
```bash
|
||||
WT=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29
|
||||
# R1 결함 재현 (임시 저장소에서만)
|
||||
R=$(mktemp -d); cd "$R"; git init -q .; git config user.email t@t; git config user.name t
|
||||
echo a>a; git add a; git commit -qm base; git config status.showUntrackedFiles no; echo secret > untracked.txt
|
||||
git status --porcelain --ignore-submodules=none # (빈 출력)
|
||||
git status --porcelain --untracked-files=all --ignore-submodules=none # ?? untracked.txt
|
||||
GIT_INDEX_FILE=$R/idx git read-tree HEAD; GIT_INDEX_FILE=$R/idx git add -A
|
||||
git ls-tree -r --name-only "$(GIT_INDEX_FILE=$R/idx git write-tree)" # untracked.txt 가 들어 있다(= add -A 는 설정 무시)
|
||||
# 수정 없이는 회귀가 실패하는지 (워크트리 복사본에서만)
|
||||
S=$(mktemp -d); rsync -a --exclude .venv --exclude .git --exclude .mam "$WT/" "$S/"
|
||||
sed -i '' 's/ --untracked-files=all --ignore-submodules=none/ --ignore-submodules=none/' \
|
||||
"$S/.agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh" "$S/.agents/skills/lib_py/loop/runner.py"
|
||||
sed -i '' 's/"git status --porcelain --untracked-files=all"/"git status --porcelain"/' "$S/.agents/skills/lib_py/worktree.py"
|
||||
(cd "$S" && PYTHONDONTWRITEBYTECODE=1 "$WT/.venv/bin/python" -m pytest -p no:cacheprovider -q \
|
||||
tests/test_issue21_phase2_loop_a.py::test_w6e2_untracked_submodule_file_hidden_by_config_blocks_the_forced_removal \
|
||||
tests/test_issue21_phase2_loop_a.py::test_w6e_work_written_while_stopping_is_not_force_removed \
|
||||
tests/test_issue21_loop_b.py::test_l17_dirty_submodule_is_refused_or_makes_the_round_stale) # 4 failed, 5 passed
|
||||
# ps 형태 비교(잘림·구분자)
|
||||
python3 - <<'PY'
|
||||
import subprocess,sys,time
|
||||
p=subprocess.Popen([sys.executable,"-c","import time;time.sleep(6)","X"*400]); time.sleep(1)
|
||||
for f in (["-axo","pid=,ppid=,command="],["-eo","pid=,ppid=,args="]):
|
||||
l=[x for x in subprocess.run(["ps",*f],capture_output=True,text=True).stdout.splitlines() if x.split()[:1]==[str(p.pid)]]
|
||||
print(f, len(l[0]), l[0].count("X"))
|
||||
p.wait()
|
||||
PY
|
||||
```
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,71 @@
|
||||
# PR #29 independent review
|
||||
|
||||
Job: b880c1f2. Reviewer: planner-reviewer-codex-01.
|
||||
Review target: detached `multi-agent-mux-wt/pr-29` at `46ea4cc1e4f664ca35711c05c3fa71258e169697`, compared with main `1134d74b3884d5818a4f738f5c9441b80f3f8802`.
|
||||
|
||||
**NOT PASS.** Two reproduced correctness defects remain in the handling of hidden untracked submodule files. Preparation also fails the brief's explicit environment requirement. These are separate from the known pending 4.7.0 release bump. No broad redesign is necessary: the guard corrections are narrow and were verified in disposable fixtures.
|
||||
|
||||
## Blocking findings
|
||||
|
||||
### R1 — P1: removal can silently delete untracked submodule data
|
||||
|
||||
Locations: `.agents/skills/lib_py/worktree.py:167` and `.agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh:91` (the resulting force-removal path is at line 161).
|
||||
|
||||
Both cleanliness checks inherit Git's `status.showUntrackedFiles` configuration. `--porcelain` does not override that setting. In an initialized submodule with `git config status.showUntrackedFiles no`, a normal, non-ignored untracked file is hidden from both the parent status and the submodule `foreach` check. The helper consequently reports that all checks passed and executes `git worktree remove --force`, deleting the file.
|
||||
|
||||
I reproduced this using the existing `ctl_sub` sandbox fixture and the actual preparation/removal scripts, with no real worktree removal:
|
||||
|
||||
1. Prepare a temporary worktree with its local test submodule initialized.
|
||||
2. Set `status.showUntrackedFiles=no` in that submodule and create `valuable-untracked.txt` there.
|
||||
3. Parent `status --porcelain --ignore-submodules=none` returns an empty string; explicit submodule `status --porcelain --untracked-files=all` reports the file.
|
||||
4. Removal returns **0**, prints `checks: locked/sessions/jobs/clean/submodules -> passed; removal method: single --force`, and the file no longer exists.
|
||||
|
||||
**Correction:** explicitly use `--untracked-files=all` in the parent and recursive submodule safety checks, retaining `--ignore-submodules=none`; do not let display preferences govern deletion eligibility. Add regressions with this configuration both before removal and for the post-stop recheck. I changed only the disposable fixture's copy of the submodule check to `git status --porcelain --untracked-files=all --ignore-submodules=none`: removal then returned **3** and preserved the file. The reviewed source remains unchanged.
|
||||
|
||||
### R2 — P1: the review snapshot can approve a dirty submodule it did not capture
|
||||
|
||||
Location: `.agents/skills/lib_py/loop/runner.py:717` (`_dirty_submodules`).
|
||||
|
||||
The review-identity guard has the same configuration dependency. With `status.showUntrackedFiles=no` inside a submodule, untracked content is not rejected. The snapshot records only the submodule gitlink, so that content is absent from the review tree and from the identity comparison. A stubbed PASS is then accepted and promoted, although the documented dirty-submodule refusal should have prevented any review.
|
||||
|
||||
Using the existing temporary `wt_repo` and `_drive` fixtures, I added a clean submodule, set this configuration, and created a non-ignored untracked file. The actual controller returned **0**, dispatched **one review**, and wrote **one PASS identity sidecar**. This demonstrates the controller admission/promotion defect; it does not represent a real reviewer's assessment.
|
||||
|
||||
**Correction:** add `--untracked-files=all` to `_dirty_submodules` so both snapshot admission and subsequent stale/pre-promotion checks reject these files. An in-memory override of only that status command made the same probe return **1**, dispatch zero reviews, and write no sidecar. Add configuration-sensitive tests for both initial and mid-round untracked changes.
|
||||
|
||||
### R3 — P2: preparation still aliases the control environment
|
||||
|
||||
Location: `.agents/skills/multi-agent-mux-create/scripts/worktree_prepare.sh:111`–118; related assertion: `tests/test_issue21_phase2_loop_a.py:264`.
|
||||
|
||||
The brief explicitly asks for a real worktree venv rather than a symlink, or explicit interpreter-path handling. The helper instead executes `ln -s "$MAIN/.venv" "$WT/.venv"`; its test requires that link, and the design/skill documents describe it. This is an implementation-and-test requirement gap, not a failing existing test. A temporary fixture confirmed that writing through the worktree `.venv` changes the control root's environment. The PR review checkout's own real venv does not establish what this helper creates.
|
||||
|
||||
**Correction:** create an independent environment with the intended dependencies, or use the brief's explicit interpreter-path alternative without creating a misleading local environment alias. Update the W1 assertion and corresponding preparation documentation. The loop's review prompt already provides an absolute interpreter path, but that does not change preparation behavior for creators.
|
||||
|
||||
## Other review results
|
||||
|
||||
- **R1–R6 removal defenses:** the post-stop tree recheck, stash checks, retained/deinitialized submodule repository checks, partial-repository refusals, and traversal of HEAD plus local refs including tags are present. The existing regressions cover those cases and passed. They do not exercise `status.showUntrackedFiles=no`, which explains the additional R1 failure above. Initialization rollback retains preexisting submodule repositories/configuration in the tested cases; initialization over a nonempty uninitialized path is refused.
|
||||
- **Resume and bindings:** the original-cwd check uses canonical paths, runs before session resolution, rejects missing/mismatching recorded cwd with exit 2, and leaves rows without cwd compatible. Single-workspace resume at its recorded path and already-running binding persistence passed. Create now starts immediate reconcile/watchdog from the control runtime, and preparation supplies `MAM_NO_SUBSCRIBER_DAEMON=1`.
|
||||
- **Identity workflow:** snapshots use a temporary index, include ordinary untracked source files, pin the diff to the snapshot, and generate detached review worktrees. Stale rounds discard approvals; promotion rechecks identity. Failure to create a review worktree does not silently lower isolation. Remote verdict recipients are refused before delegation unless explicitly admitted as diff-only; truncated/unavailable evidence is refused for those recipients. Self-review uses the identity path. These behaviors passed focused tests, subject to R2. With no worktree/base options, existing delegation prompts and the two-argument diff collector remain compatible; output is not literally byte-identical everywhere because loop-id logging/checkpoint data are new.
|
||||
- **#20 isolation:** ambient bindings are recorded/dropped before collection, and function-scoped defaults route registry/audit state to tmp_path. The #15 default-resolution tests explicitly remove overrides where necessary and check their own job/session identities. The guard watches recorded control-plane paths, not only the review checkout. Direct directory snapshots found **zero added entries** in main and review checkout `.mam/jobs` and `.mam/delegate_job_logs` during this review's tests.
|
||||
- **Acceptance A1–A8:** these are sandbox/mock integration tests, not proof of environment delivery inside a real herdr pane. The documentation now states that boundary. The tests exercise caller arguments, routing, stale review rejection, cleanup, and loop locking; all passed in the focused run. A3 uses a deliberately unreachable broker, so failure timing is bounded but the suite is not uniformly instantaneous. Real-herdr acceptance remains separate evidence to obtain before claiming complete operational validation.
|
||||
- **acceptance5 changes:** unique session/secret values remove the old fixed-session collision, retained-terminal delivery is a valid acknowledgement path, and product `started --heartbeat` behavior is unchanged. The test passed in this run. Caution: the new skip predicate at `tests/test_issue18_liveness_contract.py:1183` checks local subscription plus absence of observed events; it does not independently establish the topic actually published by the runner. Its claim that a wrong publish topic must still fail is stronger than its evidence. A controlled broker or assertions against publisher topic/outcome evidence would make this classification more reliable. Do not treat a skipped acceptance run as a PASS.
|
||||
- **Documentation/follow-ups:** the brief's four nonblocking follow-ups are reasonable: strengthen A4 beyond status/path equality to compare untracked contents; make shared info/exclude mutation and its pre-seeding in A4 explicit; scope fallback prune to owned review metadata; deduplicate submodule refusal output. A4 currently proves only its stated tuple invariants. Tracking these under the proposed #30 follow-up is appropriate, but I did not independently inspect the external issue's registration/content. R1/R2 above must be fixed before merge rather than deferred as those polish items.
|
||||
- **Release:** 4.7.0 lockstep changes and release notes remain a known merge prerequisite. Version-consistency tests passing on 4.6.0 do not fulfill it. `git diff --check 1134d74...HEAD` passed; `bash -n` passed for all eight changed shell files.
|
||||
|
||||
## Verification record
|
||||
|
||||
All pytest runs used the requested worktree's own `.venv/bin/python`, `PYTHONDONTWRITEBYTECODE=1`, and `-p no:cacheprovider`. Additional probes were injected in memory through pytest collection and used existing temporary fixtures; only the fix-direction probe altered a fixture's disposable source copy.
|
||||
|
||||
| Run | Result |
|
||||
|---|---|
|
||||
| `test_issue21_acceptance.py`, `test_issue21_phase2_loop_a.py`, `test_issue21_loop_b.py`, `test_issue21_worktree_prereqs.py`, `test_issue20_state_isolation.py`, `test_issue15_registry_isolation.py`, `test_version_consistency.py` | **118 passed, 1 skipped**, 289.64 s. Skip is the outer collection of the deliberately nested S6 probe. |
|
||||
| `test_issue14_delivery_ack.py`, `test_issue18_liveness_contract.py`, `test_issue6_turn_handshake.py` | **56 passed, 1 failed**, 331.60 s. |
|
||||
| Isolated wall-clock regression rerun | **1 failed**, 31.24 s; close delay 28.5585 s against 28 s. |
|
||||
| R1/R2 safety assertions against unmodified implementation | Both failed as described above, reproducing the blockers. |
|
||||
| R1 disposable-copy correction / R2 in-memory correction | Each passed: removal preserved the file; identity refused before review. |
|
||||
| Preparation alias probe | Confirmed the symlink and cross-environment write. |
|
||||
|
||||
The regression-suite failure was `test_wrapper_idle_grace_never_outlives_the_wall_clock`, `tests/test_issue14_delivery_ack.py:513`: the expected error status and return code were observed, but close delay was **28.7779 s**, exceeding the **28 s** allowance. The isolated rerun also failed at **28.5585 s**. The test body and wrapper are unchanged by this PR; the changed #14 code is its separate in-process publication helper. This timing failure remains unresolved in this environment and is not the evidence for R1/R2. I did not run the base suite or independently classify #28's 33 failures; these results do not justify a blanket environmental explanation.
|
||||
|
||||
No product files, source refs, production sessions, or monitor state were changed. Main remained on `1134d74`; the reviewed worktree remained on `46ea4cc`. The verdict concerns this exact tree, not later orchestrator release/fix commits.
|
||||
|
||||
[VERDICT: NOT PASS]
|
||||
@@ -0,0 +1,39 @@
|
||||
# PR #29 delta review — 2nd pass
|
||||
|
||||
Reviewer: planner-reviewer-codex-01. Job: f6b21a0d.
|
||||
|
||||
Reviewed the detached worktree `multi-agent-mux-wt/pr-29` at `80a5614`, comparing `46ea4cc..HEAD` (commits `7a94ba2`, `90a289d`, `08db1da`, and `80a5614`). The three findings from the first review are closed by the delta. I found no additional merge-blocking defect in the reviewed changes.
|
||||
|
||||
## R1/R2: hidden untracked submodule files
|
||||
|
||||
The removal guard now uses `git status --porcelain --untracked-files=all --ignore-submodules=none` for the worktree, and `lib_py.worktree.submodule_findings()` uses `git status --porcelain --untracked-files=all` inside every checked submodule. The identity runner's `_dirty_submodules()` uses the same explicit flag. This defeats `status.showUntrackedFiles=no` and remains independent of `submodule.*.ignore` and `diff.ignoreSubmodules`.
|
||||
|
||||
The source-tree snapshot still uses a temporary index and `git add -A`; that operation already sees untracked files independent of display settings, so no extra snapshot change is needed. The new regression cases cover hidden files before removal, files written while stopping, hidden files that appear during a review, and hidden files present before a review. The prior destructive probes were repeated against disposable fixtures: with the delta, removal returned exit 3 and preserved the untracked file; review identity refused before dispatch and produced no sidecar. R1 and R2 are therefore resolved.
|
||||
|
||||
## R3: worktree virtual environments
|
||||
|
||||
`worktree_prepare.sh` now refuses an existing `.venv` symlink, creates an independent venv with the control root's `.venv/bin/python`, installs the delegate-job requirements plus root `requirements.txt`/`requirements-dev.txt` when present, and reports `VENV=created`. A usable existing environment is idempotently retained as `VENV=kept`. When the control interpreter is absent it warns and reports `VENV=skipped`; it does not invent a link. If dependency installation fails, the half-created venv is removed and the worktree remains for correction and retry. The tests cover creation, the kept path, link refusal, missing interpreter, and failed-install cleanup.
|
||||
|
||||
The design document and create/stop skill documentation now describe the independent environment, network requirement, link refusal, and cleanup semantics. The managed `.gitignore` block and shared Git `info/exclude` still ignore `.venv` and `.pytest_cache`; the new environment is not tracked. I found the implementation and documentation consistent.
|
||||
|
||||
## Portability delta (#28)
|
||||
|
||||
The `bash -c` change in `test_workspace_scope.py` is appropriate because the test invokes the Bash `source` builtin. Help-output normalization collapses layout-only whitespace and the Python 3.9/3.14 option-group spelling while still comparing the actual normalized content. The deployment freshness markers skip only when the relevant `nats-docker` assets are absent. This checkout does not contain the optional `nats-docker/docker-compose.yaml`, so those asset-dependent cases skip here; `PRIVATE_SERVER.md` is present and its checks run. The test suite must still run those checks when a checkout has the assets. The process scanners now use POSIX `ps -eo pid=,ppid=,args=`, which is available on macOS and Linux and preserves the command text needed by the scanners. `MAM_ENV_FILE` is pinned to a deliberately absent per-test path, while tests that need broker configuration can override it.
|
||||
|
||||
## Wall-clock test change
|
||||
|
||||
The close-delay allowance changed from `timeout_s + 8` to `timeout_s + 15`. This is a test-only adjustment for observed suite load; it still rejects the original roughly 2× timeout failure mode (40 seconds against a 35-second limit in the test's 20-second scenario). The previously failing scenario now passes in an isolated rerun, and the remote acceptance test also passes. The wider allowance is a reasonable host-load calibration, though future runs should retain the measured close-delay evidence rather than relying on the assertion alone.
|
||||
|
||||
## Verification
|
||||
|
||||
All tests were run in the requested detached worktree with its `.venv/bin/python`, `PYTHONDONTWRITEBYTECODE=1`, and `pytest -p no:cacheprovider`.
|
||||
|
||||
- Delta-focused suites (`test_issue21_phase2_loop_a.py`, `test_issue21_loop_b.py`, `test_issue21_acceptance.py`, `test_issue21_worktree_prereqs.py`, `test_issue5_phase1_delegation.py`, `test_workspace_scope.py`, `test_deploy_freshness.py`, `test_version_consistency.py`): **209 passed** in 323.16 s. Asset-dependent freshness tests skip because this checkout has no `nats-docker` assets; this is the intended skip condition.
|
||||
- Isolated wall-clock close and remote acceptance rerun: **2 passed** in 49.79 s.
|
||||
- `bash -n` on all eight changed shell files: passed.
|
||||
- `git diff --check 1134d74...HEAD`: passed.
|
||||
- Direct snapshots before/after testing found no new entries in either checkout's real `.mam/jobs` or `.mam/delegate_job_logs`.
|
||||
|
||||
The known release prerequisite remains outside this delta: the orchestrator must perform the 4.7.0 version lockstep bump and release entry before merge. Main stayed at `1134d74`; the review worktree stayed at `80a5614`; no source, checkout, production registry, monitor, or session state was modified.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,87 @@
|
||||
# PR #29 Delta Review Report (Round 2)
|
||||
|
||||
- **Reviewer**: Antigravity (`herdr:reviewer-agy-01`)
|
||||
- **Job ID**: `d9275506`
|
||||
- **Target PR**: [PR #29](https://git.godopu.com/tmpl/multi-agent-mux/pulls/29) (`feat/issue-21-items-4-5-7` @ `08db1da`, previous review target `46ea4cc`, base `main` `1134d74` / v4.6.0)
|
||||
- **Review Target Worktree**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` (detached @ `08db1da`)
|
||||
- **Base Checkout**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux` (`main` @ `1134d74`, unmodified)
|
||||
- **Status**: Review Complete
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
This independent second-round review examines the 3 delta commits (`46ea4cc..08db1da`) in PR #29 responding to review feedback (specifically Codex R1, R2, R3, host portability fixes for Issue #28, and a wall-clock test timing adjustment):
|
||||
1. `7a94ba2`: Portable test adaptations for Linux/Python 3.14 (#28) and POSIX `ps -eo pid=,ppid=,args=` in process scanners.
|
||||
2. `90a289d`: Widened wall-clock close margin (`timeout_s + 15`) in `test_issue14_delivery_ack.py` to eliminate multi-core load timing jitter.
|
||||
3. `08db1da`: Codex R1–R3 resolutions:
|
||||
- Added `--untracked-files=all` across all three git status checks (`lib_py/worktree.py`, `worktree_remove.sh`, and `runner.py::_dirty_submodules`), preventing `status.showUntrackedFiles=no` from bypassing safety checks.
|
||||
- Replaced `.venv` symlinking in `worktree_prepare.sh` with dedicated virtualenv creation (`$WT/.venv`) via the control root interpreter (`$MAIN/.venv/bin/python`), including automatic `requirements*.txt` installation, link refusal (`exit 1`), and interpreter fallback (`VENV=skipped`).
|
||||
- Added comprehensive regressions (`test_w1`, `test_w1b`, `test_w6e[hidden]`, `test_w6e2`, `test_l17[untracked-hidden,during-hidden]`).
|
||||
|
||||
All 5 checklist verification items were evaluated in worktree `pr-29`. The delta commits resolve all feedback points without weakening existing contracts or regressing Round 1 functionality.
|
||||
|
||||
---
|
||||
|
||||
## 2. Checklist Item-by-Item Verification
|
||||
|
||||
### (1) Untracked-Safe Status Checks (Codex R1 & R2)
|
||||
- **Three Inspection Points**: `--untracked-files=all` has been added to all 3 critical cleanliness evaluation sites:
|
||||
1. `lib_py/worktree.py` line 167:
|
||||
`checks = (("dirty", "git status --porcelain --untracked-files=all"), ...)`
|
||||
2. `.agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh` line 91:
|
||||
`if ! dirty="$(git -C "$WT" status --porcelain --untracked-files=all --ignore-submodules=none 2>&1)"; then`
|
||||
3. `.agents/skills/lib_py/loop/runner.py` line 718 (`_dirty_submodules`):
|
||||
`r = _git(wt, "submodule", "foreach", "--recursive", "--quiet", "git status --porcelain --untracked-files=all")`
|
||||
- **Prevention of Data Loss & Stale Approvals**: Standard `git status --porcelain` respects `status.showUntrackedFiles=no`. Without `--untracked-files=all`, untracked files in submodules or worktrees with that setting configured were hidden from status checks, allowing `worktree_remove.sh` to execute a destructive forced removal over uncommitted work, and allowing the review loop to treat a dirty tree as clean. The explicit flag forces git to report all untracked files.
|
||||
- **Source Tree Snapshot Guard**: In `runner.py`, the source tree review snapshot is captured using `git -C <wt> add -A`. By git specification, `git add -A` captures all untracked, modified, and deleted files unconditionally, completely ignoring `status.showUntrackedFiles`. Hence, no additional flag is required for snapshot creation.
|
||||
- **Regression Tests**:
|
||||
- `test_w6e[hidden=True]` and `test_w6e2_untracked_submodule_file_hidden_by_config_blocks_the_forced_removal` in `tests/test_issue21_phase2_loop_a.py`.
|
||||
- `test_l17[untracked-hidden, during-hidden]` in `tests/test_issue21_loop_b.py`.
|
||||
All pass and confirm that config-hidden untracked files fail closed.
|
||||
|
||||
### (2) Dedicated Worktree Virtualenv (Codex R3)
|
||||
- **Idempotency**: `worktree_prepare.sh` checks `if [ -e "$WT/.venv" ]; then VENV_STATE="kept"`. Existing virtual environments are preserved without re-creation.
|
||||
- **Symlink Refusal**: `if [ -L "$WT/.venv" ]; then echo "ERROR: ... is a symbolic link..." >&2; exit 1; fi`. Rejects aliased symlinks to guarantee environment isolation.
|
||||
- **Interpreter Absent**: If `$MAIN/.venv/bin/python` is unavailable, it outputs `WARNING: no interpreter at '$MAIN/.venv/bin/python'; no venv created` and proceeds with `VENV_STATE="skipped"`.
|
||||
- **Installation Failure Handling**: If `pip install -q -r "$req"` fails on requirements files, it exits with code 1 and outputs `ERROR: ... the worktree was kept - remove it with worktree_remove.sh --path '$WT'`. The worktree is preserved for debugging rather than left broken or deleted unexpectedly.
|
||||
- **Documentation & Ignore Alignment**:
|
||||
- `docs/design/worktree-creator-model.md` and SKILL guides (`create`, `stop`) have been updated to describe dedicated venvs and interpreter requirements.
|
||||
- The `.gitignore` managed block (`/.venv`) and `info/exclude` (`/.venv`, `.pytest_cache/`) correctly ignore `$WT/.venv` as a directory.
|
||||
|
||||
### (3) Host Portability Fixes (Issue #28)
|
||||
- **`_normalize_help`**: In `tests/test_issue5_phase1_delegation.py`, replaces `optional arguments:` with `options:` and normalizes whitespace runs. This accommodates Python 3.10+ argparse formatting without weakening validation of flags, positional arguments, or descriptions.
|
||||
- **`test_deploy_freshness.py` Skips**: Uses `needs_docker_assets` / `needs_private_server_doc` decorators that skip only when external `nats-docker` or private server documentation assets are missing. On environments with assets present (including this macOS verification host), all 97 tests ran and passed.
|
||||
- **`conftest.py` `MAM_ENV_FILE` Isolation**: Anchors `MAM_ENV_FILE` to a non-existent sandbox path (`tmp_path / ".mam.env.absent"`) by default, preventing ambient `.mam.env` values from leaking into test runs. Tests requiring specific env files explicitly override it.
|
||||
- **Process Scanning (`ps -eo pid=,ppid=,args=`)**: POSIX-compliant process listing in `lib_py/agents/base.py` and `lib_py/delegation/heartbeat.py` replaces BSD-only `-ww` flags, ensuring identical, untruncated column parsing across macOS and Linux.
|
||||
|
||||
### (4) Wall-Clock Close Margin (`90a289d`)
|
||||
- **Test Integrity**: In `tests/test_issue14_delivery_ack.py`, `test_wrapper_idle_grace_never_outlives_the_wall_clock` verifies that heartbeats extend the idle window up to the wall-clock deadline (`timeout_s = 20`), without allowing idle grace to outlive the deadline.
|
||||
- **Margin Adjustment**: The assertion threshold was updated from `timeout_s + 8` to `timeout_s + 15` (35s). The original bug was counting sleeps, which ran for ~2x TIMEOUT (40s). A 35s ceiling easily catches a 40s overrun while preventing ~50ms timing jitter failures caused by heavy CPU load during full-suite execution with mock latency. Tested directly: passed in 31.35s.
|
||||
|
||||
### (5) Merge Readiness
|
||||
- `git diff 1134d74...HEAD --check` passes with **0** whitespace/formatting errors.
|
||||
- Version bump to 4.7.0 (MINOR) and `VERSIONS.md` update are confirmed as scheduled for the orchestrator release commit.
|
||||
- Zero functional, stability, or security regressions.
|
||||
|
||||
---
|
||||
|
||||
## 3. Test Execution Summary
|
||||
|
||||
Executed in worktree `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29`:
|
||||
| Test Suite | Result | Duration |
|
||||
| :--- | :---: | :---: |
|
||||
| `tests/test_version_consistency.py` | **3 passed** | 0.22s |
|
||||
| `tests/test_workspace_scope.py` | **2 passed** | 0.14s |
|
||||
| `tests/test_issue5_phase1_delegation.py` + `tests/test_deploy_freshness.py` | **97 passed** | 32.48s |
|
||||
| `tests/test_issue21_phase2_loop_a.py` | **28 passed** | 125.85s |
|
||||
| `tests/test_issue21_loop_b.py` | **34 passed** | 11.01s |
|
||||
| `tests/test_issue14_delivery_ack.py` (wall-clock test) | **1 passed** | 31.35s |
|
||||
|
||||
---
|
||||
|
||||
## 4. Conclusion
|
||||
|
||||
The 3 delta commits cleanly resolve Codex review items R1–R3, address host portability under Python 3.14/Linux (Issue #28), and stabilize the wall-clock test under multi-core load. All contracts remain fully preserved.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,122 @@
|
||||
# PR #29 Review Report: Worktree Operating Model Phase 2 (Items 4, 5, 7, Test Isolation #20, Deploy #27)
|
||||
|
||||
- **Reviewer**: Antigravity (`herdr:reviewer-agy-01`)
|
||||
- **Job ID**: `f27519ff`
|
||||
- **Target PR**: [PR #29](https://git.godopu.com/tmpl/multi-agent-mux/pulls/29) (`feat/issue-21-items-4-5-7` @ `46ea4cc`, base `main` `1134d74` / v4.6.0)
|
||||
- **Review Target Worktree**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` (detached @ `46ea4cc`)
|
||||
- **Base Repository**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux` (`main` @ `1134d74`, unmodified)
|
||||
- **Status**: Review Complete
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
PR #29 implements the Phase 2 components of the Worktree Creator Operating Model (Issue #21 items 4, 5, 7), comprehensive test suite isolation (#20), and installer `.gitignore` handling (#27) across 20 commits (+3,229 / -108 lines, 25 files changed).
|
||||
|
||||
An independent review was performed from the dedicated worktree `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` without modifying any files in either the worktree or the main working copy.
|
||||
|
||||
All 8 verification items have been thoroughly analyzed and verified:
|
||||
- **Worktree Helpers (`worktree_prepare.sh`, `worktree_remove.sh`)**: Rigorously safe against data loss (defenses R1–R6) with opt-in submodules, rollback on failed init, and guarded `--force` execution only after verifying that zero uncommitted changes, unpushed commits (including tags/detached HEAD), or stashes exist.
|
||||
- **Resume CWD Guard**: Safely enforces resuming in original cwd (exit 2) while maintaining 100% backward compatibility for single-workspace users, and persists control-plane bindings on already-running paths.
|
||||
- **Review Identity & Isolation**: Robust per-round snapshots `(base_sha, head_sha, tree_sha)`, detached review worktrees (`--review-isolation`), stale-round re-review on creator commit, pre-promotion re-check, and durable sidecar metadata (`.identity.json`). Default behavior without options remains byte-for-byte identical.
|
||||
- **Test State Isolation (#20)**: Zero test leakage into real `.mam` verified live on the control plane. Ambient bindings are dropped before test collection, test-specific tmp roots are injected via autouse fixture, and session-scoped leak guards detect anomalies.
|
||||
- **Test Suite Results**: 100% pass across all new and prerequisite test suites (132 tests passing deterministically). `git diff --check` passes with 0 errors.
|
||||
- **Known Limitation**: Version bump to 4.7.0 (MINOR) and `VERSIONS.md` update are properly scheduled for the orchestrator release commit.
|
||||
|
||||
---
|
||||
|
||||
## 2. Item-by-Item Verification
|
||||
|
||||
### (1) Worktree Helpers & Data Loss Defenses (Issue #21 Item 4)
|
||||
- **`worktree_prepare.sh`**:
|
||||
- **`.venv` Handling**: Creates a symlink `ln -s "$MAIN/.venv" "$WT/.venv"` (linking to the control root's virtual environment rather than duplicating packages). If an existing non-symlink `.venv` is present, it issues a warning and leaves it intact. Commit `fadd38c` (#27) updated the managed `.gitignore` rule from `/.venv/` to `/.venv`, ensuring git ignores the `.venv` symlink in worktrees across all forms (file, directory, or symlink).
|
||||
- **Shared Excludes**: Adds `/.venv` and `.pytest_cache/` directly into `$GIT_COMMON_DIR/info/exclude`.
|
||||
- **Opt-in Submodules**: Submodules are initialized only when `--submodules` is explicitly supplied. Without `--submodules`, no submodule checkout or `modules/` directory is created.
|
||||
- **Submodule Rollback (R3 & R4)**: If `git submodule update --init --recursive` fails, the helper rolls back any partially created submodule checkouts, removes `.git` pointer files, deletes newly created module gitdirs in `$gd/modules/`, and restores `.git/config` keys. Pre-existing submodule checkouts are preserved. If an uninitialized submodule path already contains files prior to init, it refuses upfront (`exit 3`) before modifying anything (R4).
|
||||
- **`worktree_remove.sh` & Data Loss Defenses (R1–R6)**:
|
||||
- Pre-flight checks run before any mutations:
|
||||
1. Linked worktree check & lock check (`is-locked`, exit 3 if locked).
|
||||
2. Running sessions check: refuses unless `--stop` is specified.
|
||||
3. Open jobs check: verifies no open jobs exist for sessions in the worktree.
|
||||
4. Clean working tree check: `git status --porcelain --ignore-submodules=none`.
|
||||
5. Submodule inspection: verifies `dirty`, `unpushed`, and `stashes` across all repositories under `modules/` (including deinitialized and nested submodules via `_module_gitdirs`).
|
||||
- **Post-Stop Re-Check (R1)**: After `--stop` halts sessions, open jobs are re-checked, and `judge_tree` is executed a second time immediately before removal. This prevents data loss if a session being stopped wrote files, committed, or created stashes during termination.
|
||||
- **Unpushed Commits (R6)**: Uses `UNPUSHED_REVS = ("HEAD", "--exclude=refs/stash", "--all", "--not", "--remotes")`. This inspects commits reachable from HEAD, detached HEAD, local branches, and tags. Commits preserved only by local tags in submodules are protected.
|
||||
- **Single `--force` Execution**: Git by design refuses plain `git worktree remove` on any worktree containing a `modules/` directory. `worktree_remove.sh` performs a single `git worktree remove --force` *only* after confirming R1–R6 pass completely. If any check fails, it exits with code 3 and leaves the entire worktree untouched. Manual, unguarded `git worktree remove --force` is explicitly warned against.
|
||||
|
||||
### (2) Resume Contract & Binding Persistence (`2679ddf`)
|
||||
- **Original CWD Validation**:
|
||||
- `resume_session.sh` verifies `mam_abs_workspace "$WORKSPACE"` matches `mam_abs_workspace "$ROW_CWD"`. If called with an altered workspace path, it terminates with `exit 2` and a clear error message (`Pass --workspace '$ROW_CWD' to resume it in its original workspace`).
|
||||
- If the directory recorded in `pane.cwd` no longer exists (e.g. after worktree removal), it exits with code 2 explaining that recreating the worktree at that path makes the session resumable again.
|
||||
- **Backward Compatibility**: For standard single-workspace users, the workspace matches `ROW_CWD`, so resume executes normally without disruption.
|
||||
- **Already-Running Path Binding Persistence**: When resuming an already-running session, `CONTROL_PLANE_JSON="$CP_JSON" update_yaml_resumed.sh` is invoked, persisting the control-plane binding into `agent-sessions.yaml`.
|
||||
- **Loud Binding Failure**: If control-plane binding computation fails:
|
||||
- For a worktree (workspace != control root), it fails closed with `exit 2` ("refusing to resume a session that would use the wrong control plane").
|
||||
- For the control root itself, it issues a WARNING and continues without `control_plane`, preserving resiliency.
|
||||
|
||||
### (3) Review Identity & Isolation (Issue #21 Item 5)
|
||||
- **Snapshot Identity**:
|
||||
- `lib_py/loop/runner.py` records `(loop_id, base_sha, head_sha, tree_sha)` for each review round.
|
||||
- Verification targets are embedded directly into reviewer prompts.
|
||||
- **`--review-isolation worktree`**:
|
||||
- Creates a temporary detached worktree at the snapshot's review commit (with uncommitted/untracked changes pinned) so reviewers run tests without contaminating the creator's working tree.
|
||||
- The review worktree is cleanly closed via `_close_review_tree` at the conclusion of each round, even upon failure (`_release_review_trees`).
|
||||
- **Stale Round Detection**: If the creator commits or modifies files during an active review round, the round is identified as stale, approvals are discarded, and a fresh review round is triggered.
|
||||
- **Pre-Promotion Re-Check**: Before promoting passed reviews to durable artifacts (`.mam/reviews/...`), `_identity_now()` is verified against the approved round's `(head_sha, tree_sha)`. If the tree changed after review approval, promotion fails closed with `exit 1`.
|
||||
- **Durable Identity Evidence**: Promoted reports are accompanied by a JSON sidecar `report-{jid}.identity.json` detailing loop ID, SHAs, reviewer identity, review commit, and verification timestamp.
|
||||
- **Remote Reviewers (`--remote-review`)**: Since remote reviewers cannot access local worktree paths, identity mode refuses remote reviewers by default, or allows them strictly as diff-only via `--remote-review diff-only`.
|
||||
- **Dirty Submodules**: Submodule dirtiness is detected and causes reviews to be refused or marked stale.
|
||||
- **Zero Drift Without Options**: When run without isolation/identity options, `identity` evaluates to `False`, no review worktree is created, and the prompt text matches previous releases byte-for-byte.
|
||||
|
||||
### (4) Test State Isolation (#20 Commits `3433ffd` ~ `46ea4cc`)
|
||||
- **Ambient Binding Drop**: `tests/conftest.py`'s `pytest_configure` records and unsets ambient `AGENT_SESSIONS_YAML`, `MAM_JOBS_DIR`, `DELEGATE_JOB_LOGS_DIR`, and foreign `WORKSPACE_ROOT`.
|
||||
- **Per-Test TMP Roots**: The autouse fixture `_isolate_mam_state_dirs` redirects `MAM_JOBS_DIR` and `DELEGATE_JOB_LOGS_DIR` to `tmp_path / ".mam-test-state"` for every individual test.
|
||||
- **Resolution Tests Opt-Out**: Tests in `tests/test_issue15_registry_isolation.py` that test default fallback resolution explicitly opt out by calling `monkeypatch.delenv`.
|
||||
- **Control Plane Leak Guard**: `mam_state_leak_guard` snapshots all real `.mam/jobs` and `.mam/delegate_job_logs` directories before test session execution and asserts at session finish that zero unexpected records were leaked.
|
||||
- **Live Verification**: Inspection of the host control plane `.mam/jobs` and `.mam/delegate_job_logs` confirmed **0** leaked test records. Only active delegation job `f27519ff` is present.
|
||||
|
||||
### (5) Acceptance5 Stabilization Commits (`7bc3e1a`, `371099a`, `e6d2168`)
|
||||
- **Session & Secret Randomization**: Uses a unique randomized session name (`sim-worker-{tag}`) and secret (`issue18-shared-secret-{tag}`) per run. This eliminates the collision failure mode where public MQTT broker retained messages from prior test runs were served to new tests.
|
||||
- **Widened Idle Window & Terminal Event Recognition**: Expands missed heartbeat tolerance (10 misses x 2s) and recognizes delivery confirmed by retained terminal events (`advanced to 'completed'`).
|
||||
- **Targeted Skip on Broker Non-Delivery**: Skips only when provable broker non-delivery occurs (e.g. no CONNACK from broker, or broker packet drop).
|
||||
- **Contract Maintained**: Protocol assertions (`assert proc.returncode == 0`, `status == "completed"`, `transport == "remote"`) remain fully intact; the contract is not weakened.
|
||||
|
||||
### (6) Acceptance Cases A1–A8 & Test Suite Performance
|
||||
- **Hermeticity & Determinism**:
|
||||
- All 9 acceptance tests in `tests/test_issue21_acceptance.py` run against `mock_herdr` and `mam_sandbox`/`tmp_path`.
|
||||
- Zero requirement for a live `herdr` daemon or external MQTT network broker.
|
||||
- **Test Execution Metrics (executed in `pr-29`)**:
|
||||
- `tests/test_version_consistency.py`: **3 passed** in 0.22s
|
||||
- `tests/test_issue20_state_isolation.py`: **7 passed, 1 skipped** in 0.34s
|
||||
- `tests/test_issue21_acceptance.py`: **9 passed** in 37.23s
|
||||
- `tests/test_issue21_loop_b.py`: **32 passed** in 10.01s
|
||||
- `tests/test_issue21_phase2_loop_a.py`: **25 passed** in 122.37s
|
||||
- `tests/test_issue15_registry_isolation.py`: **7 passed** in 27.11s
|
||||
- `tests/test_issue21_worktree_prereqs.py`: **35 passed** in 112.28s
|
||||
- `tests/test_issue6_turn_handshake.py`: **14 passed** in 69.39s
|
||||
- **Total**: **132 passed, 1 skipped**, 100% pass rate.
|
||||
|
||||
### (7) Documentation Integrity & Non-Blocking Recommendations
|
||||
- **Documentation Alignment**:
|
||||
- `docs/design/worktree-creator-model.md` accurately updates the implementation status matrix: items 1, 2, 3 (v4.6.0), items 4, 6 (Loop A), items 5, 7 (Loop B), with item 8 appropriately held for future work.
|
||||
- Skill docs (`multi-agent-mux-create`, `multi-agent-mux-stop`, `multi-agent-mux-resume`, `multi-agent-mux-loop`) document helper script invocations, `--stop`, `--review-isolation`, and `--remote-review`.
|
||||
- Compatibility matrix clearly defines runtime behavior across create, resume, stop, status, reconcile, delegate-job, and loop.
|
||||
- **4 Non-Blocking Recommendations**:
|
||||
1. A4 untracked comparison (pre-applying info/exclude).
|
||||
2. info/exclude pre-reflection.
|
||||
3. Prune global guidance when worktree directory is deleted externally.
|
||||
4. Deduplication of submodule refusal messages.
|
||||
All 4 are sound, low-severity improvements properly deferred to Issue #30.
|
||||
|
||||
### (8) Merge Readiness Assessment
|
||||
- `git diff 1134d74...HEAD --check` completed with **0** errors (no trailing whitespace, no merge artifacts).
|
||||
- Known limitation: Version bump to 4.7.0 (MINOR) and entry in `VERSIONS.md` are reserved for the orchestrator release commit.
|
||||
- No functional regressions, security hazards, or data loss vulnerabilities found.
|
||||
|
||||
---
|
||||
|
||||
## 3. Final Conclusion
|
||||
|
||||
PR #29 completes Phase 2 of the Worktree Creator Operating Model with exemplary rigor in data safety, deterministic test isolation, and contract preservation.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,62 @@
|
||||
# 🔍 Review Report — Job 19d56cde (PR #29 델타 `46ea4cc...08db1da`)
|
||||
|
||||
- **Reviewer**: `reviewer-grok-01` · 2026-09-14
|
||||
- **트리**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` @ `08db1da`
|
||||
- **델타**: `git log`/`git diff` **`46ea4cc..HEAD`** (3커밋). 1회차 grok PASS @ 46ea4cc 를 뒤집지 않음.
|
||||
- **본 체크아웃**: `main` `1134d74` 유지. 워크트리 깨끗. 헬퍼를 실제 트리에 실행하지 않음.
|
||||
|
||||
`git diff --check 46ea4cc...HEAD` 클린.
|
||||
|
||||
## (1) Codex R1/R2 — 닫힘
|
||||
|
||||
`--untracked-files=all` 이 세 검사에 모두 있음:
|
||||
|
||||
| 위치 | 명령 |
|
||||
|---|---|
|
||||
| `worktree_remove.sh` `judge_tree` | `status --porcelain --untracked-files=all --ignore-submodules=none` |
|
||||
| `lib_py/worktree.py` submodule dirty | `status --porcelain --untracked-files=all` |
|
||||
| `runner.py` `_dirty_submodules` | 동일 + `--ignore-submodules=none` |
|
||||
|
||||
임시 샌드박스: `status.showUntrackedFiles=no` 이면 플래그 없이 porcelain 이 비고, `--untracked-files=all` 이면 `?? secret.bin`. **`git add -A` 는 그 설정과 무관하게 untracked 를 스테이징**하므로 리뷰 스냅샷(`runner.py:698` `("add", "-A")`)은 추가 조치 불필요.
|
||||
|
||||
회귀: `test_w6e[hidden]`, `test_w6e2`, `test_l17[untracked-hidden,during-hidden]`. 플래그 없이는 숨은 untracked 가 force 제거·스냅샷을 통과했을 테스트.
|
||||
|
||||
## (2) Codex R3 — 닫힘
|
||||
|
||||
`worktree_prepare.sh:111-136`: 링크면 exit 1(무변경), 사용 가능한 `bin/python` 이면 `VENV=kept`, 컨트롤 루트 인터프리터로 `python -m venv` + `requirements*.txt` 설치 실패 시 **워크트리 유지·exit 1**, 인터프리터 없으면 경고·`VENV=skipped`. `test_w1` / `test_w1b` 가 created/kept/skip/link 거부. SKILL.md·design doc 일치. `.gitignore` 관리 블록·`info/exclude` `/.venv` 와 정합(자체 venv 는 untracked 로 안 보임).
|
||||
|
||||
## (3) #28 이식성 — 의도 유지
|
||||
|
||||
- `test_workspace_scope.py`: `bash -c` (`source` 는 dash 에 없음).
|
||||
- argparse help: `_normalize_help` 가 그룹명·공백만 정규화. 옵션 토큰 유실은 여전히 실패.
|
||||
- `conftest` autouse `MAM_ENV_FILE` → 없는 파일. 실 `.mam.env` 를 쓰려면 테스트가 직접 set. T1-4 가 공개 브로커를 타던 경로를 막음.
|
||||
- deploy d11–d19 / d22–d30 skip 은 자산 부재 시만. **이 워크트리에는 `nats-docker/PRIVATE_SERVER.md` 와 `nats-docker/docker/docker-compose.yaml` 이 있어 skip 없이 실행됨** (아래 스위트 0 skipped).
|
||||
- `ps -eo pid=,ppid=,args=`: 이 macOS 에서 BSD `-axo … command=` 와 같은 3열 출력. Linux POSIX 와 맞춤.
|
||||
|
||||
## (4) 90a289d 여유
|
||||
|
||||
`timeout_s + 8` → `+ 15`. 핀하는 결함은 ~2×TIMEOUT(~40s, timeout=20). 하한 `elapsed >= timeout_s - 1` 유지. 여유 15s 는 2× 와 겹치지 않음. 계약 검증을 무효화하지 않음.
|
||||
|
||||
## (5) 락스텝
|
||||
|
||||
4.7.0 bump 는 오케스트레이터 예정. 병합 차단 아님.
|
||||
|
||||
## 테스트 (워크트리)
|
||||
|
||||
```
|
||||
PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q \
|
||||
tests/test_issue21_phase2_loop_a.py tests/test_issue21_loop_b.py \
|
||||
tests/test_issue21_worktree_prereqs.py tests/test_issue21_acceptance.py \
|
||||
tests/test_workspace_scope.py tests/test_issue5_phase1_delegation.py \
|
||||
tests/test_deploy_freshness.py tests/test_version_consistency.py
|
||||
```
|
||||
|
||||
(`test_issue28_*.py` 없음, 건너뜀)
|
||||
|
||||
**208 passed in 314.15s**, skip 0.
|
||||
|
||||
## 종합
|
||||
|
||||
1회차 PASS 유지. Codex R1–R3 이 코드·회귀·문서와 맞다. 설계 재작업 불필요.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,63 @@
|
||||
# 🔍 Review Report — Job 5f1486ee (PR #29 @ `46ea4cc`)
|
||||
|
||||
- **Reviewer**: `reviewer-grok-01` · 2026-09-14
|
||||
- **트리**: 워크트리 `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` detached `46ea4cc`
|
||||
- **베이스**: main `1134d74` (본 체크아웃은 그대로 `main`)
|
||||
- **범위**: `git log`/`git diff` **`1134d74...HEAD`** (워크트리)
|
||||
- **안전**: 워크트리·본 체크아웃 미수정. `worktree_remove.sh` 미실행(샌드박스 테스트만). 실제 `.mam` 미조작.
|
||||
|
||||
`git diff --check 1134d74...HEAD` 클린. 20 커밋.
|
||||
|
||||
## (1) 워크트리 헬퍼 (항목 4)
|
||||
|
||||
**prepare `.venv`**: 실제 venv 가 아니라 **컨트롤 루트 `.venv` 의 심링크** (`worktree_prepare.sh:111-117`). 설계 문서의 `ln -s` 와 같음. 인터프리터는 컨트롤 루트 경로. 공유 `info/exclude` 에 `/.venv`, `.pytest_cache/` (`:120-129`) — 본 체크아웃 exclude 에도 들어가며, 비차단 권고(선반영)와 같음.
|
||||
|
||||
**서브모듈 opt-in** (`--submodules`). 실패 시 이번 시도만 deinit·modules 삭제·config 복원 (`:174-198`). 비어 있지 않은 미init 경로는 변경 전 거부 (`:163-172`).
|
||||
|
||||
**remove R1–R6**: 검사 전부 선행, 실패 시 무변경(exit 3). `--stop` 후 재검사(R1). 서브모듈 stash 거부. `git worktree remove --force` 는 submodule trace 가 있고 judge 가 통과한 뒤에만 (`:158-163`). 미커밋/미push 는 `UNPUSHED_REVS` (`worktree.py:27-30`: HEAD + `--all` − stash − remotes, 태그 포함 R6) 로 거부. **강제 제거가 보존된 커밋을 삼키는 경로는 검사 통과를 전제로 막혀 있다.** 잠긴 워크트리는 force 도 거부.
|
||||
|
||||
## (2) resume (2679ddf)
|
||||
|
||||
원래 `pane.cwd` 와 `--workspace` 불일치·경로 없음 → exit 2 (`resume_session.sh:61-69`). **단일 워크스페이스** 사용자는 cwd=체크아웃이라 기존과 같다. `ROW_CWD` 없는 레거시 행은 검사를 건너뜀. 컨트롤 평면 바인딩은 행 값이 이김; 계산 실패는 워크트리면 exit 2, 컨트롤 루트면 경고 (`:92-98`). 이미 실행 중 경로에도 영속화(주석 A13). 기존 resume 을 깨지 않음.
|
||||
|
||||
## (3) 리뷰 identity (항목 5)
|
||||
|
||||
`(loop_id, base_sha, head_sha)` 기록·stale (`runner.py:831-840`). `--review-isolation` 기본 `worktree` 이지만 **`--worktree`/`--base` 없을 때 `_worktree_context` 는 빈 문자열** (`:564-570`) — 프롬프트 바이트 동일. 옵션만 주면 경고 후 무시 (`:266-267`). detached 리뷰 워크트리 add/remove (`:775-786`). dirty 서브모듈 거부. `--remote-review` 기본 refuse. 승격 전 재확인 경로 존재. 기본(옵션 없음) 회귀 없음.
|
||||
|
||||
## (4) #20 격리
|
||||
|
||||
`pytest_configure` 가 ambient `MAM_JOBS_DIR`/`DELEGATE_JOB_LOGS_DIR`/`AGENT_SESSIONS_YAML` 을 수집 전에 pop (`conftest.py:139-147`). 매 테스트 autouse 가 tmp `.mam-test-state` 로 보냄 (`:161-172`). issue15 해석 테스트는 자체 `delenv` 로 옵트아웃. 세션 가드는 기록된 제어 평면 (`mam_state_leak_guard`).
|
||||
|
||||
**직접 확인**: 테스트 전후 본 체크아웃 `.mam/jobs/*.json` **53**, `delegate_job_logs` **547** — 증가 없음. 워크트리 `git status` 깨끗.
|
||||
|
||||
## (5) acceptance5
|
||||
|
||||
세션·secret 런마다 고유 (`test_issue18_liveness_contract.py:1194-1201`). skip 은 러너가 started 를 찍고 구독자가 그 job 을 전혀 못 받은 경우만 (`:1164-1186`). started `--heartbeat` 계약은 유지. 약화 없음.
|
||||
|
||||
## (6) A1–A8
|
||||
|
||||
`test_issue21_acceptance.py`: mock herdr / mam_sandbox. 실제 herdr pane 은 stage C. 결정적. 지정 스위트에 loop_b 전체는 없음(작성 측 별도).
|
||||
|
||||
## (7)–(8) 문서·버전
|
||||
|
||||
design doc·SKILL 정합. 비차단 4건(exclude 공유, prune, 서브모듈 거부 중복 출력 등)은 병합 비차단. 락스텝 4.7.0 은 오케스트레이터 예정 — **병합 차단 아님**. 브랜치는 4.6.0.
|
||||
|
||||
## 테스트
|
||||
|
||||
워크트리:
|
||||
|
||||
```
|
||||
PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q \
|
||||
tests/test_issue21_acceptance.py tests/test_issue21_phase2_loop_a.py \
|
||||
tests/test_issue21_worktree_prereqs.py tests/test_issue20_state_isolation.py \
|
||||
tests/test_issue15_registry_isolation.py tests/test_issue6_turn_handshake.py \
|
||||
tests/test_version_consistency.py
|
||||
```
|
||||
|
||||
**100 passed, 1 skipped in 359.97s.** 본 체크아웃 `main` `1134d74` 유지.
|
||||
|
||||
## 종합
|
||||
|
||||
항목 4·5·7·#20·#27 이 코드·테스트와 맞다. `git worktree remove --force` 는 검사 통과 후에만. 설계 재작업 불필요.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -29,7 +29,7 @@ export WORKSPACE_ROOT
|
||||
# Framework semantic version. Single runtime source of truth; kept in lockstep
|
||||
# with VERSIONS.md and the 8 SKILL.md frontmatters by tests/test_version_consistency.py.
|
||||
# NOTE: unlike other MAM_* variables this one is intentionally NOT env-overridable.
|
||||
MAM_VERSION="4.6.0"
|
||||
MAM_VERSION="4.7.0"
|
||||
export MAM_VERSION
|
||||
AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}"
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import sys
|
||||
from typing import List, Sequence
|
||||
|
||||
# Framework version locked with framework constants
|
||||
FRAMEWORK_VERSION = "4.6.0"
|
||||
FRAMEWORK_VERSION = "4.7.0"
|
||||
|
||||
CORE_MODULES: Sequence[str] = (
|
||||
# Standard library modules
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-create
|
||||
description: "Create a new agent session (claude, antigravity/agy) in a dedicated herdr session for context-preserving long-running work. Always creates a herdr session — never backgrounds with nohup/disown. Writes the new session to .mam/agent-sessions.yaml. Use when you want to start a fresh agent (no prior UUID) for a new project workspace."
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-delegate-job
|
||||
description: "Delegate a unit of work to any autonomous agent (claude-code, hermes, agy, grok-build, codex, or a human) and observe it asynchronously over an MQTT event channel. Supported roles include orchestrator, worker, and reviewer."
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos, windows]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-loop
|
||||
description: "Run an autonomous planning-execution-review loop using multiple agents (Planner, Creator, Reviewers) in the workspace. Automatically orchestrates plan discussion, code changes, and peer reviews until a unanimous PASS is achieved or the maximum iteration limit is reached."
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-monitor
|
||||
description: "Run a long-lived reconciler that watches .mam/agent-sessions.yaml against the actual herdr/agent runtime state and reconciles them. Use when you want live visibility into which agent sessions are running, which are dead, which have stale YAML entries, and which have new session ids that haven't been recorded yet. Runs as a persistent loop (`reconcile.sh --subscribe`) that keeps going until it times out, idles out, or is interrupted."
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-orc-onboard
|
||||
description: "Register current or specified orchestrator session UUID into agent-sessions.yaml orchestrator_uuids list to prevent sub-agent discovery capture."
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-resume
|
||||
description: "Resume an existing agent (claude, antigravity/agy) conversation by UUID into a herdr session. Reads .mam/agent-sessions.yaml for the saved session/conversation id, spawns (or reuses) a herdr session of the matching name, and runs `claude -r <id>` or `agy --conversation <id>` inside. Use when you want to reattach to a previous session's context, or revive a session whose herdr died but the agent's conversation is still on disk."
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-status
|
||||
description: "Read-only instant snapshot of all agent herdr sessions — name, YAML status, herdr alive, pane cmd/cwd, resume UUID on disk, and any drift. No mutation. Reuses reconcile.sh --dry-run for the diff logic. Use when you want to know 'what's running RIGHT NOW' without spinning up the monitor loop."
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: multi-agent-mux-stop
|
||||
description: "Stop an agent herdr session (claude, antigravity/agy) and update .mam/agent-sessions.yaml. Default stops gracefully and marks status=stopped with conversation preserved for resume. Does NOT delete on-disk conversation artifacts (jsonl/db) — those are preserved unless --purge-conversation is passed. Use when ending a work session, switching to a different one, or cleaning up before a fresh start."
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
author: godopu
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
본 문서는 `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.
|
||||
|
||||
[](VERSIONS.md)
|
||||
[](VERSIONS.md)
|
||||
|
||||
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
|
||||
|
||||
Applies to: MAM v4.6.0
|
||||
Applies to: MAM v4.7.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.
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
Herdr와 MQTT 브로커를 기반으로 구축된 고신뢰성 **다중 에이전트 오케스트레이션 및 메시징 백플레인** 프레임워크입니다. `claude`, `agy`, `hermes`, `grok`, `opencode`, `codex` 등 다중 LLM 백엔드 에이전트 전반에 걸쳐 장시간 수행되는 작업(코드 생성, 리팩토링, 보안 검토 등)을 조정, 격리 및 감사할 수 있도록 설계되었습니다.
|
||||
|
||||
[](VERSIONS.md)
|
||||
[](VERSIONS.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`).
|
||||
|
||||
[](VERSIONS.md)
|
||||
[](VERSIONS.md)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+31
-11
@@ -6,8 +6,8 @@
|
||||
|
||||
## 📌 현재 버전 개요 (Current Release)
|
||||
|
||||
- **프레임워크 버전**: `v4.6.0`
|
||||
- **최신 릴리스 일시**: 2026-09-14 (KST)
|
||||
- **프레임워크 버전**: `v4.7.0`
|
||||
- **최신 릴리스 일시**: 2026-09-20 (KST)
|
||||
- **기준 브랜치**: `main`
|
||||
- **핵심 아키텍처**:
|
||||
- **Terminal 상태 단방향 불변성 (`engine.py`, Issue #6 Phase 1)**: `completed`, `error`, `cancelled` 터미널 상태 진입 후 후속 이벤트나 업데이트에 의한 덮어쓰기를 엄격히 차단(Terminal Immutability). `registry.py close` 서브커맨드 및 fail-closed 조기 종료 도입.
|
||||
@@ -15,29 +15,49 @@
|
||||
- **Loop 모드 `attempt_id` 추적 및 안전 전이 (Issue #6 Phase 3)**: 워커/리뷰어 전환 시 동일 회차 내 재전이 충돌을 방지하는 `attempt_id` 규약 적용 및 4단계 fail-closed `finalize_unfinished` 보장.
|
||||
- **결정론적 종료 코드 계약 (Issue #6 Phase 4 & R1)**: `0` (Success), `1` (Failure / Inject error), `2` (Subscriber timeout; 단, 에이전트가 Herdr에서 working 중이면 벽시계 --timeout까지 디스크 재관측 후 fail-close), `3` (Broker unavailable), `5` (Precondition failed / session_busy) 표준화. (send_keys_safe 헬퍼의 배달 실패/busy 타임아웃 rc=4는 래퍼 차원에서 rc=1 실패로 수용).
|
||||
- **프로토콜 기반 생존·전달 계약 (Issue #18)**: heartbeat 의무(N, 자동 타이머) · 무응답 판정(K×N) · 전달 확인(T 내 `started`) · retained `<prefix>/state` · 원격 전송(`remote:<name>`). TUI 스크래핑은 주입·다이얼로그·진단에만 남는다.
|
||||
- **워크트리 운영 모델 2단계 (Issue #21 항목 4·5·7)**: 워크트리 준비·해체 헬퍼(변경 전 판정, untracked 포함, 검증된 단일 `--force`), 리뷰 identity(라운드별 스냅샷·stale 재리뷰·승격 직전 재확인·`identity.json`), 수용 사례 A1–A8.
|
||||
- **4-Way 버전 락스텝**: `lib.sh`(`MAM_VERSION`) ↔ `lib_py/loop/eager.py`(`FRAMEWORK_VERSION`) ↔ `VERSIONS.md` ↔ 8개 `SKILL.md`.
|
||||
|
||||
---
|
||||
|
||||
## 🧭 스킬 패키지 버전 매트릭스 (Skills Version Matrix)
|
||||
|
||||
모든 8개 스킬은 YAML frontmatter 메타데이터(`author`, `version`, `platforms`, `environments`) 표준화를 통해 `v4.6.0`로 동기화되어 배포됩니다.
|
||||
모든 8개 스킬은 YAML frontmatter 메타데이터(`author`, `version`, `platforms`, `environments`) 표준화를 통해 `v4.7.0`로 동기화되어 배포됩니다.
|
||||
|
||||
| 스킬명 | 버전 | 역할 및 주요 책임 | 상태 |
|
||||
| :--- | :---: | :--- | :---: |
|
||||
| **`multi-agent-mux-create`** | `4.6.0` | 에이전트 세션 신규 생성 및 Herdr 컨테이너 격리 스폰 | ✅ 배포 |
|
||||
| **`multi-agent-mux-stop`** | `4.6.0` | 대화 UUID 원자적 캡처 및 세션 안전 종료 (Graceful Stop) | ✅ 배포 |
|
||||
| **`multi-agent-mux-resume`** | `4.6.0` | 온디스크 대화 컨텍스트 기반 Tier-1 초고속 세션 복원 | ✅ 배포 |
|
||||
| **`multi-agent-mux-status`** | `4.6.0` | 실시간 Herdr 세션 및 레지스트리 드리프트 스냅샷 조회 | ✅ 배포 |
|
||||
| **`multi-agent-mux-monitor`** | `4.6.0` | YAML ↔ 런타임 상태 간 자율 조정자 (Reconciler Loop) | ✅ 배포 |
|
||||
| **`multi-agent-mux-delegate-job`** | `4.6.0` | MQTT 이벤트 채널 기반 비동기 단위 작업 위임 | ✅ 배포 |
|
||||
| **`multi-agent-mux-loop`** | `4.6.0` | Planner-Creator-Reviewer 3자 자율 계획·실행·피어리뷰 루프 | ✅ 배포 |
|
||||
| **`multi-agent-mux-orc-onboard`** | `4.6.0` | 오케스트레이터 UUID 격리 등록 및 서브 세션 오염 방지 | ✅ 배포 |
|
||||
| **`multi-agent-mux-create`** | `4.7.0` | 에이전트 세션 신규 생성 및 Herdr 컨테이너 격리 스폰 | ✅ 배포 |
|
||||
| **`multi-agent-mux-stop`** | `4.7.0` | 대화 UUID 원자적 캡처 및 세션 안전 종료 (Graceful Stop) | ✅ 배포 |
|
||||
| **`multi-agent-mux-resume`** | `4.7.0` | 온디스크 대화 컨텍스트 기반 Tier-1 초고속 세션 복원 | ✅ 배포 |
|
||||
| **`multi-agent-mux-status`** | `4.7.0` | 실시간 Herdr 세션 및 레지스트리 드리프트 스냅샷 조회 | ✅ 배포 |
|
||||
| **`multi-agent-mux-monitor`** | `4.7.0` | YAML ↔ 런타임 상태 간 자율 조정자 (Reconciler Loop) | ✅ 배포 |
|
||||
| **`multi-agent-mux-delegate-job`** | `4.7.0` | MQTT 이벤트 채널 기반 비동기 단위 작업 위임 | ✅ 배포 |
|
||||
| **`multi-agent-mux-loop`** | `4.7.0` | Planner-Creator-Reviewer 3자 자율 계획·실행·피어리뷰 루프 | ✅ 배포 |
|
||||
| **`multi-agent-mux-orc-onboard`** | `4.7.0` | 오케스트레이터 UUID 격리 등록 및 서브 세션 오염 방지 | ✅ 배포 |
|
||||
|
||||
---
|
||||
|
||||
## 📋 버전별 상세 변경 내역 (Changelog)
|
||||
|
||||
### 🚀 `v4.7.0` — Issue #21 워크트리 운영 모델 2단계: 워크트리 헬퍼, 리뷰 identity, 수용 사례 A1–A8, #20 테스트 격리, #27, #28 호스트 이식성 (2026-09-20, PR #29)
|
||||
|
||||
> **마이너 마일스톤 (MINOR Release)**: 워크트리 운영 모델(#21, `docs/design/worktree-creator-model.md`)의 작업 항목 4·5·7 을 구현했다. 이슈별 워크트리를 만들고 지우는 헬퍼 2개, 리뷰 라운드를 `(loop_id, base, head, tree)` 에 묶는 리뷰 identity 와 루프 옵션 2개(`--review-isolation`, `--remote-review`), 수용 사례 8건이 추가되어 MINOR 로 올린다. 항목 8(다중 루프·리뷰어 스케줄러)은 보류로 남아 #21 은 열어 둔다. 함께 #20(테스트가 실제 `.mam` 을 오염), #27(`.gitignore` 관리 블록의 `.venv`), #28(Linux/py3.14 호스트의 테스트 실패 33건)을 닫는다.
|
||||
|
||||
#### 🌟 주요 개선 사항
|
||||
1. **워크트리 준비·해체 헬퍼 (항목 4, `multi-agent-mux-create/scripts/worktree_prepare.sh`, `multi-agent-mux-stop/scripts/worktree_remove.sh`)**: prepare 는 제어 루트 옆에 워크트리를 멱등하게 만들고(안쪽 경로는 거부), 워크트리 **자체의** `.venv` 를 제어 루트의 `.venv/bin/python` 으로 만들어 `requirements*.txt` 를 설치하며(심링크는 만들지 않고 남아 있으면 exit 1 — 링크가 `git add -A` 로 커밋된 사고의 재발 방지), `/.venv`·`.pytest_cache/` 를 공유 `info/exclude` 에 넣고, 서브모듈은 `--submodules` 로만 초기화한다(실패한 시도는 사용 가능한 체크아웃으로 롤백). `--creator-agent` 는 제어 루트 바인딩과 `MAM_NO_SUBSCRIBER_DAEMON=1` 로 creator 세션을 만들고 `BASE=<sha>` 를 출력한다. remove 는 잠금·실행 중 세션(`--stop`)·열린 잡·수정(untracked 포함)·서브모듈(미push 커밋: HEAD 와 tag·notes 를 포함한 모든 로컬 ref, stash, deinit 된 저장소, 부분 저장소) 을 **아무것도 바꾸기 전에** 판정하고, `--stop` 뒤 제거 직전에 한 번 더 판정한 뒤, 서브모듈 상태가 있는 워크트리만 검증된 단일 `--force` 로 지운다.
|
||||
2. **resume 계약 (항목 4, `multi-agent-mux-resume`)**: 세션은 기록된 원래 cwd 에서만 재개한다(다른 경로는 exit 2, cwd 없는 기존 행은 호환). 이미 실행 중 경로에서도 제어 평면 바인딩을 영속화하고, 워크트리 바인딩 계산 실패는 삼키지 않고 exit 2 로 알린다. create 는 즉시 reconcile·watchdog 을 제어 루트의 코드로 띄운다.
|
||||
3. **리뷰 identity (항목 5, `lib_py/loop/runner.py`, `run_loop.sh --review-isolation worktree|env`, `--remote-review refuse|diff-only`)**: `--worktree`/`--base` 에서 매 리뷰 라운드 전에 소스의 미커밋·untracked 상태까지 담은 ref 없는 스냅샷 커밋을 만들고, 리뷰어의 diff 도 `base..snapshot` 에서 만들며, 기본은 스냅샷의 detached 리뷰 워크트리에서 검증한다. 라운드가 끝난 뒤 소스가 바뀌었으면 PASS 를 버리고 다시 리뷰하고(마지막 반복이면 exit 1), 승격 직전에도 재확인하며, 리포트 옆에 `report-<jid>.identity.json`(loop_id·base·head·tree·스냅샷·리뷰어·transport·evidence)을 둔다. 추적되지 않는 중첩 저장소와 dirty 서브모듈(`--ignore-submodules=none --untracked-files=all`, 어떤 `submodule.*.ignore`·`diff.ignoreSubmodules`·`status.showUntrackedFiles` 설정도 숨기지 못함)은 거부한다. 원격 리뷰어는 이 호스트의 리뷰 트리에 닿을 수 없으므로 어떤 잡도 위임하기 전에 거부하거나 `diff-only` 로만 받는다. 옵션이 없으면 이전 동작 그대로다.
|
||||
4. **테스트 격리 (#20, `tests/conftest.py`)**: 수집 전에 ambient 제어 평면 바인딩을 기록·제거하고 테스트마다 레지스트리·감사 로그 기본 루트를 tmp 로 보낸다. 기록된 제어 평면의 `.mam/jobs`·`.mam/delegate_job_logs` 에 새 항목이 생기면 세션 가드가 실패시키고, 기본 해석 규칙을 검증하는 #15 테스트는 명시적으로 옵트아웃한다. `MAM_ENV_FILE` 은 존재하지 않는 경로로 고정해 실 `.mam.env` 를 읽지 않는다.
|
||||
5. **호스트 이식성 (#28)**: `tests/test_workspace_scope.py` 는 `bash -c` 로 실행(dash 에 `source` 없음), argparse 도움말 비교는 `_normalize_help` 로 py3.10+ 의 "options:" 를 흡수, `test_deploy_freshness.py` 의 private-server 문서·docker 자산 의존 테스트는 자산이 없으면 skip, `lib_py/agents/base.py`·`lib_py/delegation/heartbeat.py` 의 프로세스 스캔은 POSIX `ps -eo pid=,ppid=,args=` 로 통일(BSD 전용 인자가 Linux 에서 purge 12건을 깨뜨리던 원인 가설). `test_issue14_delivery_ack.py` 의 벽시계 close 여유는 `timeout_s + 15`.
|
||||
6. **`.gitignore` 관리 블록 (#27, `deploy/install.sh`, `deploy/install_mam.sh`)**: `.venv` 를 파일·디렉터리·링크 어느 형태로도 무시한다(재설치는 옛 규칙만 바꾸고 사용자 줄은 보존).
|
||||
7. **문서**: 설계 문서(항목 4·5·7 상태, 서브모듈 제거 규칙, resume cwd 규칙, 루트 3종·리뷰 identity), create/stop/resume/loop SKILL.md, 호환성 표.
|
||||
|
||||
#### ✅ 품질 검증
|
||||
- **신규 테스트** (`tests/test_issue21_phase2_loop_a.py`, `tests/test_issue21_loop_b.py`, `tests/test_issue21_acceptance.py`(A1–A8), `tests/test_issue20_state_isolation.py`, `tests/test_issue21_worktree_prereqs.py` 확장): prepare 의 멱등성·거부·dry-run·venv(생성/유지/링크 거부/인터프리터 부재)·서브모듈 롤백, remove 의 R1–R6(종료 중 쓰기, 서브모듈 stash, 부분 init 롤백, 기존 파일, deinit 저장소, tag 로만 보존한 커밋)과 `status.showUntrackedFiles=no` 로 숨긴 untracked 파일, 리뷰 identity 의 스냅샷·stale·승격 재확인·격리 실패·원격 거부·dirty 서브모듈(숨김 설정 포함), #20 가드. 수용 사례 A1–A8 은 mock herdr 기준이며 실제 pane 의 env 값 확인은 단계 C 로 남긴다.
|
||||
- **리뷰 (병합 워크스페이스, PR head 를 detached 워크트리에 받아 4인 독립 검토)**: @46ea4cc claude·agy·grok PASS, codex NOT PASS(R1 `status.showUntrackedFiles=no` 가 숨긴 서브모듈 untracked 파일이 force 제거를 통과, R2 같은 결함으로 리뷰 스냅샷이 미포착 내용을 승인, R3 prepare 의 `.venv` 심링크) → 08db1da 로 정정. 델타 리뷰 @08db1da claude·agy·grok PASS(비차단 D1–D3 → 80a5614 반영), codex 는 루프의 프롬프트 주입 실패(잡 14c7dc38, `started` 없음)로 직접 재위임해 @80a5614 PASS. 리포트 8건은 `.agents/reports/<session>/report-<jid>.md`(291e38b5·f27519ff·5f1486ee·b880c1f2·f1174fc0·d9275506·19d56cde·f6b21a0d).
|
||||
- **전체 스위트 (병합 워크스페이스)**: @46ea4cc 1013 통과 / 1 skip / 실패 1건(부하 시 벽시계 경계, 90a289d 로 여유 확대). @08db1da(자체 venv 가 없는 워크트리에서 실행) 1015 통과 / 1 skip / 4 실패 — 4건 모두 워크트리의 `.venv` 부재로 스크립트가 system python(paho 없음)을 잡은 탓이며, 워크트리 자체 venv 를 만든 뒤 4건 모두 통과(80a5614 가 prepare 의 설치 목록에 delegate-job requirements 를 넣은 동기).
|
||||
- **#28 후속**: Linux/py3.14 호스트에서 `ps -eo` 수정이 purge 12건을 실제로 고치는지 확인이 필요하다(가설 단계).
|
||||
|
||||
### 🚀 `v4.6.0` — Issue #21 워크트리 운영 모델 1단계 선행 작업: 래퍼 절대경로, 루프 `--worktree`, 제어 평면 바인딩, 설계 문서 (2026-09-14, PR #22)
|
||||
|
||||
> **마이너 마일스톤 (MINOR Release)**: "워크스페이스 = 체크아웃 경로" 가정을 느슨하게 하는 1단계다. creator 는 이슈별 git 워크트리에서, 리뷰어 풀은 본 체크아웃에서, 잡 브리프·레지스트리·리포트·체크포인트(제어 평면)는 본 체크아웃 하나에 두는 운영 모델(#21, `docs/design/worktree-creator-model.md`)의 선행 조건 4건(작업 항목 1·2·3·6)을 구현했다. 루프 러너 옵션 3개와 `lib_py/control_plane.py` 가 추가되어 MINOR 로 올린다. 옵션 없이 쓰면 종전 동작과 프롬프트가 바이트 단위로 같다. 항목 4(워크트리 준비/종료 헬퍼)·5(리뷰 identity)·7(시험 운영)·8(다중 루프)은 #21 에 남아 있다.
|
||||
|
||||
Reference in new issue
Block a user