Files
multi-agent-mux/.agents/reports/planner-reviewer-claude-01/report-895b0370.md
T
GodopuandClaude Fable 5.1 22586d2a01 release(v4.7.4): bump framework and eight skills to 4.7.4 in lockstep; archive PR #38 merge-side reviews
Issue #34: the loop waits for the creator job's sub-jobs, reviewers get
the sub-job table as evidence, a run with sub-jobs voids a round whose
source changed during review, and a pane caller reaches an agent of
another workspace on its own server. VERSIONS.md carries the entry; the
merge-side reports (round 1 @ed293dc and round 2 @e80186f with identity
sidecars, base ed293dc -> head e80186f, evidence review-tree) are archived
under .agents/reports/<session>/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
2026-09-25 16:44:20 +09:00

86 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🔍 Review Report — PR #38 델타 리뷰 2회차 (job `895b0370`)
- **Reviewer**: `planner-reviewer-claude-01` (role: planner,reviewer) · 2026-09-25
- **대상 델타**: `ed293dc..e80186f` (2커밋, 3파일 `+73/−10`, `git diff --check` 클린), base `main 7a4a0c2` (v4.7.3).
- **검증 경로**: `/var/folders/.../T/mam-review/loop-20260925T072328Z-2f5853/1`. **identity 확인**: `HEAD = e80186f5…4774`, `HEAD^{tree} = 35018688…7a72` — 브리프와 일치.
- **1회차 내 판정**: PASS + K1~K4 (job `107d435d`). 이번에는 N1/R1(cancelled 사유)·R2(원격 마감 문구)·K1(STRAY_RULE)의 정정을 본다.
- **안전 규칙 준수**: 검증 경로·소스 워크트리·본 체크아웃(`main 7a4a0c2`) 모두 수정·커밋·checkout 없음. 실험은 in-process 러너(`env -u MAM_VERSION`, 임시 등록부·감사 로그)와 되돌린 사본에서만. 실제 herdr·세션·`.mam`·데몬 조작 없음, push·merge·PR 상태 변경 없음.
## 결론 요약
**세 정정 모두 지적을 닫고, 1회차 판정을 뒤집을 요소는 없다.** 종결 사유 12가지를 `_subjob_state` 에 넣어 분류가 설계대로임을 확인했고(감사 ndjson 경로·`.events.log` 폴백 양쪽), `get_feedback` 의 기본 동작은 그대로다. 되돌린 사본에서 T13×2·T14·T6 만 실패한다. 비차단 관찰 1건(L1).
## 1. N1/R1 — `cancelled` 사유 (항목 1)
- `registry.get_feedback(job_id, registry_dir, events=("completed","error"))` (`registry.py:316-317`): 기본값이 종전과 같아 호출자 — 래퍼의 `get-feedback` CLI 3곳(`:556`,`:565`,`:593`)과 `registry.py get-feedback` 서브커맨드(`:668`) — 는 인자 없이 부르므로 **동작 불변**. 실측: `error "first"` 뒤 `cancelled "session_busy"` 가 온 잡에서 기본 호출은 `'first'`, `events=(…,"cancelled")` 는 `'session_busy'`.
- `_subjob_state`(`runner.py:684-702`)만 `cancelled` 를 포함해 읽는다(`:691`).
- 두 경로 모두 확인: `.events.log` 에만 `cancelled` 가 있는 잡 → `NOT-DISPATCHED/target_not_found`; **감사 ndjson 에만**(`.events.log` 없음, `source_event: cancelled`) 있는 잡 → `NOT-DISPATCHED/session_busy`.
## 2. R2 — 첫 단어 판정 (항목 1)
`closed_kind = reason.split(" ", 1)[0]`(`:696`) 을 `ORCHESTRATOR_CLOSED` 와 비교한다. 원격 러너의 실제 문구는 `remote.py:352` 의 `"deadline_timeout (remote runner)"` 다. 오분류 여부를 사유 12가지로 실측:
| 상태·사유 | 분류 |
|---|---|
| `error` `deadline_timeout` (래퍼) | CLOSED-UNCONFIRMED |
| `error` `deadline_timeout (remote runner)` | **CLOSED-UNCONFIRMED** (R2 정정) |
| `error` `agent_blocked` | CLOSED-UNCONFIRMED |
| `error` 에이전트 자신의 사유(`tests failed on 3 cases`) | `error`(실패) — 첫 단어 `tests` 는 튜플 밖 |
| `error` 사유 없음 | CLOSED-UNCONFIRMED (기존 규칙) |
| `cancelled` `target_not_found` / `session_busy` / `rolled back by create_session` | **NOT-DISPATCHED** (N1 정정; `session_busy` 는 첫 단어 판정에 걸리지 않고 cancelled 분기로 간다) |
| `cancelled` 사유 없음 / `operator abort` | `cancelled`(실패 집계) |
| `completed` `all done` | completed |
첫 단어 판정이 `session_busy`·`target_not_found`·`rolled` 등을 미확인 종결로 오분류하지 않는다. 남는 이론적 여지는 에이전트가 **자기 사유를 `deadline_timeout`/`agent_blocked` 로 시작하는 문장으로 쓰는 경우**뿐인데, 이 두 단어는 오케스트레이터 예약어라 실무에서 겹치지 않는다(L1, 정보).
## 3. K1 — `STRAY_RULE` (항목 1)
`runner.py:41-42` 의 문장은 "귀속되지 않은 열린 잡은 `--parent-job` 없이 등록된 이 작업의 하위 위임일 수 있다; 그 산출물이 작업 목표에 필요하면 NOT PASS" 이고, `_subjobs_block` 은 stray 가 있을 때만 stray 줄 뒤에 붙인다(`:727-729`). SUBJOB_RULE(하위 잡이 있으면 대체 완료 입증이 없는 한 NOT PASS)과 **모순되지 않는다** — stray 는 무관한 잡일 수 있으므로 "필요하면" 이라는 조건부가 맞고, 둘 다 같은 방향(미완 → NOT PASS)이다. stray 가 없으면 프롬프트는 1회차와 바이트 동일(`if not rows and not stray: return ""` 유지).
## 4. 테스트 (항목 1·2)
### 4-1. 수정 없이는 실패하는가 — 확인함
검증 경로 사본에서 `registry.py`·`runner.py` 만 `ed293dc` 판으로 되돌리고 `-k "t13 or t14 or t6 or t5"` 를 돌렸다:
```
4 failed, 3 passed, 13 deselected in 22.39s
FAILED test_t13_…[target_not_found] ← N1
FAILED test_t13_…[session_busy] ← N1
FAILED test_t14_the_remote_runners_deadline_wording_is_closed_unconfirmed ← R2
FAILED test_t6_an_unattributed_open_job_is_shown_and_waited_for_only_on_request ← K1 (새 단언)
```
T5/T5b(`deadline_timeout` 정확 일치·늦은 쓰기 무효화)는 양쪽에서 통과 — 기존 분류가 정정으로 바뀌지 않았다. T13·T14 는 실제 `registry.py close` + `publish_event` 로 종결을 만드는 시험이라(mock 아님) 원격 러너의 실제 문구 그대로를 검증한다.
### 4-2. 검증 경로에서의 실행
```
tests/test_issue34_job_tree.py tests/test_issue5_phase1_delegation.py tests/test_issue14_delivery_ack.py
tests/test_loop_cli.py tests/test_issue25_plan_reuse.py tests/test_issue21_loop_b.py
→ 158 passed in 271.04s failed 0 · skipped 0
```
새 파일 20건 전부 통과, `registry.py` CLI 스냅샷·help 캡처(issue5 phase1) 통과 → `get_feedback` 시그니처 확장이 CLI 계약을 건드리지 않는다. 실제 `.mam/jobs` 전후 **차이 0**, 검증 경로 tracked 파일 변화 없음(HEAD `e80186f`), 본 체크아웃 `main 7a4a0c2`.
## 5. 병합 차단 여부 (항목 2)
1회차 PASS 를 뒤집을 요소는 없다. 델타는 `get_feedback` 의 기본값 보존 인자 하나, `_subjob_state` 의 두 줄, `STRAY_RULE` 한 문장, 회귀 3건이며, 정정 대상만 정확히 실패→통과로 바뀌었다. 버전 락스텝 bump 부재(`lib.sh:32` `4.7.3`, 오케스트레이터가 4.7.4 로 처리 예정) 외에 **병합을 막을 결함은 없다**. 1회차 K2(`MAM_JOB_TREE_MAX` 초과 시 대기 생략)·K3(`target_not_found` 의 rc 5)·K4(폴링마다 전체 스캔)는 이 델타 범위 밖이라 그대로 열려 있다.
| # | 비차단 관찰 | 근거 |
|---|---|---|
| L1 | 첫 단어 판정은 에이전트가 자기 사유를 `deadline_timeout`/`agent_blocked` 로 시작해 쓰면 미확인 종결로 본다 — 두 단어는 오케스트레이터 예약어라 실무 충돌은 없다 | `runner.py:696`, §2 표 |
## 6. 교차검증 가이드
```bash
RP=/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260925T072328Z-2f5853/1
PY=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python
git -C "$RP" rev-parse HEAD 'HEAD^{tree}' # e80186f… / 3501868…
# in-process (env -u MAM_VERSION): 임시 등록부에 12가지 종결 사유를 .events.log / 감사 ndjson 으로 심고 _subjob_state 호출 (§2 표)
# 되돌린 사본: registry.py·runner.py 를 ed293dc 판으로 → -k "t13 or t14 or t6 or t5" → 4 failed / 3 passed
(cd "$RP" && AGENT_PYTHON_BIN="$PY" PYTHONDONTWRITEBYTECODE=1 "$PY" -m pytest -p no:cacheprovider -q --basetemp=/tmp/bt tests/test_issue34_job_tree.py) # 20 passed
```
[VERDICT: PASS]