Files
multi-agent-mux/.agents/reports/reviewer-agy-01/report-22bdf1b4.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

11 KiB
Raw Blame History

Review Report: PR #41 (fix(#30, #33): 위임 잡 예산 조절과 벽시계 라벨 분리, 멈춘 에이전트 알림)

  • Job ID: 22bdf1b4
  • Reviewer: Antigravity CLI (agy, session: herdr:reviewer-agy-01, Role: Reviewer)
  • PR Target: #41 (fix/issue-30-33 @ 1673818, base main 8ea65bb / v4.7.4)
  • Review Identity & Tree:
    • loop_id: loop-20260926T025230Z-a30a6d
    • base: 8ea65bb23830b56c4530897270edaa6e556de392
    • head: 1673818bb5fed14ed6fee87884e70d39e81fe91f
    • tree: 00d5e03e2d521c87c3474c032f2af6f7a1af9e92
  • Verification Worktree: /var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260926T025230Z-a30a6d/1
  • Verdict: PASS

1. Executive Summary & Verification Context

PR #41 provides a comprehensive and robust implementation resolving Issue #30 (stalled agent detection and notification) and Issue #33 (job budget propagation and separation of wall-clock vs idle timeout labels).

Key Architectural Enhancements

  1. #33 Job Budget & Wall-Clock Label Separation:
    • Budget priority chain: --timeout > MAM_JOB_TIMEOUT_S > 3600s with non-positive fallback warnings.
    • Loop runner adds --job-timeout <sec>, propagating explicitly via contract_args() across all turns (implementation, review, correction, rebuttal, compliance).
    • Dynamic runner wait cap: job_wait_s defaults to budget + 300s (MAM_WAIT_GRACE_S). MAM_CHILD_WAIT_S follows the budget unless explicitly overridden.
    • Strict label distinction: Idle timeout without events yields deadline_timeout (rc 2), while expired budget yields wall_clock_timeout (rc 4 in subscriber, mapped to rc 2 for backward-compatible delegate-job wait). Both are integrated into ORCHESTRATOR_CLOSED and sub-job reporting tables.
  2. #30 Notify-Only Stall Watcher (lib_py/stall_watch.py):
    • Strictly notify-only: Contains zero code paths that close or fail a job (publishes only progress events via publish_event.py, verified statically via AST analysis in test_s12).
    • blocked handling: Detection snapshot attached from herdr buffer; no automated response or key injection (fully conforms to Team Agreement Revision 1).
    • idle handling: Evaluates S seconds (MAM_STALL_IDLE_S, default 600s) of continuous idle, verifies no OS background descendants (excluding adapter resident_children and heartbeat timer), and confirms input box emptiness before delivering a single non-disruptive nudge prompt per job.
    • Automated cleanup: Graceful SIGTERM handling with zero orphan/zombie processes.

Identity & Cleanliness

  • git rev-parse HEAD = 1673818bb5fed14ed6fee87884e70d39e81fe91f
  • git rev-parse 'HEAD^{tree}' = 00d5e03e2d521c87c3474c032f2af6f7a1af9e92
  • git diff 8ea65bb...HEAD --check = Clean (0 formatting / whitespace errors)
  • Verification worktree status: Clean

2. Technical Code Audit (Items 1 – 7)

(1) #33 Budget Propagation & Wait Caps

  • Wrapper Priority: TIMEOUT is initialized to 3600, overridden by valid positive MAM_JOB_TIMEOUT_S (with stderr warning on invalid input), and finally overridden by --timeout.
  • Loop Runner Propagation: --job-timeout validates positive integers. contract_args() passes --timeout <budget> to every delegated execution.
  • Wait Caps: job_wait_s calculates budget + 300 (respecting MAM_WAIT_GRACE_S). MAM_CHILD_WAIT_S defaults to job_wait_s and respects explicit environment overrides.
  • Remote Runner: remote.py labels command timeouts and poll deadline expiration as wall_clock_timeout, maintaining parity with local executions.

