# ๐Ÿ” 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]