# ๐Ÿ” Review Report โ€” Job 9b88790c (๋ธํƒ€ `ab842f1...263d6bf`) - **Reviewer**: `reviewer-grok-01` (role: reviewer) ยท 2026-09-14 - **๋ฒ”์œ„**: `git diff`/`git log` **`ab842f1...HEAD`** (๊ทธ ์ด์ „ ๋ธŒ๋žœ์น˜๋Š” PASS ๋ฒ”์œ„) - **HEAD**: `263d6bf` `fix(issue-18): let an exiting heartbeat timer skip the busy lifecycle lock; kill a timer whose pid cannot be recorded` - **์„ ํ–‰**: `2b1d590` ์žก๋ณ„ lifecycle flock - **์•ˆ์ „**: ์‹ค์ œ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท๋ชจ๋‹ˆํ„ฐยทpurge ๋ฏธ์ ‘์ด‰. ์†Œ์Šค ๋ฌด๋ณ€๊ฒฝ. `git diff --check ab842f1...HEAD` ํด๋ฆฐ. ## (1) ๋ฝ ์ˆœ์„œยท๊ต์ฐฉ ๋‘ flock: ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ/seq (`registry.lock`) vs ํƒ€์ด๋จธ ์†Œ์œ ๊ถŒ (`.heartbeat.lock`). - `start_timer` / `stop_timer` ๋Š” **lifecycle ๋งŒ** ์žก๋Š”๋‹ค. drain ๋Œ€๊ธฐ ์ค‘ seq lock ์„ ์•ˆ ์žก์œผ๋ฏ€๋กœ, ์ด๋ฏธ admit ๋œ beat ๊ฐ€ `next_seq` ๋กœ seq lock ์„ ์žก์„ ์ˆ˜ ์žˆ๋‹ค. ์ฃผ์„๋Œ€๋กœ drainร—seq ๊ต์ฐฉ์„ ํ”ผํ•œ๋‹ค. - ํ„ฐ๋ฏธ๋„: `raise_barrier`(seq lock, ํ•ด์ œ) โ†’ `stop_timer`(lifecycle). seq ๋ฅผ ์žก์€ ์ฑ„ lifecycle ์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. - **F1**: ์ข…๋ฃŒ ํƒ€์ด๋จธ๋Š” `lifecycle_lock(..., blocking=False)` (`heartbeat.py:472-478`). ๋ถ€๋ชจ๊ฐ€ drain ๋™์•ˆ lock ์„ ์ฅ๊ณ  ์žˆ์–ด๋„ ์ž์‹์€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋น ์ ธ๋‚˜๊ฐ„๋‹ค. lock ์„ ์–ป์œผ๋ฉด pidfile ์ด **์ž๊ธฐ pid** ์ผ ๋•Œ๋งŒ unlink. ๋ฐ”์œ lock ์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์Œ โ€” ํ™€๋”๊ฐ€ pidfile ์„ ์ง€์šด๋‹ค. - ์žฌํ˜„ ํ…Œ์ŠคํŠธ: `test_an_idle_timer_stops_gracefully_well_before_the_drain_deadline` (drain=10, ์ข…๋ฃŒ < 3s, SIGKILL ๊ฒฝ๊ณ  ์—†์Œ). ## (2) ๋™์‹œ start ยท barrier ยท pidfile ์‹คํŒจ - ๋™์‹œ `start_timer`: observe/drain/spawn/pidfile ์ด ํ•œ lock. `test_concurrent_start_timer_calls_leave_exactly_one_tracked_owner` โ€” ์ƒ์กด 1, pidfile ์ผ์น˜, stop ํ›„ ๋‘˜ ๋‹ค ์‚ฌ๋ง. - barrier/ํ„ฐ๋ฏธ๋„ ๋’ค: lock ์•„๋ž˜ ์žฌํ™•์ธ, arm ์•ˆ ํ•จ (`:318-320`). `test_start_timer_after_the_terminal_barrier_does_not_arm`. - **F2**: pidfile `open`/`write` ์‹คํŒจ ์‹œ ๊ทธ `Popen` ์„ kill+wait, ๋ถ€๋ถ„ pidfile ์€ ์ด pid ์ ‘๋‘์ผ ๋•Œ๋งŒ ์‚ญ์ œ, ์˜ˆ์™ธ ์žฌ๋ฐœ์ƒ, lock ์€ context `finally` ๋กœ ํ•ด์ œ (`:344-364`). spawn ์ž์ฒด ์‹คํŒจ๋Š” pidfile ์—†์Œ. ## (3) ์˜ˆ์™ธยท์ผ๊ด€์„ฑ `lifecycle_lock` ์€ ํš๋“ ํ›„์—๋งŒ UNLOCK. `BlockingIOError` ๋Š” yield False ํ›„ return (UNLOCK ์•ˆ ํ•จ). spawn ์‹คํŒจยทpidfile ์‹คํŒจ ๋ชจ๋‘ lock ํ•ด์ œ. ํ…Œ์ŠคํŠธ๊ฐ€ lock ์žฌํš๋“ `LOCK_NB` ๋กœ ํ™•์ธ. ## (4) ํ…Œ์ŠคํŠธ ๊ฒฐ์ •์„ฑ ์‹ ๊ทœ: ๋™์‹œ start, barrier ํ›„ ๋ฏธarm, idle ์ •์ƒ ์ข…๋ฃŒ, pidfile ENOSPC, spawn ์‹คํŒจ. ๋ชจ๋‘ ํ”„๋กœ์„ธ์Šค finally ํšŒ์ˆ˜. ๋™์‹œ start ๋Š” ์ธํ„ฐ๋ฆฌ๋น™์„ ๊ฐ•์ œํ•˜์ง€ ์•Š์œผ๋‚˜ lock ์ด ์ง๋ ฌํ™”ํ•˜๋ฏ€๋กœ ํ•œ ์†Œ์œ ์ž๋ฉด ์ถฉ๋ถ„. ## (5) ๋ฌธ์„œยท๋ฝ์Šคํ… MESSAGING ยง2.5 ๊ฐ€ lifecycle ์ง๋ ฌํ™”ยทnon-blocking ์ž์‹ ์ •๋ฆฌยทpidfile ์‹คํŒจ ์‹œ ์‚ด์ฒ˜๋ถ„์„ ๋ฐ˜์˜. VERSIONS **31** = collect 31. **4.5.0** ์œ ์ง€ (`lib.sh`/`eager.py`). ## ํ…Œ์ŠคํŠธ ์‹คํ–‰ ``` env -u MAM_VERSION .venv/bin/python -m pytest -q \ tests/test_issue18_liveness_contract.py \ tests/test_issue5_phase1_delegation.py \ tests/test_version_consistency.py ``` **100 passed in 127.96s** (issue18 31 + phase1 + version). ๋ธŒ๋กœ์ปค ์˜์กด ๊ฑด ์ด๋ฒˆ ์‹คํ–‰ skip ์—†์Œ. ## ์ข…ํ•ฉ F1 ๊ต์ฐฉ๊ณผ F2 ๋ฏธ์ถ”์  ํƒ€์ด๋จธ๊ฐ€ ๋‹ซํ˜”๊ณ , seq lock ๊ณผ lifecycle lock ์˜ ์ˆœ์„œ๋Š” drain ๊ณผ ์–‘๋ฆฝํ•œ๋‹ค. ์„ค๊ณ„ ์žฌ์ž‘์—… ๋ถˆํ•„์š”. [VERDICT: PASS]