(2) #33 Label Separation & Terminal Guarantees

  • Exit Codes: Subscriber constants (RC_COMPLETED=0, RC_ERROR=1, RC_NO_EVENT=2, RC_BROKER_UNAVAILABLE=3, RC_WALL_CLOCK=4) are mirrored 1:1 in bash wrapper SUB_RC_*.
  • Close Paths: FINALIZE_CLOCK correctly identifies wall-clock expiration when subscriber returns rc 4, when disk heartbeat grace encounters deadline, or when broker is unreachable up to deadline.
  • Registry & Subjob Table: registry.py CLI parser accepts wall_clock_timeout, engine.py maps it to error, and runner.py includes it in ORCHESTRATOR_CLOSED (closed_kind safely strips trailing colon for remote runner format).
  • Compatibility: cmd_wait() normalizes SUB_RC_WALL_CLOCK to SUB_RC_NO_EVENT (rc 2), fully preserving G-8 caller contracts.

(3) #30 Stall Watcher (stall_watch.py)

  • Notify-Only Verification: AST inspection confirms no imports of lib_py.delegation.registry, no references to close/update_status, and all published events are strictly --event progress.
  • Herdr Queries: Relies on socket API agent wait without aggressive polling.
  • Blocked State: Captures prompt text from --source detection --lines 8 and publishes stalled:blocked <dialog> once per episode.
  • Idle State: Requires continuous idle across state_change_seq, verifies absence of non-resident child processes (ignoring heartbeat timers and resident hosts like codex-code-mode-host), verifies empty input area, and issues one polite nudge per job.
  • Process Management: Wrapper manages child PID via start_stall_watch/stop_stall_watch with SIGTERM termination and stderr logging on unexpected watcher exit.

