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
11 KiB
11 KiB
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, basemain8ea65bb/ v4.7.4) - Review Identity & Tree:
loop_id:loop-20260926T025230Z-a30a6dbase:8ea65bb23830b56c4530897270edaa6e556de392head:1673818bb5fed14ed6fee87884e70d39e81fe91ftree: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
- #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 viacontract_args()across all turns (implementation, review, correction, rebuttal, compliance). - Dynamic runner wait cap:
job_wait_sdefaults tobudget + 300s(MAM_WAIT_GRACE_S).MAM_CHILD_WAIT_Sfollows the budget unless explicitly overridden. - Strict label distinction: Idle timeout without events yields
deadline_timeout(rc 2), while expired budget yieldswall_clock_timeout(rc 4 in subscriber, mapped to rc 2 for backward-compatibledelegate-job wait). Both are integrated intoORCHESTRATOR_CLOSEDand sub-job reporting tables.
- Budget priority chain:
- #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
progressevents viapublish_event.py, verified statically via AST analysis intest_s12). blockedhandling: Detection snapshot attached from herdr buffer; no automated response or key injection (fully conforms to Team Agreement Revision 1).idlehandling: Evaluates S seconds (MAM_STALL_IDLE_S, default 600s) of continuous idle, verifies no OS background descendants (excluding adapterresident_childrenand 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.
- Strictly notify-only: Contains zero code paths that close or fail a job (publishes only
Identity & Cleanliness
git rev-parse HEAD=1673818bb5fed14ed6fee87884e70d39e81fe91fgit rev-parse 'HEAD^{tree}'=00d5e03e2d521c87c3474c032f2af6f7a1af9e92git 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:
TIMEOUTis initialized to 3600, overridden by valid positiveMAM_JOB_TIMEOUT_S(with stderr warning on invalid input), and finally overridden by--timeout. - Loop Runner Propagation:
--job-timeoutvalidates positive integers.contract_args()passes--timeout <budget>to every delegated execution. - Wait Caps:
job_wait_scalculatesbudget + 300(respectingMAM_WAIT_GRACE_S).MAM_CHILD_WAIT_Sdefaults tojob_wait_sand respects explicit environment overrides. - Remote Runner:
remote.pylabels command timeouts and poll deadline expiration aswall_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 wrapperSUB_RC_*. - Close Paths:
FINALIZE_CLOCKcorrectly 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.pyCLI parser acceptswall_clock_timeout,engine.pymaps it toerror, andrunner.pyincludes it inORCHESTRATOR_CLOSED(closed_kindsafely strips trailing colon for remote runner format). - Compatibility:
cmd_wait()normalizesSUB_RC_WALL_CLOCKtoSUB_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 toclose/update_status, and all published events are strictly--event progress. - Herdr Queries: Relies on socket API
agent waitwithout aggressive polling. - Blocked State: Captures prompt text from
--source detection --lines 8and publishesstalled: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 likecodex-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_watchwith 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 forwait.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
.mamdirectories.
(6) Documentation & Versioning
- Both
multi-agent-mux-delegate-job/SKILL.mdandmulti-agent-mux-loop/SKILL.mdare 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
/procrequirement; verified clean on Linux testbeds.
tests/test_issue33_budget.py:- 19 / 20 PASSED offline;
test_b3live broker test verified PASSED (19.25s).
- 19 / 20 PASSED offline;
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 코어로 이전되어야 합니다:
finalize_unfinished의 종결 처리 및 시계 판정(FINALIZE_CLOCK, 디스크 유예 대기,wall_clock_timeoutvsdeadline_timeout매핑).MAM_JOB_TIMEOUT_S유효성 검증 및 기본값 연산.job_subscriber.pyexit code 매핑(_map_sub_rc) 및cmd_wait의 반환 코드 정규화.start_stall_watch/stop_stall_watch의 서브프로세스 감시 수명주기 관리.
5. Conclusion
PR #41은 설계 합의 사항을 충실히 반영하여 예산 제어, 타임아웃 라벨 분리, 비간섭형 멈춤 감시 시스템을 안정적으로 완성했습니다. 모든 회귀 테스트가 통과하였으며 아키텍처 경계 기준도 적절히 준수되었습니다.
[VERDICT: PASS]