Files
multi-agent-mux/.agents/reports/reviewer-grok-01/report-0d52f435.md
T
GodopuandClaude Opus 5.5 53f87e4560 release(v4.8.0): bump framework and eight skills to 4.8.0 in lockstep; archive PR #41 reviews (three rounds)
Issue #33 (job budget, wall_clock_timeout vs deadline_timeout) and
Issue #30 (notify-only stall watcher). Reviews of PR #41 in the merge
workspace: round 1 (@1673818) claude N1 / codex R1 R2 NOT PASS, agy grok
PASS; round 2 (@af0a2a2) grok N1 / codex D1 D2 NOT PASS, claude agy PASS;
round 3 (@9200562) 4/4 PASS. Reports and the round-3 identity sidecars go
to .agents/reports/<session>/. The design consensus on items A-D is in the
VERSIONS.md entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
2026-09-26 16:08:37 +09:00

4.7 KiB
Raw Blame History

🔍 Review Report — Job 0d52f435 (PR #41 @ 1673818)

  • Reviewer: reviewer-grok-01 · 2026-09-26
  • 검증 경로: /var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260926T025230Z-a30a6d/1
  • identity: base 8ea65bb head 1673818 tree 00d5e03 (일치)
  • 본 체크아웃: main 8ea65bb 유지. stall_watch 를 실제 세션에 붙이지 않음.

git diff --check 8ea65bb...HEAD 클린. 6커밋.

코드

(1) 예산. --timeout > MAM_JOB_TIMEOUT_S(양의 정수, 아니면 경고 후 3600) > 3600. 루프 contract_args 가 모든 _dispatch 에 --timeout str(job_budget) 을 붙인다. job_wait_s = 예산 + MAM_WAIT_GRACE_S(기본 300). MAM_CHILD_WAIT_S 가 있으면 그 값, 없으면 job_wait_s. 원격 러너는 timeout_sec 초과를 wall_clock_timeout: … 로, 관측 실패를 wall_clock_timeout (remote runner) 로 닫고 첫 단어 분류가 CLOSED-UNCONFIRMED 에 넣는다.

(2) 라벨. 구독자 RC_* 와 래퍼 SUB_RC_* 가 0/1/2/3/4. test_issue5_phase1_delegation.py 가 이름과 값을 맞춘다. finalize_unfinished 는 rc 4 또는 디스크 유예가 START_TIME+TIMEOUT 에 닿으면 wall_clock_timeout, 그 외 침묵은 deadline_timeout. preflight/idle_wait 는 cancelled. delegate-job wait 는 rc 4 를 rc 2 로 접는다(G-8). 닫은 뒤 디스크가 completed 면 래퍼는 0 으로 돌아간다. 이미 error 인 레코드에 늦은 completed 는 엔진 터미널 전이가 거부한다.

(3) 감시자. stall_watch.py 의 유일한 발행은 _notify 의 progress. close/error/cancelled 호출이 없다. herdr agent wait(watcher.wait_once)로 상태를 받고, 판단 직전에 agent get·process-info·agent read 로 자손과 입력창을 본다. blocked 는 stalled:blocked + detection 한 줄, 키 입력 없음. idle 은 MAM_STALL_IDLE_S(600), 상주 자식·heartbeat 타이머 제외, 입력창이 비었을 때만 잡당 1회 stalled:idle + nudge. 원격·human·MAM_STALL_WATCH=0 은 기동하지 않는다. herdr 실패는 경고 후 MAM_STALL_HERDR_FAIL_S(120) 에 exit 2. 래퍼는 SIGTERM + wait. .stall.out 은 로그로 남는다(프로세스는 회수).

(4) 경계. 새 bash 판정은 셋이다. 항목 4 이전이 예고돼 있으므로 비차단.

bash 하는 일 분류
FINALIZE_CLOCK 디스크 유예가 벽시계에 닿았는지로 라벨을 고름 판정. 구독자 rc 4 는 Python 이 이미 정함
MAM_JOB_TIMEOUT_S 양의 정수가 아니면 3600 작은 검증
rc → close_req 2/4/preflight 를 라벨로 매핑 나르기. 매핑표는 bash 에 있음

(5) 테스트. G-8 은 두 시계가 같은 rc 2 였던 단언을 rc 4(벽시계)로 고쳤다. 새 테스트는 mock herdr·tmp. 1회차 스위트에서 test_b3_a_heartbeating_job… 가 공개 브로커가 heartbeat 를 놓쳐 구독자는 idle 로 끝났고(래퍼 라벨은 이미 wall_clock_timeout), 재실행은 1 passed in 18s.

(6)–(7) loop SKILL 하위 위임 절이 wall_clock_timeout 을 CLOSED-UNCONFIRMED 에 넣는다. delegate-job/loop SKILL 에 --job-timeout·MAM_JOB_TIMEOUT_S·감시자 변수가 있다. 4.8.0 bump 는 오케스트레이터 예정.

설계 합의

(A) 동의. 갱신 1(알림만, 자동 수락 없음, 판정으로 닫지 않음)을 stall_watch.py 가 강제한다. 종결은 #33 시계뿐이다.

(B) 동의. 러너가 래퍼를 동기 호출하므로 연장 정책을 그 안에서 물을 수 없다. #39 위험은 에이전트가 progress 로 연장을 반복하는 것이다. 보완: 연장 횟수 상한을 프로토콜과 별개로 두고, heartbeat 만 있고 state_change_seq 가 그대로면 거절한다.

(C) 조건부 동의. 트리거는 completed 를 검증하는 쪽이어야 한다. 워크트리 creator(MAM_NO_SUBSCRIBER_DAEMON=1)도 잡마다 래퍼 구독자가 completed 를 받으므로 핀은 그 구독자가 맞고, 루프 wait_for_job 은 구독자가 없을 때의 보조다. 항목 4 가 종결 처리를 Python 으로 옮긴 그 함수에 붙이면 된다.

(D) 동의. 라벨 분리만으로 #33 은 성립하고, 종결 처리 이전은 더 큰 변경이다. 항목 4 때 Python 으로 옮길 것은 FINALIZE_CLOCK 선택, MAM_JOB_TIMEOUT_S 검증, rc→close_req 매핑이다. bash 에는 start_stall_watch/stop_stall_watch 와 키 주입만 남긴다.

테스트

test_issue30_stall_watch.py + test_issue33_budget.py + test_tier1_unit.py: 1 failed, 86 passed, 48 skipped, 이어서 실패 1건 재실행 passed.

종합

#33 두 시계와 #30 알림 전용이 코드와 맞다. bash 의 라벨 선택은 예고된 이전이 남았고 병합을 막지 않는다.

[VERDICT: PASS]