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, baseed293dc-> heade80186f, 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
51 lines
4.6 KiB
Markdown
51 lines
4.6 KiB
Markdown
# 🔍 Review Report — Job addfcf70 (PR #38 @ `ed293dc`)
|
||
|
||
- **Reviewer**: `reviewer-grok-01` · 2026-09-25
|
||
- **검증 경로**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260925T055502Z-a37cf1/1`
|
||
- **identity**: base `7a4a0c2` head `ed293dc` tree `671b0e5` (일치)
|
||
- **본 체크아웃**: `main` `7a4a0c2` 유지
|
||
|
||
`git diff --check 7a4a0c2...HEAD` 클린. 6커밋.
|
||
|
||
## NOT PASS
|
||
|
||
### N1. `cancelled` 사유는 `get_feedback` 이 읽지 않아 `NOT-DISPATCHED` 가 되지 않는다
|
||
|
||
`registry.py:316-349` `get_feedback` 은 마지막 `completed`/`error` 의 detail 만 돌려준다. `cancelled` 는 보지 않는다.
|
||
|
||
래퍼는 미투입 종료를 `publish_event --event cancelled --detail target_not_found|session_busy` 로만 남긴다 (`multi-agent-mux-delegate-job` finalize). `runner.py:687-694` `_subjob_state` 는 그 reason 이 `session_busy` / `target_not_found` / `rolled back…` 일 때만 `NOT-DISPATCHED` 다. reason 이 항상 `""` 이므로 표는 `cancelled` / `-` 가 된다.
|
||
|
||
`deadline_timeout`·`agent_blocked` 는 `error` 이벤트 detail 이라 `CLOSED-UNCONFIRMED` 는 맞다. 사유 없는 `error` 도 맞다. 빠지는 것은 취소 쪽뿐이다. `test_issue34_job_tree.py` 는 `NOT-DISPATCHED` 를 단언하지 않는다.
|
||
|
||
**대안**: `get_feedback` 이 마지막 `cancelled` detail 도 반환하게 하거나, `_subjob_state` 가 `events.log` 의 마지막 `cancelled` detail 을 직접 읽게 하라. 그 뒤에 `target_not_found` 잡이 표에서 `NOT-DISPATCHED | target_not_found` 인 테스트를 둘 것.
|
||
|
||
리뷰어 규칙이 `cancelled` 자체도 실패로 보게 하므로 판정이 뒤집히지는 않는다. 사유(투입 전 취소 vs 에이전트 실패)는 사라진다.
|
||
|
||
## 그 외 (N1 만 막음)
|
||
|
||
**(1)** 없는 `--parent-job` 은 래퍼와 `registry.py register` 둘 다 exit 1. `MAM_JOB_ID` 가 이 등록부에 없으면 경고 후 부모 없음. `descendants_of` 는 한 번 스캔, `seen` 으로 순환 중단, `depth` 는 조회 때 1부터. 폴링마다 전체 스캔은 수백 건에서 부담이 작다. `MAM_JOB_TREE_MAX` 초과·`MAM_CHILD_WAIT_S` 만료는 대기를 멈추고 루프는 리뷰로 간다. 표는 상한 없이 자손을 그리므로 `OPEN` 이 남아 NOT PASS 규칙이 적용된다. 기다리는 쪽이 더 위험하다.
|
||
|
||
**(2)** 주입문·brief·create `--submit-job` 이 `--parent-job` 을 적는다. 에이전트가 빠뜨리면 `--wait-open-jobs`(기본 off)와 `[귀속되지 않은 열린 잡]` 이 안전망이다. 원격은 `MAM_JOB_ID` 가 이 등록부에 있을 때만 기본 부모.
|
||
|
||
**(3)** `wait_for_job_tree` 는 실행(`round_roots = [exec_jid]`), 수정·순응(`append` 후)에 걸린다. `MAM_CHILD_WAIT_S` 기본 3900 은 그 호출 시각부터. SIGINT/SIGTERM 핸들러는 `sys.exit` 이라 `interrupted` 폴링은 보조다. `wait_for_descendants` 가 False 여도 `wait_for_job_tree` 는 True 를 돌려 리뷰로 넘긴다(OPEN 은 표에 남음).
|
||
|
||
**(4)** 자손도 stray 도 없으면 `_subjobs_block` 은 `""`. stray 는 `created_jobs` 를 빼므로 이 루프의 리뷰어 잡은 제외된다. 다른 루프의 부모 없는 열린 잡은 표시된다(기본 대기는 안 함). `SUBJOB_RULE` 은 OPEN·CLOSED-UNCONFIRMED·실패 시 NOT PASS, 대체 완료는 예외라고 적는다.
|
||
|
||
**(5)** `_fingerprint` 는 `.mam`·jobs·감사 로그가 소스 안일 때만 exclude. 절대 `DELEGATE_JOB_LOGS_DIR` 은 소스 밖이라 비교에 안 들어간다. 가드는 `subjobs` 가 비지 않을 때만(1d4f338). identity 모드(`--worktree`/`--base`)는 `light` 를 안 켠다. 승격 직전 `light` 재확인이 있다. `test_e2e_scenario5_multi_agent_review_loop` 통과.
|
||
|
||
**(6)** 같은 서버에서 `agent|has-session|kill-session` 이고 `HERDR_PANE_ID` 의 워크스페이스가 `HERDR_WORKSPACE_ID` 와 같을 때만 그 ID 를 뺀다. 교차 서버는 기존처럼 세 ID 를 전부 뺀다. pane 밖·불일치 ID·`new-session`/pane 호출은 유지(#7 패킹). `target_not_found` 는 preflight detail·cancelled 이벤트에 들어간다(표 라벨은 N1).
|
||
|
||
**(7)** `test_issue34_job_tree.py` 는 실제 래퍼 `--dry-run`·registry·publish. tmp 만 씀.
|
||
|
||
**(8)–(9)** SKILL·MULTI_AGENT_RULES(ko) 가 `--parent-job`, `MAM_CHILD_WAIT_S`, `--wait-open-jobs`, 판정 규칙, stale 가드를 적는다. 4.7.4 bump 는 오케스트레이터 예정.
|
||
|
||
## 테스트
|
||
|
||
검증 경로, `AGENT_PYTHON_BIN` = 컨트롤 `.venv`, basetemp `/tmp`:
|
||
|
||
`test_issue34_job_tree.py` + `test_issue23_24_startup_path.py` + `test_tier4_e2e.py` + `test_issue5_phase1_delegation.py` + `test_issue21_worktree_prereqs.py`
|
||
|
||
**193 passed in 1429.09s.** N1 은 이 스위트에 단언이 없다.
|
||
|
||
[VERDICT: NOT PASS]
|