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
This commit is contained in:
GodopuandClaude Fable 5.1 committed 2026-09-25 16:44:20 +09:00
1 parent e80186f560
commit 22586d2a01
28 files changed
+825 -26

No files matched your search

@@ -0,0 +1,50 @@
# 🔍 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]
@@ -0,0 +1,14 @@
{
"loop_id": "loop-20260925T072328Z-2f5853",
"base_sha": "ed293dcef4c346e0050a31cb46812bcdcf1d67cf",
"head_sha": "e80186f56064eaf93a2f32c48d31d84816894774",
"tree_sha": "3501868872d828894f031911b8ca31b01ce77a72",
"review_commit": "e80186f56064eaf93a2f32c48d31d84816894774",
"reviewer": "reviewer-grok-01",
"job_id": "df6a4aa1",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-09-25T07:43:24Z"
}
@@ -0,0 +1,32 @@
# 🔍 Review Report — Job df6a4aa1 (PR #38 델타 `ed293dc...e80186f`)
- **Reviewer**: `reviewer-grok-01` · 2026-09-25
- **검증 경로**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260925T072328Z-2f5853/1`
- **identity**: head `e80186f` tree `3501868` (일치). 1회차 grok N1 을 이 델타가 닫음.
- **본 체크아웃**: `main` `7a4a0c2` 유지.
`git diff --check ed293dc...HEAD` 클린. 커밋 `8a9101e`, `e80186f`.
## (1) 세 정정 — 닫힘
**N1.** `get_feedback` (`registry.py:316-317`) 기본 `events` 는 `("completed", "error")` 그대로다. 감사 ndjson 은 `source_event`/`event`, `.events.log` 폴백은 `event` 를 같은 튜플로 본다. `_subjob_state` (`runner.py:690-691`) 만 `cancelled` 를 더 넣는다. T13: `target_not_found`·`session_busy` 가 `| NOT-DISPATCHED | <reason> |`. 임시 사본에서 그 인자를 빼면 T13 두 건이 실패했다.
**R2.** 종류는 `reason.split(" ", 1)[0]` (`runner.py:696-697`). `deadline_timeout (remote runner)` → `CLOSED-UNCONFIRMED`, 미확인 종결 1. `session_busy`·`target_not_found` 는 `ORCHESTRATOR_CLOSED` 에 없어 미확인으로 안 간다. 취소 쪽은 예전처럼 완전 일치/`rolled back` 접두다. 임시 사본에서 완전 일치로 되돌리면 T14 가 실패했다.
**K1.** stray 가 있을 때만 `STRAY_RULE` (`runner.py:726-729`). 없으면 그 문장이 없고, 자손도 stray 도 없으면 `""`. SUBJOB_RULE 은 표에 있는 자손용이고, STRAY_RULE 은 “필요할 때만 NOT PASS” 라 모순이 아니다. T6 통과.
## (2) 1회차·락스텝
부모 검증, 자손 대기, 같은 서버 이름 조회, stale 가드는 이 델타가 되돌리지 않는다. `feedback` CLI 와 다른 `get_feedback` 호출은 기본 인자라 취소 이벤트를 새로 집지 않는다. 4.7.4 bump 는 오케스트레이터 예정. 병합 차단 아님.
## 테스트
검증 경로, `AGENT_PYTHON_BIN` = 컨트롤 `.venv`, basetemp `/tmp`:
T13×2, T14, T6 → **4 passed in 13.93s**.
## 종합
1회차 N1·R2·K1 이 코드와 macOS 실행과 맞다. 설계 재작업 불필요.
[VERDICT: PASS]