(4) Boundary Compliance Audit (#17 Item 4)

Bash 위치 코드 내용 역할 분석 준수 여부
multi-agent-mux-delegate-job (상수) SUB_RC_* 미러링 Python 상수 1:1 매핑 준수 (Execution)
multi-agent-mux-delegate-job (MAM_JOB_TIMEOUT_S) 환경변수 정수 검증 및 fallback CLI 옵션 기본값 처리 준수 (Execution)
start_stall_watch / stop_stall_watch 프로세스 fork 및 SIGTERM reap 외부 프로세스 수명 관리 준수 (Execution)
finalize_unfinished (FINALIZE_CLOCK) sub_rc 4 또는 deadline 도달 시 라벨 전달 종결 라벨 매핑 (Python 이전 대기) 허용 (비차단, 항목 4 대상)
cmd_wait rc 4 -> rc 2 변환 하위 호환성 정규화 준수 (Execution)

Bash layer does not perform autonomous policy decisions; it purely carries signals and spawns processes.

(5) Existing Test Adjustments & Isolation

  • test_tier1_unit.py (G-8): Updated to test that both clocks yield rc 2 for wait.
  • tests/fixtures/panes/: Added 5 real idle captures across supported agents.
  • conftest.py: Mock herdr updated with real 0.9.1 process-info structures and detection buffers.
  • All new tests run under isolated sandboxes without touching real herdr servers or production .mam directories.

(6) Documentation & Versioning

  • Both multi-agent-mux-delegate-job/SKILL.md and multi-agent-mux-loop/SKILL.md are updated with --job-timeout, MAM_JOB_TIMEOUT_S, wall_clock_timeout, and stall watcher configuration variables.
  • Version 4.8.0 bump is properly reserved for orchestrator release.

3. Test Suite Results

  • tests/test_issue30_stall_watch.py:
    • Static AST notify-only check: PASSED
    • Input region extraction across all 5 agent captures & typing states: PASSED
    • Decision table (14 pure state cases): PASSED
    • Full suite skipped on macOS due to Linux /proc requirement; verified clean on Linux testbeds.
  • tests/test_issue33_budget.py:
    • 19 / 20 PASSED offline; test_b3 live broker test verified PASSED (19.25s).
  • tests/test_a4_adapter_contract.py, tests/test_tier1_unit.py, tests/test_issue5_phase1_delegation.py: 166 / 166 PASSED (91.98s).
  • tests/test_issue18_liveness_contract.py: 31 / 31 PASSED (146.70s).
  • tests/test_version_consistency.py, tests/test_loop_cli.py, tests/test_issue34_job_tree.py: 32 / 32 PASSED (123.53s).
  • Total: 250+ regression tests passed with 0 regressions.

4. 설계 합의 (Design Consensus)

(A) #30 알림 전용 원칙 (갱신 1)

  • 판정: 동의 (Agree)
  • 근거: stall_watch.py의 알림 전용(Notify-only) 원칙은 Issue #18의 대원칙("오직 프로토콜 이벤트만이 위임 잡의 종결 근거가 된다")을 온전히 지키는 필수 안전장치입니다. 외부 감시 프로세스가 TUI 출력이나 상태 추론만으로 잡을 강제 종결하거나 대화창에 임의 키를 주입하는 것은 장기 연산 중인 에이전트의 정상 작업을 교란할 위험이 높습니다. 본 PR은 AST 정적 분석(test_s12)을 통해 stall_watch.py가 오직 progress 이벤트만 발행할 수 있도록 원천 차단하고 있어 합의된 원칙을 코드로 엄격히 강제합니다.

(B) 예산 연장의 #39 분리 및 위험 보완책

  • 판정: 동의 (Agree)
  • 근거: 동기 프로세스로 래퍼를 호출하는 현 러너 아키텍처에서 래퍼가 벽시계 도달 시 멈춰서 정책 결정을 기다리는 것은 프로세스 간 IPC/락 복잡도를 지나치게 가중시킵니다. 에이전트가 프로토콜 이벤트(progress --detail extend_budget)로 연장을 요청하는 #39 방식은 작업 의도를 직접 반영할 수 있어 타당합니다. 위험 요인: 루프나 버그에 빠진 에이전트가 무한 연장을 요청하여 벽시계 안전망이 무력화될 수 있습니다. 보완책: 잡 등록부 스키마에 extend_count와 max_extends(기본 1회, 최대 MAM_JOB_EXTEND_MAX)를 두고, 등록부 레벨에서 1회 한정 연장만을 허용하는 하드 캡(Hard Cap)을 적용하며, 최근 유효 heartbeat 및 state_change_seq 진척이 확인된 경우에만 연장 승인하도록 제한해야 합니다.

(C) #40 세션 ID 라이프사이클 트리거 위치 및 순서

  • 판정: 동의 (Agree)
  • 근거: 세션 등록부 및 제어 평면 메타데이터는 오케스트레이터의 배타적 소유 영역(#15/#20/#21)이므로, 세션 ID 캡처 트리거를 에이전트 내부(publish_event.py)가 아닌 completed 이벤트를 수신하는 수신단에서 처리하는 방향에 전적으로 동의합니다. 구체적으로는 **루프 러너의 wait_for_job 직후(또는 래퍼 구독자가 completed를 반환한 직후)**가 적합합니다. 순서상으로는 래퍼 종결 처리를 Python 코어로 일원화하는 #17 항목 4에 흡수하여 함께 다루거나 그 직후에 수행하는 것이 구조적 정합성에 부합합니다.

(D) #17 항목 4의 분리 타당성 및 향후 이전 대상

  • 판정: 동의 (Agree)
  • 근거: PR #41이 이미 #30(새 감시 모듈, TUI 파싱, herdr 연동)과 #33(예산 제어, 라벨 분리)이라는 대규모 변경(+2047/-95)을 포함하고 있으므로, 래퍼 종결 처리 전면 개편까지 한 번에 묶지 않고 분리한 판단은 리스크 관리 관점에서 매우 현명합니다. 향후 #17 항목 4 구현 시 본 PR의 bash 변경 중 다음 항목들이 반드시 Python 코어로 이전되어야 합니다:
    1. finalize_unfinished의 종결 처리 및 시계 판정(FINALIZE_CLOCK, 디스크 유예 대기, wall_clock_timeout vs deadline_timeout 매핑).
    2. MAM_JOB_TIMEOUT_S 유효성 검증 및 기본값 연산.
    3. job_subscriber.py exit code 매핑(_map_sub_rc) 및 cmd_wait의 반환 코드 정규화.
    4. start_stall_watch / stop_stall_watch의 서브프로세스 감시 수명주기 관리.

5. Conclusion

PR #41은 설계 합의 사항을 충실히 반영하여 예산 제어, 타임아웃 라벨 분리, 비간섭형 멈춤 감시 시스템을 안정적으로 완성했습니다. 모든 회귀 테스트가 통과하였으며 아키텍처 경계 기준도 적절히 준수되었습니다.

[VERDICT: PASS]