# ๐Ÿ“œ Multi-Agent Mux ๋ฒ„์ „ ์ด๋ ฅ (`VERSIONS.md`) ์ด ๋ฌธ์„œ๋Š” `multi-agent-mux` ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋ฒ„์ „๋ณ„ ์ฃผ์š” ๊ธฐ๋Šฅ ์ถ”๊ฐ€, ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„ , ๋ฒ„๊ทธ ์ˆ˜์ • ๋ฐ ํ’ˆ์งˆ ๊ฒ€์ฆ ์ด๋ ฅ์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. --- ## ๐Ÿ“Œ ํ˜„์žฌ ๋ฒ„์ „ ๊ฐœ์š” (Current Release) - **ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒ„์ „**: `v4.9.0` - **์ตœ์‹  ๋ฆด๋ฆฌ์Šค ์ผ์‹œ**: 2026-10-12 (KST) - **๊ธฐ์ค€ ๋ธŒ๋žœ์น˜**: `main` - **ํ•ต์‹ฌ ์•„ํ‚คํ…์ฒ˜**: - **Terminal ์ƒํƒœ ๋‹จ๋ฐฉํ–ฅ ๋ถˆ๋ณ€์„ฑ (`engine.py`, Issue #6 Phase 1)**: `completed`, `error`, `cancelled` ํ„ฐ๋ฏธ๋„ ์ƒํƒœ ์ง„์ž… ํ›„ ํ›„์† ์ด๋ฒคํŠธ๋‚˜ ์—…๋ฐ์ดํŠธ์— ์˜ํ•œ ๋ฎ์–ด์“ฐ๊ธฐ๋ฅผ ์—„๊ฒฉํžˆ ์ฐจ๋‹จ(Terminal Immutability). `registry.py close` ์„œ๋ธŒ์ปค๋งจ๋“œ ๋ฐ fail-closed ์กฐ๊ธฐ ์ข…๋ฃŒ ๋„์ž…. - **Herdr ํ„ด ์™„๋ฃŒ ํ•ธ๋“œ์…ฐ์ดํฌ (`lib.sh: wait_for_agent_idle`, Issue #6 Phase 2)**: ์œ„์ž„ ์—์ด์ „ํŠธ(`claude`, `agy`, `hermes`, `codex` ๋“ฑ)์˜ ํ”„๋กฌํ”„ํŠธ ์ „์†ก ํ›„ Herdr ๋ทฐํฌํŠธ ๋ฐ ํ”„๋กœ์„ธ์Šค ์ƒํƒœ๋ฅผ ๊ด€์ธกํ•˜์—ฌ ๋น„๋™๊ธฐ ํ„ด ์ข…๋ฃŒ๋ฅผ ๊ฒฐ์ •๋ก ์ ์œผ๋กœ ๋Œ€๊ธฐํ•˜๋Š” ํ•ธ๋“œ์…ฐ์ดํฌ ํ”„๋กœํ† ์ฝœ ํ™•๋ฆฝ. - **Loop ๋ชจ๋“œ `attempt_id` ์ถ”์  ๋ฐ ์•ˆ์ „ ์ „์ด (Issue #6 Phase 3)**: ์›Œ์ปค/๋ฆฌ๋ทฐ์–ด ์ „ํ™˜ ์‹œ ๋™์ผ ํšŒ์ฐจ ๋‚ด ์žฌ์ „์ด ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๋Š” `attempt_id` ๊ทœ์•ฝ ์ ์šฉ ๋ฐ 4๋‹จ๊ณ„ fail-closed `finalize_unfinished` ๋ณด์žฅ. - **๊ฒฐ์ •๋ก ์  ์ข…๋ฃŒ ์ฝ”๋“œ ๊ณ„์•ฝ (Issue #6 Phase 4 & R1)**: `0` (Success), `1` (Failure / Inject error), `2` (Subscriber timeout; ๋‹จ, ์—์ด์ „ํŠธ๊ฐ€ Herdr์—์„œ working ์ค‘์ด๋ฉด ๋ฒฝ์‹œ๊ณ„ --timeout๊นŒ์ง€ ๋””์Šคํฌ ์žฌ๊ด€์ธก ํ›„ fail-close), `3` (Broker unavailable), `5` (Precondition failed / session_busy) ํ‘œ์ค€ํ™”. (send_keys_safe ํ—ฌํผ์˜ ๋ฐฐ๋‹ฌ ์‹คํŒจ/busy ํƒ€์ž„์•„์›ƒ rc=4๋Š” ๋ž˜ํผ ์ฐจ์›์—์„œ rc=1 ์‹คํŒจ๋กœ ์ˆ˜์šฉ). - **ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ฐ˜ ์ƒ์กดยท์ „๋‹ฌ ๊ณ„์•ฝ (Issue #18)**: heartbeat ์˜๋ฌด(N, ์ž๋™ ํƒ€์ด๋จธ) ยท ๋ฌด์‘๋‹ต ํŒ์ •(Kร—N) ยท ์ „๋‹ฌ ํ™•์ธ(T ๋‚ด `started`) ยท retained `/state` ยท ์›๊ฒฉ ์ „์†ก(`remote:`). TUI ์Šคํฌ๋ž˜ํ•‘์€ ์ฃผ์ž…ยท๋‹ค์ด์–ผ๋กœ๊ทธยท์ง„๋‹จ์—๋งŒ ๋‚จ๋Š”๋‹ค. - **์›ŒํฌํŠธ๋ฆฌ ์šด์˜ ๋ชจ๋ธ 2๋‹จ๊ณ„ (Issue #21 ํ•ญ๋ชฉ 4ยท5ยท7)**: ์›ŒํฌํŠธ๋ฆฌ ์ค€๋น„ยทํ•ด์ฒด ํ—ฌํผ(๋ณ€๊ฒฝ ์ „ ํŒ์ •, untracked ํฌํ•จ, ๊ฒ€์ฆ๋œ ๋‹จ์ผ `--force`), ๋ฆฌ๋ทฐ identity(๋ผ์šด๋“œ๋ณ„ ์Šค๋ƒ…์ƒทยทstale ์žฌ๋ฆฌ๋ทฐยท์Šน๊ฒฉ ์ง์ „ ์žฌํ™•์ธยท`identity.json`), ์ˆ˜์šฉ ์‚ฌ๋ก€ A1โ€“A8. - **์žก ์˜ˆ์‚ฐยท๋ผ๋ฒจยท๋ฉˆ์ถค ์•Œ๋ฆผ (Issue #33ยท#30)**: ์œ„์ž„ ์žก ์˜ˆ์‚ฐ์€ `--job-timeout`/`MAM_JOB_TIMEOUT_S`, ๋ฒฝ์‹œ๊ณ„ ์ดˆ๊ณผ๋Š” `wall_clock_timeout`(๋ฌด์‘๋‹ต `deadline_timeout` ๊ณผ ๊ตฌ๋ถ„), `lib_py/stall_watch.py` ๊ฐ€ `blocked`/`idle` ์„ `progress stalled:*` ๋กœ ์•Œ๋ฆฌ๊ธฐ๋งŒ ํ•œ๋‹ค(์ข…๊ฒฐ ๊ทผ๊ฑฐ๋Š” #18 ์˜ ํ”„๋กœํ† ์ฝœ ์ด๋ฒคํŠธ๋ฟ). - **์„ค์ • ํŒŒ์ผ ๋กœ๋” ํ•˜๋‚˜ (Issue #26)**: `.mam.env` ๋Š” `lib_py/delegation/envfile.py` ํ•œ ๊ณณ์ด ์ฝ๋Š”๋‹ค(์…ธ ํ•ด์„ ์—†์Œ, ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์šฐ์„ , ์ฝ์„ ์ˆ˜ ์—†๋Š” ํŒŒ์ผ์€ ์˜ค๋ฅ˜). bash ์‚ฐ์ˆ ์— ๋“ค์–ด๊ฐ€๋Š” ์ •์ˆ˜ ์„ค์ •๊ฐ’์€ `mam_int` ๋กœ 10์ง„์ˆ˜. - **4-Way ๋ฒ„์ „ ๋ฝ์Šคํ…**: `lib.sh`(`MAM_VERSION`) โ†” `lib_py/loop/eager.py`(`FRAMEWORK_VERSION`) โ†” `VERSIONS.md` โ†” 8๊ฐœ `SKILL.md`. --- ## ๐Ÿงญ ์Šคํ‚ฌ ํŒจํ‚ค์ง€ ๋ฒ„์ „ ๋งคํŠธ๋ฆญ์Šค (Skills Version Matrix) ๋ชจ๋“  8๊ฐœ ์Šคํ‚ฌ์€ YAML frontmatter ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(`author`, `version`, `platforms`, `environments`) ํ‘œ์ค€ํ™”๋ฅผ ํ†ตํ•ด `v4.9.0`๋กœ ๋™๊ธฐํ™”๋˜์–ด ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. | ์Šคํ‚ฌ๋ช… | ๋ฒ„์ „ | ์—ญํ•  ๋ฐ ์ฃผ์š” ์ฑ…์ž„ | ์ƒํƒœ | | :--- | :---: | :--- | :---: | | **`multi-agent-mux-create`** | `4.9.0` | ์—์ด์ „ํŠธ ์„ธ์…˜ ์‹ ๊ทœ ์ƒ์„ฑ ๋ฐ Herdr ์ปจํ…Œ์ด๋„ˆ ๊ฒฉ๋ฆฌ ์Šคํฐ | โœ… ๋ฐฐํฌ | | **`multi-agent-mux-stop`** | `4.9.0` | ๋Œ€ํ™” UUID ์›์ž์  ์บก์ฒ˜ ๋ฐ ์„ธ์…˜ ์•ˆ์ „ ์ข…๋ฃŒ (Graceful Stop) | โœ… ๋ฐฐํฌ | | **`multi-agent-mux-resume`** | `4.9.0` | ์˜จ๋””์Šคํฌ ๋Œ€ํ™” ์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ Tier-1 ์ดˆ๊ณ ์† ์„ธ์…˜ ๋ณต์› | โœ… ๋ฐฐํฌ | | **`multi-agent-mux-status`** | `4.9.0` | ์‹ค์‹œ๊ฐ„ Herdr ์„ธ์…˜ ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋“œ๋ฆฌํ”„ํŠธ ์Šค๋ƒ…์ƒท ์กฐํšŒ | โœ… ๋ฐฐํฌ | | **`multi-agent-mux-monitor`** | `4.9.0` | YAML โ†” ๋Ÿฐํƒ€์ž„ ์ƒํƒœ ๊ฐ„ ์ž์œจ ์กฐ์ •์ž (Reconciler Loop) | โœ… ๋ฐฐํฌ | | **`multi-agent-mux-delegate-job`** | `4.9.0` | MQTT ์ด๋ฒคํŠธ ์ฑ„๋„ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ๋‹จ์œ„ ์ž‘์—… ์œ„์ž„ | โœ… ๋ฐฐํฌ | | **`multi-agent-mux-loop`** | `4.9.0` | Planner-Creator-Reviewer 3์ž ์ž์œจ ๊ณ„ํšยท์‹คํ–‰ยทํ”ผ์–ด๋ฆฌ๋ทฐ ๋ฃจํ”„ | โœ… ๋ฐฐํฌ | | **`multi-agent-mux-orc-onboard`** | `4.9.0` | ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ UUID ๊ฒฉ๋ฆฌ ๋“ฑ๋ก ๋ฐ ์„œ๋ธŒ ์„ธ์…˜ ์˜ค์—ผ ๋ฐฉ์ง€ | โœ… ๋ฐฐํฌ | --- ## ๐Ÿ“‹ ๋ฒ„์ „๋ณ„ ์ƒ์„ธ ๋ณ€๊ฒฝ ๋‚ด์—ญ (Changelog) ### ๐Ÿš€ `v4.9.0` โ€” Issue #50ยท#55: ์˜จ๋ณด๋”ฉยท์œ„์ž„์ด ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋กœ ๊ธฐ๋‹ค๋ฆฌ๊ณ , pane ๋ฐฐ์น˜๋Š” ์„œ๋ฒ„๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ, ํ…Œ์ŠคํŠธ๋Š” ์‹ค์ œ ์—์ด์ „ํŠธ ํ™ˆ์— ์“ฐ์ง€ ์•Š๋Š”๋‹ค (2026-10-12) > **๋งˆ์ด๋„ˆ (MINOR Release)**: ์ƒˆ ์ข…๋ฃŒ ์ฝ”๋“œ(`create --onboard` ์˜ 44ยท45)์™€ ์ƒˆ ๋Œ€๊ธฐ ๋™์ž‘(์œ„์ž„ ๋ž˜ํผ์˜ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋Œ€๊ธฐ, pane ๋ฐฐ์น˜ ์ž ๊ธˆ). PR #56. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **#55 ํ…Œ์ŠคํŠธ ๊ฒฉ๋ฆฌ**: lib.sh ๊ฐ€ ์—์ด์ „ํŠธ pane ์— ๋‚ด๋ณด๋‚ด๋Š” `CLAUDE_PROJECT_DIR` ๋“ฑ์„ ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ์ง€์šฐ๋Š” autouse ํ”ฝ์Šค์ฒ˜(`tests/conftest.py`)์™€, ์‹คํ–‰ ์ค‘ ์‹ค์ œ ์—์ด์ „ํŠธ ํ™ˆ์— ์ƒˆ ํ•ญ๋ชฉ์ด ์ƒ๊ธฐ๋ฉด ์„ธ์…˜์„ ์‹คํŒจ์‹œํ‚ค๋Š” ์ฝ๊ธฐ ์ „์šฉ ๊ฐ€๋“œ(`tests/_agent_home_guard.py`). ์šด์˜์šฉ lib.sh export ๋Š” ๊ทธ๋Œ€๋กœ๋‹ค. 2. **#50 A โ€” `create --onboard` ๊ฐ€ ์˜จ๋ณด๋”ฉ ์žก์˜ ์ข…๊ฒฐ์„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค**(`worktree_prepare.sh --onboard` ๋„): ์ƒํ•œ `MAM_ONBOARD_WAIT_S`(๊ธฐ๋ณธ 600 s). ์ƒˆ ์ข…๋ฃŒ ์ฝ”๋“œ **44**(์˜จ๋ณด๋”ฉ์ด error/cancelled ๋กœ ๋๋‚จ)ยท**45**(์‹œ๊ฐ„ ์•ˆ์— ๋๋‚˜์ง€ ์•Š์Œ); ๋‘˜ ๋‹ค ์„ธ์…˜์„ ๋‚จ๊ธฐ๊ณ  ํ–‰์— `last_visible_status: onboarding-` ๋ฅผ ์“ด๋‹ค. 3. **#50 C โ€” ์œ„์ž„ ๋ž˜ํผ๊ฐ€ ์ฃผ์ž… ์ „์— ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋กœ ๊ธฐ๋‹ค๋ฆฐ๋‹ค**: ๋Œ€์ƒ ์„ธ์…˜์˜ ์•ž์„  ์—ด๋ฆฐ ์žก(์˜จ๋ณด๋”ฉ ํฌํ•จ)์ด ๋๋‚  ๋•Œ๊นŒ์ง€ `MAM_DISPATCH_WAIT_S` ๋งŒํผ; ๋๋‚˜์ง€ ์•Š์œผ๋ฉด ์ „์ฒ˜๋Ÿผ `cancelled`(session_busy), ์ข…๋ฃŒ ์ฝ”๋“œ 5. ์„ธ์…˜๋‹น ํ•œ ๋ฒˆ์— ํ•œ ์žก๋งŒ ์Šน์ธ(๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ž ๊ธˆ ์•„๋ž˜ ๊ธฐ๋ก), herdr idle ๋Œ€๊ธฐ๋Š” ๋ณด์กฐ ํ™•์ธ์œผ๋กœ๋งŒ ๋‚จ๋Š”๋‹ค. `submit --dry-run` ๋ ˆ์ฝ”๋“œ๋Š” ์ฃผ์ž…๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ pending ์ธ ๋™์•ˆ ์„ธ์…˜์„ ์žก์ง€ ์•Š๋Š”๋‹ค(pick-pending ์ด ๊ฐ€์ ธ๊ฐ€๋ฉด ์žก๋Š”๋‹ค). 4. **๊ฐ™์€ ํด๋” ๋™์‹œ create**: pane ๋ฐฐ์น˜๋ฅผ herdr ์„œ๋ฒ„๋งˆ๋‹ค `lib_py/pane_lock.py`(fd 9 ์˜ `fcntl.flock`, macOS ์—๋Š” flock(1) ์ด ์—†์Œ)๋กœ ์ง๋ ฌํ™”ํ•œ๋‹ค. ๋Œ€๊ธฐ ์ƒํ•œ `MAM_PANE_CLAIM_WAIT_S`, ๋ณด์œ ์ž๊ฐ€ ์ฃฝ์œผ๋ฉด ํ’€๋ฆฐ๋‹ค. create ๋Š” monitor ๊ฐ€ ์ž๊ธฐ ์„œ๋ฒ„์— ๋“ฑ๋กํ•ด ๋‘” ํ–‰๋งŒ ์ด์–ด๋ฐ›๊ณ , monitor ๋Š” `default` ์กฐํšŒ์— ์‹ค์ œ๋กœ ๋‹ตํ•œ ์„œ๋ฒ„(์ƒ์†๋œ `HERDR_SESSION_NAME`)๋ฅผ ํ–‰์— ์ ๋Š”๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **PR ์ž‘์„ฑ์ž ๊ฒ€์ฆ (LinuxยทPython 3.14ยทherdr 0.9.3)**: 320aa9f ์ „์ฒด ์Šค์œ„ํŠธ 1864 passed / 12 failed(#28 ์˜ ์•Œ๋ ค์ง„ `test_issue10_13_purge.py` ๊ธฐ์ค€์„ ) / 20 skipped, ๊ฐ™์€ ํด๋” ๋™์‹œ `create --onboard` 3 ๊ฐœ ์‹ค์ œ ์‹œํ—˜ ๋ชจ๋‘ exit 0. ๋ฆฌ๋ทฐ ๋ฃจํ”„ 3 ๊ฐœ, 320aa9f ์—์„œ ๋งŒ์žฅ์ผ์น˜ PASS. - **๋ณ‘ํ•ฉ ์ „ macOS ๊ฒ€์ฆ (macOSยทPython 3.9ยทbash 3.2ยทherdr 0.9.3)**: 320aa9f ์—์„œ macOS ์ „์šฉ ์‹คํŒจ๋ฅผ ๊ณ ์นจ(a9ffeda: AF_UNIX ๊ฒฝ๋กœ 104 ๋ฐ”์ดํŠธ, tty canonical 1024 ๋ฐ”์ดํŠธ, bash 3.2 ์˜ ๋งˆ์ง€๋ง‰ ๋ช…๋ น ๋ฏธ-exec, `_adopted_own` ์˜ ์„œ๋ฒ„ ํ™•์ธ, ๊ฒฝํ•ฉ๋งŒ ์žฌ์‹œ๋„ํ•˜๋Š” flock, dry-run ๋ ˆ์ฝ”๋“œ). ๋ฆฌ๋ทฐ 1ํšŒ์ฐจ 4๋ช… ๋ชจ๋‘ NOT PASS(macOS ์‹คํŒจ ์ˆ˜์ •ยทdry-runยท์„œ๋ฒ„ ํ™•์ธยทflock ๋ฒ”์œ„), 2ํšŒ์ฐจ codex F1(๊ฐ€์ ธ๊ฐ„ dry-run ์žก์€ ์„ธ์…˜์„ ์žก๋Š”๋‹ค, efd27de), 3ํšŒ์ฐจ 4/4 PASS(๋ฆฌ๋ทฐ ์ค‘ ์›ŒํฌํŠธ๋ฆฌ ์ˆ˜์ •์œผ๋กœ ๋ฃจํ”„๊ฐ€ ํ๊ธฐ), @21f0c12(monitor ์„œ๋ฒ„ ํ‘œ๊ธฐ) **4/4 PASS**: 4ํšŒ์ฐจ์—์„œ claudeยทagyยทgrok PASS, codex ๋Š” ์ŠคํŠธ๋ฆผ ๋Š๊น€์œผ๋กœ ํŒ์ • ์—†์ด ์ค‘๋‹จ๋˜์–ด 5ํšŒ์ฐจ ๋‹จ๋… ์žฌ๋ฆฌ๋ทฐ์—์„œ PASS. ๋ฆฌํฌํŠธ๋Š” `.agents/reports//`(๋ฃจํ”„๊ฐ€ ์Šน๊ฒฉํ•œ codex ๋ฆฌํฌํŠธ๋งŒ identity ์‚ฌ์ด๋“œ์นด๊ฐ€ ์žˆ๊ณ , ๋‚˜๋จธ์ง€ ์…‹์€ 4ํšŒ์ฐจ ์žก์˜ report-final ์„ ๋ณต์‚ฌ). - **์ „์ฒด ์Šค์œ„ํŠธ (21f0c12, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด, `CLAUDE_PROJECT_DIR` ์„ค์ •)**: **1860 passed / 0 failed / 38 skipped**(75 ๋ถ„). ์‹คํ–‰ ๋’ค `~/.claude/projects` ์— ์ƒˆ ํ•ญ๋ชฉ ์—†์Œ. ### ๐Ÿš€ `v4.8.9` โ€” ํ…Œ์ŠคํŠธ๊ฐ€ ์ฒดํฌ์•„์›ƒ์˜ ์‹ค์ œ ๋ฃจํ”„ ์ž ๊ธˆ์„ ์žก์ง€ ์•Š๋Š”๋‹ค (2026-10-09) > **ํŒจ์น˜ (PATCH Release, ํ…Œ์ŠคํŠธ ์ „์šฉ)**: v4.8.8 ์„ผํ‹ฐ๋„ ์‹คํ–‰ ๋’ค ๋ฆฌ๋ทฐ ์›ŒํฌํŠธ๋ฆฌ์— ๋นˆ `.mam/` ์ด ๋‚จ์€ ์›์ธ. ์ œํ’ˆ ์ฝ”๋“œ(`.agents/skills`)๋Š” ๋ฒ„์ „ ์ค„ ์™ธ์— ๋ฐ”๋€Œ์ง€ ์•Š์•˜๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **์›์ธ**: `tests/test_o3_scoped_guard.py` ์˜ `test_b13_reexec_preserves_original_argv`ยท`test_b13_no_freeze_switch_disables_reexec` ๋Š” ์ฒดํฌ์•„์›ƒ์˜ `run_loop.sh` ๋ฅผ `--creator` ์™€ ํ•จ๊ป˜ ์‹คํ–‰ํ•ด ์ธ์ž ํ•ด์„์„ ์ง€๋‚˜ `mam_acquire_loop_lock` ๊นŒ์ง€ ๊ฐ„๋‹ค. ์ž ๊ธˆ ์œ„์น˜ ๊ธฐ๋ณธ๊ฐ’์€ `$MAM_REAL_ROOT/.mam/loop-guard-active` ์ด๊ณ  `MAM_REAL_ROOT` ๋Š” ์Šคํฌ๋ฆฝํŠธ ์ž์‹ ์˜ ์ฒดํฌ์•„์›ƒ์ด๋ผ `WORKSPACE_ROOT` ๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๋Š”๋‹ค. ์„ธ์…˜์ด ์—†์–ด ๊ณง ๋๋‚˜๋ฉฐ marker ๋Š” ์ง€์šฐ์ง€๋งŒ `.mam/` ์€ ๋‚จ๋Š”๋‹ค. ๋ณธ ์ฒดํฌ์•„์›ƒ์—์„œ๋Š” ๊ทธ ์ˆœ๊ฐ„ ์‹ค์ œ ๋ฃจํ”„์˜ ์ž ๊ธˆ์„ ์žก์•˜๋‹ค ๋†“๋Š”๋‹ค(์‹ค์ œ ๋ฃจํ”„๊ฐ€ ๊ฑฐ๋ถ€๋˜๊ฑฐ๋‚˜ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฑฐ๋ถ€๋จ). 2. **์ˆ˜์ •**: ๋‘ ํ…Œ์ŠคํŠธ๊ฐ€ `MAM_LOOP_MARKER` ๋ฅผ ์ž๊ธฐ `tmp_path` ์•„๋ž˜๋กœ ๋‘”๋‹ค. `run_loop.sh` ๋ฅผ ์“ฐ๋Š” 16 ๊ฐœ ๋ชจ๋“ˆ์„ ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ์ฒดํฌ์•„์›ƒ์˜ ์ƒˆ `.mam` ์„ ๊ฐ์‹œํ•˜๋ฉฐ ํ›‘์—ˆ๊ณ , ๋‚˜๋จธ์ง€๋Š” ์ด๋ฏธ marker ๋ฅผ ์ง€์ •ํ•˜๊ฑฐ๋‚˜ ์ž ๊ธˆ ์ „์— ๋๋‚˜๊ฑฐ๋‚˜(`--help`, ์ธ์ž ์˜ค๋ฅ˜) ๋ณต์‚ฌ๋ณธ ํŠธ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค. ์ž ๊ธˆ ๋™์ž‘์„ ์‹œํ—˜ํ•˜๋Š” ํ…Œ์ŠคํŠธ(`test_o2_race_free_lock` ๋“ฑ)๋Š” ๊ทธ๋Œ€๋กœ๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **ํšŒ๊ท€ ํ…Œ์ŠคํŠธ**: `tests/test_loop_marker_isolation.py` L1 โ€” ์ฒดํฌ์•„์›ƒ์˜ `run_loop.sh` ๋กœ ์ž ๊ธˆ๊นŒ์ง€ ๊ฐ€๋Š” ํ…Œ์ŠคํŠธ๋“ค์„ ์ฒดํฌ์•„์›ƒ ๋ณต์‚ฌ๋ณธ์—์„œ ๋Œ๋ ค `.mam` ์ด ์ƒ๊ธฐ์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค. v4.8.8 ์˜ W1(`tests/test_issue17_workspace_root_isolation.py`)์€ ํŒŒ์ผ๋งŒ ๋ณด์•„ ๋นˆ ์ž ๊ธˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋†“์ณค์œผ๋ฏ€๋กœ ์ด์ œ `.mam` ์ž์ฒด๊ฐ€ ์—†์–ด์•ผ ํ•œ๋‹ค. 97c2946 ์—์„œ ๋‘˜ ๋‹ค ์‹คํŒจ. - **๋ฆฌ๋ทฐ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ๋ฃจํ”„ `--all-reviewer`)**: 1ํšŒ์ฐจ(@a93c9bf) **4/4 PASS**. ๋ฆฌํฌํŠธ์™€ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, macOSยทPython 3.9ยทherdr 0.9.3, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด, `WORKSPACE_ROOT` = ๋นˆ ์„ผํ‹ฐ๋„)**: a93c9bf **1752 passed / 0 failed / 38 skipped**(73 ๋ถ„). ์‹คํ–‰ ๋’ค ์„ผํ‹ฐ๋„์€ ๋น„์—ˆ๊ณ , ๋ฆฌ๋ทฐ ์›ŒํฌํŠธ๋ฆฌ์— `.mam/` ์ด ์—†์—ˆ๊ณ , ๋ณธ ์ฒดํฌ์•„์›ƒ์˜ `.mam/shim/herdr` ์ˆ˜์ • ์‹œ๊ฐ์€ ๊ทธ๋Œ€๋กœ์˜€๋‹ค. ### ๐Ÿš€ `v4.8.8` โ€” Issue #17 ํ›„์†: lib.sh ๋ฅผ source ํ•˜๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์ƒ์†๋œ `WORKSPACE_ROOT` ์— ์“ฐ์ง€ ์•Š๋Š”๋‹ค (2026-10-09) > **ํŒจ์น˜ (PATCH Release, ํ…Œ์ŠคํŠธ ์ „์šฉ)**: v4.8.7 ๋ฆฌ๋ทฐ(claude ๋น„์ฐจ๋‹จ)๊ฐ€ ๋‚จ๊ธด ํ›„์†. ์ œํ’ˆ ์ฝ”๋“œ(`.agents/skills`)๋Š” ๋ฐ”๋€Œ์ง€ ์•Š์•˜๋‹ค. lib.sh ๋Š” source ๋  ๋•Œ๋งˆ๋‹ค `$WORKSPACE_ROOT/.mam/shim/herdr` ๋ฅผ ๋‹ค์‹œ ์“ฐ๋Š”๋ฐ(`_init_herdr_isolation`), ์…ธ ํ™˜๊ฒฝ์„ ๊ทธ๋Œ€๋กœ ๋ฌผ๋ ค๋ฐ›๋Š” ํ…Œ์ŠคํŠธ๋Š” ๊ทธ ๊ฐ’์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ค์ œ ์›Œํฌ์ŠคํŽ˜์ด์Šค(์—์ด์ „ํŠธ pane ์—์„œ๋Š” ๊ทธ pane ์˜ ์›Œํฌ์ŠคํŽ˜์ด์Šค, PR #51 ๋ฆฌ๋ทฐ ๋•Œ๋Š” ๋‹ค๋ฅธ ์›Œํฌ์ŠคํŽ˜์ด์Šค)์˜ shim ์„ ๋ฎ์–ด์ผ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **item-4 ํ—ฌํผ (`tests/test_issue17_item4_decisions.py`)**: `_env` ๊ฐ€ `WORKSPACE_ROOT` ๋ฅผ ํ…Œ์ŠคํŠธ์˜ ์ž„์‹œ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ง€์ •ํ•œ๋‹ค. ์ง€์šฐ๊ธฐ๋งŒ ํ•˜๋ฉด lib.sh ๊ฐ€ ์ฒดํฌ์•„์›ƒ ์ž์‹ ์„ ๋ฃจํŠธ๋กœ ๊ณ„์‚ฐํ•˜๊ณ , conftest ๊ฐ€ ์ด ์ฒดํฌ์•„์›ƒ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ’์„ ๋‚จ๊ฒจ ๋‘๋ฏ€๋กœ ์ฒดํฌ์•„์›ƒ์˜ shim(๋ณธ ์ฒดํฌ์•„์›ƒ์ด๋ฉด ์‹ค์ œ shim)์„ ์“ฐ๊ฒŒ ๋œ๋‹ค. 2. **๊ณตํ†ต fixture (`tests/conftest.py`)**: `own_workspace_root` โ€” `WORKSPACE_ROOT` ๋ฅผ `/.mam-test-state/workspace` ๋กœ ๋‘”๋‹ค. autouse ๊ฐ€ ์•„๋‹ˆ๋‹ค(runner.pyยทmqtt_common ์€ ํ”„๋กœ์„ธ์Šค ์•ˆ์—์„œ ๊ทธ ๊ฐ’์„ ์ฝ๊ณ , ์ผ๋ถ€ ํ…Œ์ŠคํŠธ๋Š” ์ผ๋ถ€๋Ÿฌ ์ง€์šด๋‹ค). `mam_sandbox` ์™€ ๋ช…์‹œ env ๊ฐ€ ๋’ค์—์„œ ์ด๊ธด๋‹ค. 3. **๊ฐ™์€ ๊ฒฐํ•จ์˜ ๋ชจ๋“ˆ๋“ค**: `test_version_consistency`, `test_b19_headless_reconcile_fixes`, `test_b8_send_keys_verification`, `test_issue6_fail_closed`, `test_loop_cli`, `test_issue5_phase3_loop`, `test_issue5_phase4_runtime`, `test_o3_scoped_guard`, `test_issue25_plan_reuse`, `test_orc_onboard`(import ์‹œ์ ์˜ `SCRUBBED_ENV` ๊ฐ€ ๋”๋Š” `WORKSPACE_ROOT` ๋ฅผ ๋‹ด์ง€ ์•Š๊ณ  ํ˜ธ์ถœ๋งˆ๋‹ค ๊ทธ ํ˜ธ์ถœ์˜ ๋ฃจํŠธ๋กœ env ๋ฅผ ๋งŒ๋“ ๋‹ค), `test_o1_rebuttal`, `test_o2_race_free_lock` ์€ fixture ๋ฅผ ์“ฐ๊ณ , `test_workspace_scope`ยท`test_issue21_worktree_prereqs._mutate` ๋Š” ์ด๋ฏธ ๋งŒ๋“  ๋ฃจํŠธ๋ฅผ ๊ณ ์ •ํ•œ๋‹ค. lib.shยทrun_loop.shยท๋ž˜ํผยท์Šคํ‚ฌ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋‚˜๋จธ์ง€ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์€ ํ‘œ์‹œ๋˜์—ˆ๊ฑฐ๋‚˜ `mam_sandbox`/๊ณ ์ • ๋ฃจํŠธ๋ฅผ ์“ฐ๊ฑฐ๋‚˜ lib.sh ๋ฅผ source ํ•˜์ง€ ์•Š๋Š”๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **ํšŒ๊ท€ ํ…Œ์ŠคํŠธ (`tests/test_issue17_workspace_root_isolation.py` ์™ธ)**: ์„ผํ‹ฐ๋„ `WORKSPACE_ROOT` ์•„๋ž˜ item-4 ํ—ฌํผ๊ฐ€ ์•„๋ฌด๊ฒƒ๋„ ๋งŒ๋“ค์ง€ ์•Š์Œ; W1 โ€” ์ฒดํฌ์•„์›ƒ ๋ณต์‚ฌ๋ณธ์—์„œ ์œ„ ๋ชจ๋“ˆ๋งˆ๋‹ค ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ๊ทธ ๋ณต์‚ฌ๋ณธ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” `WORKSPACE_ROOT` ๋กœ ๋Œ๋ ค ๋ณต์‚ฌ๋ณธ์— `.mam` ์ด ์ƒ๊ธฐ์ง€ ์•Š์Œ; W2 โ€” `test_orc_onboard` ๋ฅผ ์„ผํ‹ฐ๋„ ๋ฃจํŠธ๋กœ import ํ•œ ๋’ค ๋‹ค๋ฅธ ๋ฃจํŠธ๋กœ ํ˜ธ์ถœํ•ด๋„ ์„ผํ‹ฐ๋„์ด ๋น”. ์ˆ˜์ • ์ „ ์ปค๋ฐ‹์—์„œ๋Š” ๋ชจ๋‘ ์‹คํŒจ(`.mam/shim/herdr` ์ƒ์„ฑ). - **๋ฆฌ๋ทฐ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ๋ฃจํ”„ `--all-reviewer`)**: 1ํšŒ์ฐจ(@d198dd5) claudeยทagy PASS, grokยทcodex NOT PASS(๊ฐ™์€ ๊ฒฐํ•จ์ด ๋‹ค๋ฅธ ๋ชจ๋“ˆ 11 ๊ณณ์— ๋‚จ์Œ, `test_orc_onboard` ์˜ import ์‹œ์  env). 8b47cc7 ์—์„œ ์ˆ˜์ •. 2ํšŒ์ฐจ agyยทcodex PASS, claudeยทgrok NOT PASS(`test_o2_race_free_lock`ยท`test_o1_rebuttal` ๋ˆ„๋ฝ). ac48cb7 ์—์„œ ์ˆ˜์ •. 3ํšŒ์ฐจ(@ac48cb7) **4/4 PASS**. ๋ฆฌํฌํŠธ์™€ 3ํšŒ์ฐจ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, macOSยทPython 3.9ยทherdr 0.9.3, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด, `WORKSPACE_ROOT` = ๋นˆ ์„ผํ‹ฐ๋„ ๋””๋ ‰ํ„ฐ๋ฆฌ)**: ac48cb7 **1751 passed / 0 failed / 38 skipped**(73 ๋ถ„). ์‹คํ–‰ ๋’ค ์„ผํ‹ฐ๋„์€ ๋น„์—ˆ๊ณ , ๋ณธ ์ฒดํฌ์•„์›ƒ์˜ `.mam/shim/herdr` ์ˆ˜์ • ์‹œ๊ฐ์€ ๊ทธ๋Œ€๋กœ์˜€๋‹ค. skip 38 ๊ฑด์€ v4.8.7 ๊ณผ ๊ฐ™๋‹ค. - **codex ๋ฆฌ๋ทฐ์–ด ํ™˜๊ฒฝ**: 1ยท2ํšŒ์ฐจ ๋™์•ˆ codex ์„ธ์…˜์€ ๊ณต์œ  app-server ๋ฐ๋ชฌ ์žฌ์‹œ์ž‘์œผ๋กœ ์ƒŒ๋“œ๋ฐ•์Šค ์šฐํšŒ๊ฐ€ ํ’€๋ ค(workspace-write, ๋„คํŠธ์›Œํฌ ์ œํ•œ) ๋กœ์ปฌ ๋ธŒ๋กœ์ปค ํ…Œ์ŠคํŠธ์™€ MQTT ๋ฐœํ–‰์„ ํ•˜์ง€ ๋ชปํ–ˆ๊ณ , ๋ธŒ๋กœ์ปค ์—†๋Š” ์‹คํ–‰๊ณผ ์ •์  ๊ฒ€ํ† ๋กœ ํŒ์ •ํ–ˆ๋‹ค. ๋ฃจํ”„ ๋’ค `codex resume --no-daemon --dangerously-bypass-approvals-and-sandbox` ๋กœ ์žฌ๊ธฐ๋™ํ•ด ๋ฃจํ”„๋ฐฑ bind ์™€ `danger-full-access` ๋ฅผ ํ™•์ธํ–ˆ๋‹ค. #### ๐Ÿ“ ํ›„์† - codex ์–ด๋Œ‘ํ„ฐ์˜ resume ๋ช…๋ น์— `--no-daemon` ์ด ์—†๋‹ค: ๊ณต์œ  ๋ฐ๋ชฌ์„ ์“ฐ๋ฉด ์žฌ๊ธฐ๋™์ด "Cannot use the shared background server โ€ฆ rerun with --no-daemon" ์œผ๋กœ ์‹คํŒจํ•˜๊ณ , ๋ฐ๋ชฌ์ด ์žฌ์‹œ์ž‘๋˜๋ฉด ์ƒŒ๋“œ๋ฐ•์Šค ์šฐํšŒ๊ฐ€ ์กฐ์šฉํžˆ ํ’€๋ฆฐ๋‹ค. - ์„ผํ‹ฐ๋„ ์‹คํ–‰ ๋’ค ๋ฆฌ๋ทฐ ์›ŒํฌํŠธ๋ฆฌ์— ๋นˆ `.mam/` ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์ƒ๊ฒผ๋‹ค(ignore ๋Œ€์ƒ, shim ์—†์Œ). ์–ด๋А ํ…Œ์ŠคํŠธ๊ฐ€ ๋งŒ๋“œ๋Š”์ง€๋Š” ํ™•์ธํ•˜์ง€ ์•Š์•˜๋‹ค. ### ๐Ÿš€ `v4.8.7` โ€” Issue #17 ํ›„์†: ๋ž˜ํผ๊ฐ€ ํŒ์ •์„ ๋ชป ๋‚ด๋ฆฌ๋ฉด 1 ๋กœ ๋๋‚˜๊ณ , resume ์ด herdr 0.9.3 ์˜ ์ž๋ฆฌํ‘œ์‹œ pane ์„ ๋‹ซ๋Š”๋‹ค (2026-10-08) > **ํŒจ์น˜ (PATCH Release)**: v4.8.6(PR #51) ๋ฆฌ๋ทฐ๊ฐ€ ๋‚จ๊ธด ํ›„์†. ํŒ์ • ๋ชจ๋“ˆ์ด ์‹คํŒจํ•  ๋•Œ์˜ ๋ž˜ํผ ์ข…๋ฃŒ ์ฝ”๋“œ, herdr 0.9.3 ์—์„œ ๋“œ๋Ÿฌ๋‚œ #45 B ์˜ ๋นˆํ‹ˆ, ๋ฌธ์„œ ๋‘ ๊ณณ. ์ •์ƒ ๊ฒฝ๋กœ์˜ ์ข…๋ฃŒ ์ฝ”๋“œยท์ถœ๋ ฅยท๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท์ด๋ฒคํŠธ๋Š” ๊ฐ™๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **ํŒ์ • ์‹คํŒจ๋Š” 1 (`multi-agent-mux-delegate-job`, `lib_py/delegation/wrapper.py`)**: `exit "$(_decide exit-code โ€ฆ)"` ๋Š” ํŒ์ • ๋ชจ๋“ˆ์ด๋‚˜ `$PY` ๊ฐ€ ๊นจ์ง€๋ฉด `exit ""` โ†’ bash 3.2 ์—์„œ 255 ์˜€๊ณ , ๋‹ค๋ฅธ ํŒ์ •๋„ ์ €๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ์‹คํŒจํ–ˆ๋‹ค(wait-rc 255, finalize-begin ์‹คํŒจ ์‹œ finalize-end ์˜ argparse 2, set -e ์•„๋ž˜ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์ž์‹ ์˜ ์ฝ”๋“œ 1ยท127). ์ด์ œ ๊ฐ’์„ ๋‚ด๋Š” ํŒ์ •์€ `_decided`(0 ์ด ์•„๋‹Œ rcยท๋นˆ ๋‹ต์ด๋ฉด ์‹คํŒจ), ์ข…๋ฃŒ๋Š” `_exit_for`(์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ์‹คํŒจ)๋ฅผ ๊ฑฐ์น˜๊ณ , ์‹คํŒจ๋Š” `_decision_failed` ๊ฐ€ `ERROR:` ํ•œ ์ค„๊ณผ ํ•จ๊ป˜ 1 ๋กœ ๋๋‚ธ๋‹ค(fail-closed). 2. **์˜ˆ/์•„๋‹ˆ์˜ค ํŒ์ • (๋ฆฌ๋ทฐ codex R1)**: `subscriber-up`ยท`stall-watch` ๋Š” rc ๋กœ๋งŒ ๋‹ตํ•ด, ์‹คํ–‰์กฐ์ฐจ ๋ชป ํ•œ ํ˜ธ์ถœ(import ์˜ค๋ฅ˜ rc 1, ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์—†์Œ 127)์ด "์•„๋‹ˆ์˜ค"๋กœ ์ฝํ˜”๋‹ค โ€” 0 ์œผ๋กœ ๋๋‚œ ๊ตฌ๋…์ž์—์„œ ์ œ์ถœ์ด 0(completed)์œผ๋กœ ๋๋‚ฌ๊ณ , stall-watch ์‹คํŒจ๋Š” ๊ฐ์‹œ๋ฅผ ์กฐ์šฉํžˆ ๊ป๋‹ค. ๋‘ ์„œ๋ธŒ์ปค๋งจ๋“œ๋Š” `--print` ๋กœ `yes`/`no` ๋ฅผ ์ถœ๋ ฅํ•˜๊ณ (rc 0), ๋ž˜ํผ๋Š” `_decided_yes` ๋กœ ๋ฌป๋Š”๋‹ค. ๊ทธ ๋ฐ–์˜ ๋‹ตยท์‹คํŒจ๋Š” 1 ๋กœ ๋๋‚œ๋‹ค. `--print` ์—†๋Š” rc ๊ณ„์•ฝ(์˜ˆ 0, ์•„๋‹ˆ์˜ค 1)์€ ๊ทธ๋Œ€๋กœ. 3. **herdr 0.9.3 ์˜ ์ž๋ฆฌํ‘œ์‹œ pane (`lib_py/herdr_restore.py`)**: herdr 0.9.3 ์€ ์ €์žฅ๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์‚ฌ๋ผ์ง„ pane ์„ `$HOME` ์˜ ์…ธ์ด ์•„๋‹ˆ๋ผ ์›๋ž˜ cwd ์˜ ์ž๋ฆฌํ‘œ์‹œ(`restore_error` "Saved directory is unavailableโ€ฆ", ํ”„๋กœ์„ธ์Šค ์—†์Œ)๋กœ ๋ณต์›ํ•˜๊ณ , ๊ทธ pane ์˜ `pane process-info` ๋Š” `pane_not_found` ๋กœ ๋‹ตํ•œ๋‹ค. `close_restored_shells` ๋Š” ์—ฌ๊ธฐ์„œ ์˜ˆ์™ธ๋กœ exit 1 ํ•˜๋ฉฐ ๋‹ค๋ฅธ pane ์„ ๋ณด์ง€ ์•Š์•„, #45 B ๊ฐ€ ๋‹ซ์•„์•ผ ํ•  ์ง€์›Œ์ง„ ์›ŒํฌํŠธ๋ฆฌ์˜ pane ์ด ๋‚จ์•˜๋‹ค. `restore_error` ๊ฐ€ ์žˆ๊ณ  **๊ทธ๋ฆฌ๊ณ ** `pane_not_found` ์ธ ๊ฒฝ์šฐ๋งŒ "์‹คํ–‰ ์ค‘์ธ ๊ฒƒ ์—†์Œ"์œผ๋กœ ๋ณด๊ณ  ๋‹ซ๋Š”๋‹ค. ๋‹ค๋ฅธ process-info ์‹คํŒจ๋Š” ์ „์ฒ˜๋Ÿผ exit 1. docstring ๋„ ๋ฒ„์ „๋ณ„ ๋ณต์› ์œ„์น˜๋กœ ์ •์ •. 4. **ํ…Œ์ŠคํŠธ ์ „์ œ (`tests/test_issue45_restore.py`)**: ์ง€์›Œ์ง„ cwd ์˜ ๋ณต์› ์œ„์น˜๋ฅผ herdr 0.9.1(`$HOME`)ยท0.9.3(์›๋ž˜ ๊ฒฝ๋กœ) ๋‘˜ ๋‹ค ๋ฐ›๋Š”๋‹ค. 5. **๋ฌธ์„œ**: `wrapper.py` ๋จธ๋ฆฌ๋ง โ€” ๋ชจ๋“ˆ์€ stdlib ๋งŒ import ํ•˜์ง€๋งŒ `python -m lib_py.delegation.wrapper` ๋Š” ํŒจํ‚ค์ง€ `__init__` ์„ ๊ฑฐ์ณ paho ๋ฅผ ์‹ฃ๋Š”๋‹ค(N1). `lib_int.sh` โ€” ๋‹จ๋… source ๋  ๋•Œ PATH ์˜ `python3` ๋ฅผ ์“ด๋‹ค(N3). #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์ƒˆ ํ…Œ์ŠคํŠธ**: ํŒ์ • ์‹คํŒจ(tracebackยทrc 127ยท๋นˆ ๋‹ต) ร— ํŒ์ •(exit-code, sub-status, finalize-beginยท-end, wait-rc, subscriber-up ์ง์ ‘ยทloop/discuss, stall-watch)์ด 1 ๊ณผ `ERROR:` ์ค„๋กœ ๋๋‚จ โ€” ์ˆ˜์ • ์ „ ์ฝ”๋“œ์—์„œ 255 ๋˜๋Š” 0 ์œผ๋กœ ์‹คํŒจ. ์˜ˆ/์•„๋‹ˆ์˜ค ๋‘ ๋ถ„๊ธฐ ๋ณด์กด, `--print` ๋‹จ์œ„ํ‘œ, rc ๋กœ ์ฝ๋Š” ์˜ˆ/์•„๋‹ˆ์˜ค ํŒ์ •์„ ๊ธˆ์ง€ํ•˜๋Š” ์ •์  ๊ฐ€๋“œ. herdr stub ์œผ๋กœ ์ž๋ฆฌํ‘œ์‹œ ๋‹ซ๊ธฐ(56d0bfd ์—์„œ ์‹คํŒจ)์™€, `restore_error` ์—†๋Š” `pane_not_found`ยท๋‹ค๋ฅธ ์˜ค๋ฅ˜์˜ `restore_error` ๋Š” ์—ฌ์ „ํžˆ ์˜ˆ์™ธ. - **๋ฆฌ๋ทฐ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ๋ฃจํ”„ `--all-reviewer`)**: 1ํšŒ์ฐจ(@8241bd5) claudeยทgrok PASS, agy NOT PASS(๋ž˜ํผ ๋ฌธ์ž์—ด์„ ๊ณ ์ •ํ•œ ๊ธฐ์กด ์ •์  ํ…Œ์ŠคํŠธ 2 ๊ฑด์ด `_decided`/`_exit_for` ๋กœ ๋ฐ”๋€ ํ˜ธ์ถœ์„ ๋ชป ๋”ฐ๋ผ๊ฐ), codex NOT PASS(R1 ์œ„ 2). 541c3feยท65b78b6 ์—์„œ ์ˆ˜์ •. 2ํšŒ์ฐจ(@65b78b6) **4/4 PASS**. ๋ฆฌํฌํŠธ์™€ 2ํšŒ์ฐจ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, macOSยทPython 3.9ยทherdr 0.9.3, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด)**: 65b78b6 **1730 passed / 0 failed / 56 skipped**(72 ๋ถ„). skip ์ค‘ 18 ๊ฑด์€ ์ƒˆ ์›ŒํฌํŠธ๋ฆฌ์— nats-docker ์„œ๋ธŒ๋ชจ๋“ˆ์ด ์—†์–ด์„œ์˜€๊ณ , ์„œ๋ธŒ๋ชจ๋“ˆ์„ ๋ฐ›์€ ๋’ค `tests/test_deploy_freshness.py` **31 passed**. ๋‚˜๋จธ์ง€ 38 skip ์€ v4.8.6 ๊ณผ ๊ฐ™๋‹ค(tomllib ์ „์šฉ 34 ๋“ฑ). v4.8.6 ์˜ herdr 0.9.3 ์‹คํŒจ(`test_issue45_restore`)๋Š” ์ด๋ฒˆ์— ํ†ต๊ณผ. #### ๐Ÿ“ ํ›„์† - `tests/test_issue17_item4_decisions.py` ์˜ `_env` ๊ฐ€ ์ƒ์†๋œ `WORKSPACE_ROOT` ๋ฅผ ๋น„์šฐ์ง€ ์•Š์•„, ์˜›/์ƒˆ bash ๋น„๊ต ํ—ฌํผ๊ฐ€ ์—์ด์ „ํŠธ pane ์˜ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋กœ lib.sh ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค(claude ๋น„์ฐจ๋‹จ). ### ๐Ÿš€ `v4.8.6` โ€” Issue #17 ํ•ญ๋ชฉ 4: delegate-job ๋ž˜ํผ์˜ ํŒ์ •์ด Python ์œผ๋กœ ์˜ฎ๊ฒจ์ง„๋‹ค (2026-10-08) > **ํŒจ์น˜ (PATCH Release)**: ๋™์ž‘ ๋ณ€ํ™” ์—†๋Š” ๋ฆฌํŒฉํ„ฐ๋ง. ๋ž˜ํผ(bash)์˜ ํŒ์ • ๋กœ์ง์„ `lib_py/delegation/wrapper.py` ๋กœ ์˜ฎ๊ธฐ๊ณ , #26 ์ •์ˆ˜ ๊ทœ์น™์˜ ๋‘ ๊ตฌํ˜„์„ `lib_py/ints.py` ํ•˜๋‚˜๋กœ ํ•ฉ์ณค๋‹ค. ์ข…๋ฃŒ ์ฝ”๋“œยท๋ผ๋ฒจยท๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๊ธฐ๋กยท์ด๋ฒคํŠธยท๋ฉˆ์ถค ๊ฐ์‹œ ๋™์ž‘์€ ๊ฐ™๋‹ค. #17 ์˜ ๋‚˜๋จธ์ง€ ํ•ญ๋ชฉ์€ ์—ด๋ฆฐ ์ฑ„ ๋‚จ๋Š”๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **ํŒ์ •์˜ ์ด๋™ (`lib_py/delegation/wrapper.py`)**: `finalize_unfinished`(์‹œ๊ณ„ ์„ ํƒ, ๋””์Šคํฌ heartbeat ์œ ์˜ˆ, ๋ธŒ๋กœ์ปค ์žฌ๊ด€์ธก, `wall_clock_timeout`/`deadline_timeout`/`cancelled` ๋ผ๋ฒจ, close ์™€ ์ด๋ฒคํŠธ) โ†’ `finalize-begin`/`finalize-end`, `start_stall_watch` ์˜ ๊ฐ์‹œ ์—ฌ๋ถ€ โ†’ `stall-watch`, `_map_sub_rc` โ†’ `sub-status`, `_rc_for`(+`MAM_DELEGATE_FAIL_CLOSED`) โ†’ `exit-code`, ๊ตฌ๋…์ž ์กฐ๊ธฐ ์ข…๋ฃŒ ๊ฒ€์‚ฌ โ†’ `subscriber-up`, `cmd_wait` ์˜ rc ์ •๊ทœํ™” โ†’ `wait-rc`. ๋ž˜ํผ๋Š” `_decide` ํ•˜๋‚˜๋กœ ๋ถ€๋ฅด๊ณ  ์„œ๋ธŒ์ปค๋งจ๋“œ์˜ ์ข…๋ฃŒ ์ฝ”๋“œ๋‚˜ ํ•œ ์ค„ ์ถœ๋ ฅ์—๋งŒ ๋ถ„๊ธฐํ•œ๋‹ค. 2. **์ •์ˆ˜ ๊ทœ์น™ ํ•˜๋‚˜ (`lib_py/ints.py`)**: `lib_int.sh` ์˜ `mam_int`/`mam_int_knob` ์™€ runner ์˜ `_positive_int` ๊ฐ€ ๊ฐ™์€ ๊ตฌํ˜„(`positive_int`, `knob`)์„ ๋ถ€๋ฅธ๋‹ค. 3. **bash ์— ๋‚จ๋Š” ๊ฒƒ**: spawnยทwaitยทkill, ํ‚ค ์ฃผ์ž…, ์‹œ๊ทธ๋„ ์ •๋ฆฌ, ์ง„๋‹จ์šฉ herdr busy ํ™•์ธ ํ•˜๋‚˜, CLI ํ˜ธํ™˜ ์ธต. 4. **finalizer ์˜ ์ˆ˜๋ช… (PR #41 ๊ณ„์•ฝ๊ณผ ๊ฐ™๊ฒŒ)**: `finalize-end` ๋Š” `--watch-pid "$$"` ๋ฅผ ๋ฐ›์•„ ๋‘ ๋Œ€๊ธฐ ๋‚ด๋‚ด์™€ closeยท์ด๋ฒคํŠธ ์ง์ „์— ๋ž˜ํผ๋ฅผ ํ™•์ธํ•˜๊ณ , ๋ž˜ํผ๊ฐ€ ์—†์œผ๋ฉด ๋‹ซ์ง€๋„ ๋ฐœํ–‰ํ•˜์ง€๋„ ์•Š๊ณ  ๋๋‚œ๋‹ค. ์ข…๋ฃŒํ–ˆ์ง€๋งŒ ํšŒ์ˆ˜๋˜์ง€ ์•Š์€ ๋ž˜ํผ(์ข€๋น„, `ps -o stat=` ์˜ Z)๋„ ์—†๋Š” ๊ฒƒ์œผ๋กœ ๋ณธ๋‹ค. 5. **๋ฆฌ๋ทฐ R1 (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, codex)**: `finalize-end` ์˜ ๋Œ€๊ธฐ(`_sleep`)๊ฐ€ ๋ฒฝ์‹œ๊ณ„๋กœ ๊ธธ์ด๋ฅผ ์žฌ์„œ, ๋Œ€๊ธฐ ์ค‘ ์‹œ๊ณ„๊ฐ€ ๋’ค๋กœ ๊ฐ€๋ฉด bash `sleep N` ๊ณผ ๋‹ฌ๋ฆฌ ๋Š˜์–ด๋‚ฌ๋‹ค(10 ์ดˆ ๋’ค๋กœ โ†’ 1 ์ดˆ ๋Œ€๊ธฐ๊ฐ€ 11 ์ดˆ). ๋Œ€๊ธฐ ๊ธธ์ด๋Š” monotonic ์‹œ๊ณ„๋กœ ์žฌ๊ณ  ๋งˆ๊ฐ ํŒ์ •์€ ๋ฒฝ์‹œ๊ณ„์— ๋‚จ๊ธด๋‹ค(4c86107, ํšŒ๊ท€ ํ…Œ์ŠคํŠธ `test_u_a_wait_is_not_stretched_by_a_wall_clock_step_back`). #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์˜› bash ๋Œ€ ์ƒˆ ๋ž˜ํผ (`tests/test_issue17_item4_decisions.py`)**: 7209750 ์˜ bash ํ•จ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•œ `tests/data/issue17_item4_old_decisions.sh` ์™€ ์ƒˆ ๋ž˜ํผ๋ฅผ ๊ฐ™์€ ์ž…๋ ฅ์— ๋Œ๋ ค ์ถœ๋ ฅยท`FINAL_JOB_STATUS`ยท๋ ˆ์ง€์ŠคํŠธ๋ฆฌ/๋ฐœํ–‰ ํ˜ธ์ถœ์„ ๋น„๊ตํ•œ๋‹ค(๊ฐ ์œ ์˜ˆ ์ค‘ TERM, ํšŒ์ˆ˜ ์—†๋Š” TERM ํฌํ•จ). ํŒ์ •ํ‘œ ์ „์ฒดยท์ž˜๋ชป๋œ ์ž…๋ ฅยท๊ฒฝ๊ณ„๊ฐ’ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ. - **๋…๋ฆฝ ๋Œ€์กฐ (์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ)**: 7209750 ๋Œ€ 518beda ์‹ค์ œ CLI, ์˜คํ”„๋ผ์ธ์œผ๋กœ 179 ๊ฒฝ์šฐ ๋ชจ๋‘ ์ข…๋ฃŒ ์ฝ”๋“œยท์ถœ๋ ฅ ์ผ์น˜(`mam_int`/`mam_int_knob` 19 ์ž…๋ ฅ, `submit --dry-run` ์ •์ˆ˜ ํ”Œ๋ž˜๊ทธ 6 ๊ฐœ ร— 19 ์ž…๋ ฅ, 8 ์ƒํƒœ์˜ ๋ ˆ์ฝ”๋“œ์— ๋Œ€ํ•œ `wait`). - **๋ฆฌ๋ทฐ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ๋„ค ๋ฆฌ๋ทฐ์–ด)**: 1ํšŒ์ฐจ(@518beda) claudeยทagyยทgrok PASS, codex NOT PASS(R1 ์œ„ 5). 4c86107 ์—์„œ ์ˆ˜์ •. 2ํšŒ์ฐจ ๋ธํƒ€(@4c86107) claudeยทagyยทgrok PASS. codex ๋Š” ์ฒซ ์žก(817d1650)์ด ํŒ์ • ์—†์ด `deadline_timeout` ์œผ๋กœ ๋‹ซํ˜€ ๋‹ค์‹œ ๋Œ๋ ธ๊ณ (24b21b9c), ๊ทธ ๋ฆฌํฌํŠธ๋Š” "R1 ๋‹ซํž˜, ์ƒˆ ์ฝ”๋“œ ๊ฒฐํ•จ ์—†์Œ"์ด์ง€๋งŒ ์ž๊ธฐ ์ƒŒ๋“œ๋ฐ•์Šค๊ฐ€ ๋ฃจํ”„๋ฐฑ ๋ธŒ๋กœ์ปค์˜ bind ๋ฅผ ๋ง‰์•„ ํ•„์ˆ˜ ๋‘ ํŒŒ์ผ ์Šค์œ„ํŠธ(278 ๊ฑด)๋ฅผ ์‹คํ–‰ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค๋Š” ์ด์œ ๋กœ๋งŒ NOT PASS ๋ฅผ ๋ƒˆ๋‹ค. ๊ฐ™์€ ์Šค์œ„ํŠธ๋ฅผ ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ 4c86107 ๋กœ ๋Œ๋ ค **278 passed** ๋ฅผ ํ™•์ธํ•˜๊ณ  ๋ณ‘ํ•ฉํ–ˆ๋‹ค. ๋ฆฌํฌํŠธ๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, macOSยทPython 3.9, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด)**: 518beda **1711 passed / 1 failed / 38 skipped**(72 ๋ถ„). ์‹คํŒจํ•œ `tests/test_issue45_restore.py::test_b_only_the_resumed_sessions_restored_shell_is_closed` ๋Š” main(7209750)์—์„œ๋„ ์‹คํŒจํ•œ๋‹ค: herdr 0.9.3 ์€ ์ง€์›Œ์ง„ cwd ์˜ pane ์„ `$HOME` ์ด ์•„๋‹ˆ๋ผ ์›๋ž˜ ๊ฒฝ๋กœ๋กœ ๋ณต์›ํ•ด ํ…Œ์ŠคํŠธ์˜ ์ „์ œ๊ฐ€ ๋ฐ”๋€Œ์—ˆ๋‹ค(์ด PR ๊ณผ ๋ฌด๊ด€). 4c86107 ์˜ ๋ธํƒ€๋Š” ๊ด€๋ จ ํ…Œ์ŠคํŠธ 321 passed. #### ๐Ÿ“ ํ›„์† - herdr 0.9.3 ์— ๋งž์ถ˜ `test_issue45_restore` ์˜ cwd ์ „์ œ ์ˆ˜์ •. - ๋ž˜ํผ ๋ชจ๋“ˆ์ด ํŒจํ‚ค์ง€ ๊ฒฝ์œ ๋กœ paho ๋ฅผ import ํ•˜๋Š”๋ฐ ๋ฌธ์„œ๋Š” "stdlib only" ๋ผ๊ณ  ํ•œ๋‹ค; `_decide` ์‹คํŒจ ์‹œ `exit ""` ๊ฐ€ 255 ๊ฐ€ ๋œ๋‹ค(`${rc:-1}` ์ œ์•ˆ); `lib_int.sh` ๊ฐ€ PATH ์˜ `python3` ๋ฅผ ์“ด๋‹ค; PR ๋ณธ๋ฌธ์€ 0.2 ์ดˆ, ์ฝ”๋“œ๋Š” 0.5 ์ดˆ ๊ฐ„๊ฒฉ(claude 1ํšŒ์ฐจ ๋น„์ฐจ๋‹จ ์ง€์  N1ยทN2ยทN3ยทN5). ### ๐Ÿš€ `v4.8.5` โ€” Issue #45 AยทB: ์žฌ๊ธฐ๋™๋œ ๊ฒฉ๋ฆฌ ์„œ๋ฒ„๊ฐ€ ์ €์žฅ๋œ ์—์ด์ „ํŠธ๋ฅผ ๋‹ค์‹œ ๋„์šฐ์ง€ ์•Š๊ณ , resume ์ด ๋ณต์›๋œ ๋นˆ ์…ธ์„ ๋‹ซ๋Š”๋‹ค (2026-10-04) > **ํŒจ์น˜ (PATCH Release)**: ๊ฒฉ๋ฆฌ herdr ์„œ๋ฒ„๊ฐ€ ์—์ด์ „ํŠธ๋ฅผ ์ •์ง€ํ•˜์ง€ ์•Š์€ ์ฑ„ ๊บผ์กŒ๋‹ค ๋‹ค์‹œ ๋œจ๋ฉด, herdr ๊ฐ€ ์ €์žฅ ๋ ˆ์ด์•„์›ƒ์„ ๋ณต์›ํ•˜๋ฉฐ ์—์ด์ „ํŠธ๋ฅผ ์ž๊ธฐ ์žฌ๊ฐœ ๋ช…๋ น์œผ๋กœ(๊ถŒํ•œ ํ”Œ๋ž˜๊ทธ ์—†์ด, ์ง€์šด ์—์ด์ „ํŠธ๊นŒ์ง€) ๋‹ค์‹œ ๋„์› ๋‹ค. herdr 0.9.1 ์˜ `[session] resume_agents_on_restore = false` ๋กœ MAM ์ด ๋„์šฐ๋Š” ์„œ๋ฒ„์—์„œ ์ด๋ฅผ ๋ˆ๋‹ค(A). ๋ˆ ๋’ค์—๋„ pane ์€ ๋นˆ ์…ธ๋กœ ๋ณต์›๋˜๋ฏ€๋กœ, resume ์ด ๊ทธ ์„ธ์…˜ ์ž์‹ ์˜ ๋นˆ ์…ธ์„ ๋‹ซ๋Š”๋‹ค(B). ์ด์Šˆ์˜ ์ œ์•ˆ 2ยท4 ๋Š” ๋ฒ”์œ„ ๋ฐ–. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **์„œ๋ฒ„ ์„ค์ • (`lib_py/herdr_config.py`)**: herdr ๊ฐ€ ์ฝ๋Š” ์‚ฌ์šฉ์ž ์„ค์ •(`HERDR_CONFIG_PATH` โ†’ `XDG_CONFIG_HOME` โ†’ `~/.config`)์—์„œ ๊ทธ ํ‚ค๋งŒ false ๋กœ ๋ฐ”๊พผ `mam-server-config.toml` ์„ ์›๋ณธ ์˜†์— ๋งŒ๋“ ๋‹ค(์ƒ๋Œ€ ๊ฒฝ๋กœ๊ฐ€ ๊ฐ™๊ฒŒ ํ’€๋ฆฐ๋‹ค). ์‚ฌ์šฉ์ž ํŒŒ์ผ์€ ๊ณ ์น˜์ง€ ์•Š๋Š”๋‹ค. ์ด ๋ณ‘ํ•ฉ์—๋Š” `tomllib`(Python 3.11+)์ด ํ•„์š”ํ•˜๋‹ค. PATH ์˜ `python3` ๊ฐ€ ๊ทธ๋ณด๋‹ค ์˜ค๋ž˜๋˜๋ฉด(macOS ๊ธฐ๋ณธ 3.9) ๋ณต์›์€ ๊บผ์ง€์ง€๋งŒ ํŒŒ์ผ์— ๊ทธ ํ‚ค๋งŒ ๋‹ด๊ฒจ, MAM ์„œ๋ฒ„๋Š” ์‚ฌ์šฉ์ž์˜ ๋‹ค๋ฅธ herdr ์„ค์ • ์—†์ด ๋œจ๊ณ  ๊ฒฝ๊ณ ๊ฐ€ ๋‚จ๋Š”๋‹ค: ์ถœ๋ ฅ์ด ์›๋ณธ ์ž์‹ (๋ณ„์นญ ํฌํ•จ)์ด๊ฑฐ๋‚˜ ๊ฐ™์€ ์›๋ณธ์œผ๋กœ ๋งŒ๋“  ํŒŒ์ผ์ด ์•„๋‹ˆ๋ฉด ์›๋ณธ๋ณ„ ์ด๋ฆ„์„ ์“ด๋‹ค. ํŒŒ์‹ฑ โ†’ ํ‚ค ์„ค์ • โ†’ ์ง๋ ฌํ™”ํ•˜๊ณ , ๊ฒฐ๊ณผ๊ฐ€ ์›๋ณธ + ํ‚ค์™€ ์ •ํ™•ํžˆ ๊ฐ™์•„์•ผ ํ•œ๋‹ค(ํ—ค๋”ยท์  ํ‘œ๊ธฐยท์ธ๋ผ์ธ ํ‘œ ๋ชจ๋‘). ์“ด ํŒŒ์ผ์€ ๊ทธ herdr ์˜ `config check` ๋ฅผ ํ†ต๊ณผํ•ด์•ผ ํ•œ๋‹ค. herdr ๋Š” ํŒŒ์‹ฑยท์Šคํ‚ค๋งˆ ์˜ค๋ฅ˜๋ฉด ํŒŒ์ผ ์ „์ฒด๋ฅผ ๋ฒ„๋ฆฌ๊ณ ("using defaults") ๋ณต์›์„ ์ผœ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ†ต๊ณผํ•˜์ง€ ๋ชปํ•˜๋ฉด(๊นจ์ง, ๋น„-bool ๊ฐ’, ํ‘œ๊ฐ€ ์•„๋‹Œ `session`, UTF-8 ์•„๋‹˜) ํ‚ค ํ•˜๋‚˜๋งŒ ์žˆ๋Š” ํŒŒ์ผ์„ ์“ฐ๊ณ  ๊ฒฝ๊ณ ํ•œ๋‹ค. MAM pane ์ด ๋ฌผ๋ ค๋ฐ›์€ ์ƒ์„ฑ ํŒŒ์ผ์€ ๊ธฐ๋ก๋œ ์›๋ณธ์œผ๋กœ ๊ฑฐ์Šฌ๋Ÿฌ ์˜ฌ๋ผ๊ฐ„๋‹ค. 2. **shim bootstrap (`lib.sh` heredoc)**: ์„œ๋ฒ„๋ฅผ ๋„์šธ ๋•Œ๋งˆ๋‹ค ์„ค์ •์„ ๋‹ค์‹œ ๋งŒ๋“ค์–ด `HERDR_CONFIG_PATH` ๋กœ ๋„˜๊ธด๋‹ค. ๋ชป ๋งŒ๋“ค๋ฉด ๊ฒฝ๊ณ ํ•˜๊ณ  ์˜ˆ์ „์ฒ˜๋Ÿผ(๋ณต์› ์ผœ์ง) ๋„์šด๋‹ค. ๊ธฐ๋ณธ `default` ์„œ๋ฒ„์™€ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋„์šด ์„œ๋ฒ„๋Š” ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค. 3. **๋ณต์›๋œ ๋นˆ ์…ธ (`lib_py/herdr_restore.py`, `resume_session.sh`)**: spawn ์ง์ „ ํ•œ ๋ฒˆ, `agent_session` ์ด resume ํ•˜๋Š” id ์™€ ๊ฐ™๊ณ  live ์—์ด์ „ํŠธ๊ฐ€ ์—†๊ณ  ์ „๊ฒฝ์ด ์…ธ๋ฟ์ธ pane ์„ ๋‹ซ๋Š”๋‹ค. ๋“ฑ๋ก๋ถ€๋Š” ๋ณด์ง€ ์•Š๋Š”๋‹ค(์„œ๋ฒ„๊ฐ€ ์ฃฝ์€ ์„ธ์…˜๋„ running ์œผ๋กœ ๋‚จ๋Š”๋‹ค, Creator ์ด์˜ job 9f5f1ebe). `CP_PREFIX` ๊ฐ€ ๋ถ™๋Š” resume ์ด ์žฌ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•ด ๋‚จ๋˜ ๋นˆ ์…ธ๊ณผ `$HOME` ์œผ๋กœ ๋–จ์–ด์ง„ ๋นˆ ์…ธ์ด ๋Œ€์ƒ์ด๋‹ค. 4. **S11 (`tests/test_issue23_24_startup_path.py`)**: ์ƒ์„ฑ shim ์ด ์šฐ์—ฐํžˆ ๋ฐ”๋€Œ์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ๋น„๊ต ๊ธฐ์ค€์„ heredoc ์„ ์˜๋„์ ์œผ๋กœ ๋ฐ”๊พผ ed73f67 ๋กœ ์˜ฎ๊ฒผ๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ค์„œ๋ฒ„ ํ…Œ์ŠคํŠธ (`tests/test_issue45_restore.py`, herdr 0.9.1)**: ์งง์€ ์ผํšŒ์šฉ HOMEยท์„ธ์…˜๊ณผ ๊ฐ€์งœ claude ๋กœ, MAM bootstrap ๊ฒฝ๋กœ์—์„œ ๋ณต์›์ด ๊บผ์ง(์‚ฌ์šฉ์ž ์„ค์ •์— `[session]` ํ‘œ๊ฐ€ ์žˆ์„ ๋•Œ ํฌํ•จ)๊ณผ ๊ฐ™์€ ๋ ˆ์ด์•„์›ƒ์„ ์ผ๋ฐ˜ ์„œ๋ฒ„๋กœ ๋„์šฐ๋ฉด `claude --resume` ์ด ๋œจ๋Š” ์–‘์„ฑ ๋Œ€์กฐ, ๋‹ซ๊ธฐ ์กฐ๊ฑด, create โ†’ crash โ†’ resume ๋’ค pane ํ•˜๋‚˜๋งŒ ๋‚จ์Œ์„ ํ™•์ธํ•œ๋‹ค. ์ˆ˜์ •์„ ๋„๋ฉด AยทB ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ๊ฐ ์‹คํŒจํ•จ์„ ํ™•์ธํ–ˆ๋‹ค. - **๋‹จ์œ„ (`tests/test_issue45_herdr_config.py`)**: ํ•ฉ์น˜๊ธฐ 12 ๊ฐ€์ง€ ๊ฒฝ์šฐ์™€ ์‹ค์ œ `herdr config check`, ์›๋ณธ ๊ฒฝ๋กœ ๊ทœ์น™, ์“ฐ๊ธฐยทํด๋ฐฑ. - **๊ด€๋ จ ์Šค์œ„ํŠธ**: ์ƒˆ ๋‘ ํŒŒ์ผ 40 passed. issue9ยทshim contractยทissue42ยทissue24 resumeยทuuid_targetยทissue21 prereqsยทissue23_24 199 passed / 1 skipped(S11 ๊ธฐ์ค€ ์ด๋™ ํ›„). tier1~4ยทissue21_phase2_loop_a 148 passed. ์ „์ฒด ์Šค์œ„ํŠธ๋Š” ๋Œ๋ฆฌ์ง€ ์•Š์•˜๋‹ค. - **๋ฆฌ๋ทฐ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ๋„ค ๋ฆฌ๋ทฐ์–ด)**: 1ํšŒ์ฐจ(@35935f9) claudeยทagyยทgrok NOT PASS(ํ…Œ์ŠคํŠธ ์ด์‹์„ฑ: Python 3.9 ์˜ `tomllib`, macOS ์˜ `/proc`ยท`/tmp`), codex ํŒ์ • ์—†์Œ(๋ชจ๋ธ ์šฉ๋Ÿ‰ ์ดˆ๊ณผ๋กœ 1 ์‹œ๊ฐ„ ์˜ˆ์‚ฐ ์†Œ์ง„). 36f0b8dยทe8d9151 ์—์„œ ์ˆ˜์ •. 2ํšŒ์ฐจ(@e8d9151) claudeยทagyยทgrok PASS, codex NOT PASS(R1 `config check` ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ๋ชปํ•˜๋ฉด ๊ฒ€์‚ฌ ์•ˆ ๋œ ๋ณ‘ํ•ฉ์„ ๋„˜๊น€ โ†’ ์Šคํ‚ค๋งˆ ์˜ค๋ฅ˜๋ฉด herdr ๊ฐ€ ํŒŒ์ผ ์ „์ฒด๋ฅผ ๋ฒ„๋ ค ๋ณต์›์ด ๋‹ค์‹œ ์ผœ์ง, R2 0ยทFalseยท-1 ๊ฐ™์€ pid ์‘๋‹ต์—๋„ pane ์„ ๋‹ซ์Œ, R3 TOML `nan` ์ด ์žฌํŒŒ์‹ฑ ๋น„๊ต๋ฅผ ๊นจ ์‚ฌ์šฉ์ž ์„ค์ •์„ ๋ชจ๋‘ ๋ฒ„๋ฆผ). ab93776 ์—์„œ ์ˆ˜์ •: ๊ฒ€์‚ฌ ๋ชป ํ•œ ๋ณ‘ํ•ฉ์€ ์“ฐ์ง€ ์•Š๊ณ  MINIMAL(๊ทธ ๊ฒ€์‚ฌ๋„ ๋ชป ํ•˜๋ฉด MINIMAL ์„ ๊ฒฝ๊ณ ์™€ ํ•จ๊ป˜ โ€” ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋„˜๊ธฐ๋ฉด ๋ณต์›์ด ์ผœ์ง„ ์ฑ„ ๋‚จ๋Š”๋‹ค), pid ๋Š” ์–‘์˜ ์ •์ˆ˜๋งŒ, nan ๋ผ๋ฆฌ ๊ฐ™๋‹ค๊ณ  ๋ณด๋Š” ์žฌ๊ท€ ๋น„๊ต. 3ํšŒ์ฐจ(@ab93776) **4/4 PASS**. ๋ฆฌํฌํŠธ์™€ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, macOSยทPython 3.9, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด)**: ab93776 **1456 passed / 0 failed / 38 skipped**(65 ๋ถ„; skip ์ค‘ 34 ๊ฑด์€ Python 3.9 ์˜ tomllib ์ „์šฉ ๊ฒ€์‚ฌ). 1ํšŒ์ฐจ head 35935f9 ๋Š” herdr_config ํ…Œ์ŠคํŠธ๊ฐ€ ์ˆ˜์ง‘ ๋‹จ๊ณ„์—์„œ ์Šค์œ„ํŠธ๋ฅผ ๋ฉˆ์ท„๊ณ , ๊ทธ ํŒŒ์ผ์„ ๋นผ๋ฉด 1418 passed / 5 failed(1ํšŒ์ฐจ ์ง€์  ํ…Œ์ŠคํŠธ). purge ํ…Œ์ŠคํŠธ๋„ ํ†ต๊ณผ(PR ๋ณธ๋ฌธ์˜ 12 ๊ฑด ๊ธฐ์ค€ ์‹คํŒจ๋Š” Linux, #28). - **Python 3.9 (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค)**: `tests/test_issue45_herdr_config.py` ๊ฐ€ ๋ชจ๋“ˆ ๋จธ๋ฆฌ์—์„œ `import tomllib` ์„ ํ•ด 3.11 ๋ฏธ๋งŒ์—์„œ๋Š” ์Šค์œ„ํŠธ ์ „์ฒด๊ฐ€ ์ˆ˜์ง‘ ๋‹จ๊ณ„์—์„œ ๋ฉˆ์ท„๋‹ค. import ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ๋ฐ”๊พธ๊ณ  ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์‹ฑํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž ์„ค์ • ์œ ์ง€๋ฅผ ๊ธฐ๋Œ€ํ•˜๋Š” 29 ๊ฑด๋งŒ `tomllib` ์—†์œผ๋ฉด skip(3.9: 22 passed / 29 skipped, 3.11: 51 passed). - **๋ฆฌ๋ทฐ**: 1ํšŒ์ฐจ codex NOT PASS(R1 herdr ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ ์—†์Œ, R2 ์  ํ‘œ๊ธฐยท์ธ๋ผ์ธ ํ‘œ์—์„œ ์„ค์ • ์œ ์‹ค, R3 ์›๋ณธ๊ณผ ์ถœ๋ ฅ ์ด๋ฆ„ ์ถฉ๋Œ ์‹œ ๋ฎ์–ด์”€). 811b080 ์—์„œ ์ˆ˜์ •. 2ํšŒ์ฐจ codex NOT PASS(R3 ์˜ ํด๋ฐฑ ๊ฒฝ๋กœ๋Š” ๊ฐ™์€ ๊ฒ€์‚ฌ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š์Œ), ํด๋ฐฑ๋„ ๊ฐ™์€ ์ด๋ฆ„ ์„ ํƒ์„ ๊ฑฐ์น˜๋„๋ก ์ˆ˜์ •. ### ๐Ÿš€ `v4.8.4` โ€” `.claude/skills` ๋งํฌ ๊ฒ€์‚ฌ๊ฐ€ ๋‘ ๋กœ์ปฌ ๋ฐฐ์น˜๋ฅผ ๋ฐ›๊ณ  ๊นจ์ง„ ๋งํฌ๋ฅผ ์žก๋Š”๋‹ค (2026-10-04, PR #47) > **ํŒจ์น˜ (PATCH Release)**: v4.8.3(PR #46)์ด ๋„ฃ์€ `test_claude_skills_link_resolves_to_agents_skills` ๊ฐ€ ๋ณ‘ํ•ฉ ํ›„ ๋ณธ ์ฒดํฌ์•„์›ƒ์—์„œ ์‹คํŒจํ–ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋Š” `.claude/skills` ๋ฅผ `.agents/skills` ๋กœ ๊ฐ€๋Š” ๋งํฌ ํ•˜๋‚˜๋กœ ๊ฐ€์ •ํ–ˆ์ง€๋งŒ, ์ด ๋กœ์ปฌ(git-ignored) ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ์Šคํ‚ฌ๋ณ„ ๋งํฌ๋ฅผ ๋‹ด์€ ๋””๋ ‰ํ„ฐ๋ฆฌ์ผ ์ˆ˜๋„ ์žˆ๋‹ค. PR #46 ์˜ ๋ฆฌ๋ทฐยท์Šค์œ„ํŠธ๋Š” ์›ŒํฌํŠธ๋ฆฌ์—์„œ ๋Œ์•„ ์ด ํ…Œ์ŠคํŠธ๊ฐ€ skip ์ด์—ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋งŒ ๊ณ ์นœ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **๋‘ ๋ฐฐ์น˜ (`tests/test_issue42_skill_blocks.py`)**: ๋‹จ์ผ ๋งํฌ(โ†’ `.agents/skills`) ๋˜๋Š” ์—ฌ๋Ÿ ์Šคํ‚ฌ ๊ฐ๊ฐ์ด `.agents/skills/` ๋กœ ํ•ด์„๋˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ชจ๋‘ ๋ฐ›๋Š”๋‹ค. 2. **๊นจ์ง„ ๋งํฌ๋Š” ์‹คํŒจ (๋ฆฌ๋ทฐ codex R1)**: `Path.exists()` ๋Š” ๋งํฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฏ€๋กœ ๊นจ์ง„ ์ตœ์ƒ์œ„ ๋งํฌ๊ฐ€ "์—†์Œ"์œผ๋กœ skip ๋๋‹ค. ๋ถ€์žฌ๋Š” "์กด์žฌํ•˜์ง€๋„ ์•Š๊ณ  ๋งํฌ๋„ ์•„๋‹˜"์ผ ๋•Œ๋งŒ์ด๋‹ค. 3. **์›ŒํฌํŠธ๋ฆฌ์—์„œ๋„ ๊ฒ€์‚ฌ**: ๊ฒ€์‚ฌ๋ฅผ `claude_skills_problems()` ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ  ํ•ฉ์„ฑ ๋ฐฐ์น˜ 6 ๊ฐœ(์—†์Œ, ๋‹จ์ผ ๋งํฌ, ์Šคํ‚ฌ๋ณ„ ๋งํฌ, ๋‹ค๋ฅธ ์Šคํ‚ฌ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋งํฌ, ๊นจ์ง„ ์ตœ์ƒ์œ„ ๋งํฌ, ๊นจ์ง„ ์Šคํ‚ฌ ๋งํฌ)๋กœ ์‹œํ—˜ํ•œ๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **๋ฆฌ๋ทฐ (๋„ค ๋ฆฌ๋ทฐ์–ด)**: 1ํšŒ์ฐจ(@e1ed270) claudeยทagyยทgrok PASS, codex NOT PASS(R1 ๊นจ์ง„ ์ตœ์ƒ์œ„ ๋งํฌ skip). 150b493 ์—์„œ ์ˆ˜์ •. 2ํšŒ์ฐจ **4/4 PASS**. ๋ฆฌํฌํŠธ์™€ 2ํšŒ์ฐจ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **ํ…Œ์ŠคํŠธ**: `tests/test_issue42_skill_blocks.py` 37 passed / 1 skipped(์›ŒํฌํŠธ๋ฆฌ). codex ์˜ ํƒ์นจ(ํŠธ๋ฆฌ์— ๊นจ์ง„ `.claude/skills`)์€ ์ด์ œ ์‹คํŒจ, ๋ณธ ์ฒดํฌ์•„์›ƒ์˜ ์Šคํ‚ฌ๋ณ„ ๋ฐฐ์น˜๋Š” ๋ฌธ์ œ ์—†์Œ. ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜ ํ•˜๋‚˜๋งŒ ๋ฐ”๋€Œ์–ด ์ „์ฒด ์Šค์œ„ํŠธ๋Š” ๋Œ๋ฆฌ์ง€ ์•Š์•˜๋‹ค(์ง์ „ ์ „์ฒด ์Šค์œ„ํŠธ: v4.8.3 ํ›„๋ณด 1408 passed / 0 failed). ### ๐Ÿš€ `v4.8.3` โ€” Issue #42 ๋‹จ๊ณ„ 1ยท2: SKILL.md ์ฝ”๋“œ ๋ธ”๋ก ๋ชฉ๋ก์„ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋กœ, ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ด๋ฏธ ํ•˜๋Š” ๋ธ”๋ก ์‚ญ์ œ (2026-10-04, PR #46) > **ํŒจ์น˜ (PATCH Release)**: ์Šคํ‚ฌ ์ ˆ์ฐจ๋ฅผ SKILL.md ์˜ ์ฝ”๋“œ ๋ธ”๋ก์œผ๋กœ ๋‹ค์‹œ ์ ์–ด ์—์ด์ „ํŠธ๊ฐ€ ์†์œผ๋กœ ๋”ฐ๋ผ ํ•˜๊ฒŒ ํ•˜๋˜ ๊ณณ ๊ฐ€์šด๋ฐ, ์ง„์ž… ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ด๋ฏธ ๊ทธ๋Œ€๋กœ ํ•˜๋Š” ๋ธ”๋ก์„ ์ง€์šฐ๊ณ  ํ˜ธ์ถœ ํ•œ ์ค„๊ณผ ์˜ต์…˜ยท์ข…๋ฃŒ ์ฝ”๋“œ ํ‘œ๋กœ ๋ฐ”๊ฟจ๋‹ค(Issue #42 ์˜ ๋‹จ๊ณ„ 1ยท2). ๋™์ž‘ ๋ณ€๊ฒฝ ์—†์Œ. ๋‹จ๊ณ„ 3~5(์Šคํฌ๋ฆฝํŠธ์— ์—†๋Š” ์ ˆ์ฐจ์˜ ์ฝ”๋“œํ™”, bash ์ง„์ž…์  ์ •๋ฆฌ, ๋ฌธ์„œ ๊ฐ€๋“œ ํ™•์žฅ)๋Š” #17 ํ•ญ๋ชฉ 4 ๋‹ค์Œ์— ์ง„ํ–‰ํ•œ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **๋ชฉ๋ก์ด ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋‹ค (`tests/data/skill_code_blocks.json`)**: 45d1621 ์˜ SKILL.md 8 ๊ฐœ์— ์žˆ๋˜ ํŽœ์Šค ๋ธ”๋ก 37 ๊ฐœ๋ฅผ (a) ์Šคํฌ๋ฆฝํŠธ ์ค‘๋ณต, (b) ์Šคํฌ๋ฆฝํŠธ์— ์—†๋Š” ์ ˆ์ฐจ, (c) ์˜ˆ์‹œยทํ˜ธ์ถœยท๋„์‹์œผ๋กœ ๋‚˜๋ˆ„๊ณ  ์Šคํฌ๋ฆฝํŠธ file:line ๊ทผ๊ฑฐ๋ฅผ ๋‹ฌ์•˜๋‹ค. (a) 5 ๊ฐœ๋Š” ์‚ญ์ œ(`removed`), ๋‹ค๋ฅธ ํ˜ธ์ถœ ๋ธ”๋ก์— ํ•ฉ์ณ์ง„ (c) 1 ๊ฐœ๋Š” `superseded`, ๋‚˜๋จธ์ง€๋Š” ํ˜„์žฌ ๋ธ”๋ก 33 ๊ฐœ๋กœ ์ถ”์ ํ•œ๋‹ค. (b) ๋ชฉ๋ก์€ ๋‹จ๊ณ„ 3 ๊ณผ #17 ํ•ญ๋ชฉ 4 ์˜ ์ž…๋ ฅ์ด๋‹ค. 2. **๋ฌธ์„œ ๊ฐ€๋“œ (`tests/test_issue42_skill_blocks.py`)**: ๋ธ”๋ก์ด ๋ชฉ๋ก ์—†์ด ๋ฐ”๋€Œ๊ฑฐ๋‚˜, ์ง€์šด ๋ธ”๋ก์˜ ๋ณธ๋ฌธ์ด ๋Œ์•„์˜ค๊ฑฐ๋‚˜(์ •ํ™• sha ์™€ ๊ณต๋ฐฑ ๋ฌด์‹œ fingerprint, ๋ชฉ๋ก๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ), ์ง€์šด ๋ธ”๋ก์˜ ์ง„์ž…์  ํ˜ธ์ถœ์ด ๋น ์ง€๊ฑฐ๋‚˜, ์ข…๋ฃŒ ์ฝ”๋“œ ํ‘œ๊ฐ€ ์Šคํฌ๋ฆฝํŠธ์™€ ์–ด๊ธ‹๋‚˜๊ฑฐ๋‚˜, ๊ทผ๊ฑฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณณ์„ ๊ฐ€๋ฆฌํ‚ค๋ฉด ์‹คํŒจํ•œ๋‹ค. 3. **์ค‘๋ณต ๋ธ”๋ก ์‚ญ์ œ (resumeยทstopยทcreate SKILL.md)**: resume Workflow, stop ์‚ฌ์ „ ํ™•์ธ, create ์‚ฌ์ „ ํ™•์ธยทWorkflowยทYAML ์ˆ˜๋™ ์ถ”๊ฐ€๋ฅผ ์ง„์ž…์  ํ˜ธ์ถœ ํ•œ ์ค„๊ณผ ์˜ต์…˜ยท์ข…๋ฃŒ ์ฝ”๋“œ ํ‘œ๋กœ ๋ฐ”๊ฟจ๋‹ค. 4. **๋ฌธ์„œ-์Šคํฌ๋ฆฝํŠธ ๋ถˆ์ผ์น˜ ์ •์ •**: resume ์€ attach ํ•˜์ง€ ์•Š์œผ๋ฉฐ, attach ๋Š” ์„œ๋ฒ„๋ฅผ ๋ช…์‹œํ•ด์•ผ ํ•œ๋‹ค(`herdr --session agent attach `). stop `--purge-conversation` ์€ `--yes` ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. `--dry-run` ์€ ๊ธฐ๋™ยท๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๊ฐฑ์‹ ์„ ํ•˜์ง€ ์•Š์ง€๋งŒ macOS ์—์„œ ์—์ด์ „ํŠธ ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ quarantine ์†์„ฑ์„ ์ง€์šด๋‹ค. ๊ทธ ๋ฐ–์— status `--json` ํ‚ค, create `cmd_full`, ์กด์žฌํ•˜์ง€ ์•Š๋˜ yaml ํŽธ์ง‘ ์Šคํฌ๋ฆฝํŠธ ์–ธ๊ธ‰. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **๋ฆฌ๋ทฐ (์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค)**: planner + ๋„ค ๋ฆฌ๋ทฐ์–ด 3 ํšŒ์ฐจ ๋งŒ์žฅ์ผ์น˜ PASS. - **๋ฆฌ๋ทฐ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค)**: 1ํšŒ์ฐจ(@8d26106) claudeยทagyยทgrok PASS, codex NOT PASS(R1 ๋ถ„๋ฆฌ๋œ attach ์•ˆ๋‚ด๊ฐ€ ๊ฒฉ๋ฆฌ ์„œ๋ฒ„๋ฅผ ์žƒ์Œ, R2 `--dry-run` "๋ฌด์“ฐ๊ธฐ" ์„ค๋ช…์ด ์‹ค์ œ์™€ ๋‹ค๋ฆ„, R3 ์ง€์šด ๋ธ”๋ก์ด ๋ชฉ๋ก์— ๋‹ค์‹œ ๋“ฑ๋ก๋˜๋ฉด ๋Œ์•„์™€๋„ ๊ฐ€๋“œ๊ฐ€ ๋ชป ์žก์Œ). 17eba09 ์—์„œ ์ˆ˜์ •(๋ฌธ์„œยทํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐยท๊ฐ€๋“œ ํ…Œ์ŠคํŠธ๋งŒ, ์ œํ’ˆ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์Œ). 2ํšŒ์ฐจ **4/4 PASS**. ๋ฆฌํฌํŠธ์™€ 2ํšŒ์ฐจ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, 8d26106, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด)**: **1408 passed / 0 failed / 4 skipped**(61 ๋ถ„). PR ๋ณธ๋ฌธ์˜ "๊ธฐ์ค€ ์‹คํŒจ 12 ๊ฑด"(`tests/test_issue10_13_purge.py`)์€ Linux ํ˜ธ์ŠคํŠธ์˜ ๊ฒƒ์œผ๋กœ(#28), ์ด macOS ํ˜ธ์ŠคํŠธ์—์„œ๋Š” ํ†ต๊ณผ. 17eba09 ์˜ ๋ณ€๊ฒฝ(๋ฌธ์„œ 3 ๊ฐœยทํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐยท๊ฐ€๋“œ ํ…Œ์ŠคํŠธ)์€ ๋ฌธ์„œ ํ…Œ์ŠคํŠธ 6 ๊ฐœ ํŒŒ์ผ 132 passed / 1 skipped ๋กœ ํ™•์ธ. - **ํ›„์†**: ์ข…๋ฃŒ ์ฝ”๋“œ ์Šค์บ๋„ˆ๊ฐ€ heredoc ์•ˆ ์ฃผ์„ยท`sys.exit`ยท`|| true` ๋ฅผ ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค๋ฃจ์ง€ ๋ชปํ•จ(ํ˜„์žฌ ํ‘œ๋Š” ๋งž์Œ, codex C2); ๋ชฉ๋ก์ด ๋ธ”๋ก ์ค„ ๋ฒˆํ˜ธ์— ๋ฌถ์—ฌ ์‚ฐ๋ฌธ ํŽธ์ง‘์—๋„ ๊ฐฑ์‹ ์ด ํ•„์š”(claude O2); `$(( 1 << 2 ))` ๋ฅผ heredoc ์‹œ์ž‘์œผ๋กœ ์˜ค์ธ(ํ˜„์žฌ ์Šคํฌ๋ฆฝํŠธ์—” ์—†์Œ, claude O3); Linux purge 12 ๊ฑด์€ #28. ### ๐Ÿš€ `v4.8.2` โ€” PR #41ยท#43 ๋ฆฌ๋ทฐ ํ›„์† ์ •๋ฆฌ: ๋‚ ์งœ ํ…Œ์ŠคํŠธ, ์„ค์ • ํŒŒ์ผ ์˜ค๋ฅ˜ ํ•œ ์ค„, BOM, ์ •์ˆ˜ ์„ค์ •๊ฐ’ ๊ทœ์น™ ํ™•์žฅ, ํ…Œ์ŠคํŠธ ํ›… ์ œ๊ฑฐ (2026-10-03, PR #44) > **ํŒจ์น˜ (PATCH Release)**: v4.8.0(PR #41)ยทv4.8.1(PR #43) ๋ฆฌ๋ทฐ์—์„œ ๋น„์ฐจ๋‹จ์œผ๋กœ ๋‚จ๊ธด ํ›„์† ์‚ฌํ•ญ์„ ์ด ์›Œํฌ์ŠคํŽ˜์ด์Šค์˜ ๋ฃจํ”„(creator-claude-01 ๊ตฌํ˜„, ๋„ค ๋ฆฌ๋ทฐ์–ด ๋ฆฌ๋ทฐ)๋กœ ์ฒ˜๋ฆฌํ–ˆ๋‹ค. ๋™์ž‘ ๋ณ€๊ฒฝ์€ ์•„๋ž˜ ํ•ญ๋ชฉ์— ํ•œ์ •ํ•œ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **F1 ๋‚ ์งœ ์˜์กด ํ…Œ์ŠคํŠธ (`tests/test_issue21_acceptance.py`)**: `test_a8` ๊ฐ€ ๋งค๋‹ฌ 1~9 ์ผ์— ์‹คํŒจํ–ˆ๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ๋งˆ์ปค์— ์“ฐ๋Š” `ps -o lstart` ๋Š” `Oct 1` ์ฒ˜๋Ÿผ ๊ณต๋ฐฑ ๋‘ ์นธ์ธ๋ฐ `loop_lock.sh` ๋Š” ๊ณต๋ฐฑ์„ ํ•œ ์นธ์œผ๋กœ ์ค„์—ฌ ๋น„๊ตํ•œ๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ์ œํ’ˆ๊ณผ ๊ฐ™์€ ์ •๊ทœํ™”๋กœ ์“ฐ๊ณ , ๋‘ ์นธ ๊ณต๋ฐฑ ๋งˆ์ปค๋กœ ๋‚ ์งœ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ณ ์ •ํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋”ํ–ˆ๋‹ค. ์ œํ’ˆ ์ฝ”๋“œ๋Š” ๊ทธ๋Œ€๋กœ. 2. **F2 Python ์ง„์ž…์ ์˜ ์„ค์ • ํŒŒ์ผ ์˜ค๋ฅ˜ (`mqtt_common.load_env_or_report`)**: ์กด์žฌํ•˜์ง€๋งŒ ์ฝ์„ ์ˆ˜ ์—†๋Š” `.mam.env` ์—์„œ publish_eventยทregistryยทjob_subscriberยทcliยทbenchยทheartbeatยทremote ๊ฐ€ ํŠธ๋ ˆ์ด์Šค๋ฐฑ ๋Œ€์‹  `ERROR: <๊ฒฝ๋กœ>: cannot read (<์˜ˆ์™ธ ์ข…๋ฅ˜>)` ํ•œ ์ค„๊ณผ rc 1. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜๋Š” ์ข…๋ฃŒํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฆฌ๋ทฐ(codex R1)์—์„œ ์‹คํŒจ๋ฅผ "์ด๋ฏธ ์ฝ์Œ"์œผ๋กœ ์บ์‹œํ•ด ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค์˜ ์žฌํ˜ธ์ถœ์ด ๊ฒ€์‚ฌ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๋˜ ๋ฌธ์ œ๋ฅผ ์ฐพ์•„, ์ฝ๊ธฐ์— ์„ฑ๊ณตํ–ˆ์„ ๋•Œ๋งŒ ์บ์‹œํ•œ๋‹ค. 3. **F3 `.mam.env` ์„ธ๋ถ€ (`envfile.py`, `.mam.env.example`, `MESSAGING.md`)**: ์ฒซ ์ค„ UTF-8 BOM ์„ ๋ฒ—๊ธด๋‹ค. ํ•œ ํŒŒ์ผ ์•ˆ ์ค‘๋ณต ํ‚ค๋Š” ์ฒซ ๊ฐ’์ด ์ด๊ธด๋‹ค๊ณ  ๋ช…์‹œ. "๋ช…์‹œํ•œ `MAM_ENV_FILE` ์ด ์—†์œผ๋ฉด ์˜ค๋ฅ˜" ์„ค๋ช…์„ ์‹ค์ œ(์—†์œผ๋ฉด ๊ฑด๋„ˆ๋œ€, ์žˆ๋Š”๋ฐ ๋ชป ์ฝ์œผ๋ฉด ์˜ค๋ฅ˜)์— ๋งž์ท„๋‹ค. 4. **F4 ์ •์ˆ˜ ์„ค์ •๊ฐ’ ๊ทœ์น™ ํ™•์žฅ (`runner.py`, `lib_int.sh`)**: ๋Ÿฌ๋„ˆ์˜ `MAM_WAIT_GRACE_S`ยท`MAM_CHILD_WAIT_S`ยท`MAM_JOB_TREE_MAX` ๊ฐ€ `_positive_int` ๊ทœ์น™(์ž˜๋ชป๋œ ๊ฐ’์€ ๊ฒฝ๊ณ  ํ›„ ๊ธฐ๋ณธ๊ฐ’; `MAM_CHILD_WAIT_S=abc` ์˜ ValueError ์ œ๊ฑฐ). `[ -ge ]`/`[ -le ]`/`[ -gt ]`/`sleep` ์— ์“ฐ์ด๋˜ bash ์ •์ˆ˜ ๋…ธ๋ธŒ๋„ `mam_int_knob` ์œผ๋กœ. `mam_int`/`mam_int_knob` ์€ `lib.sh` ์—†์ด source ๋˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋„ ์“ฐ๋„๋ก `lib_int.sh` ๋กœ ๋ถ„๋ฆฌ. `MAM_WAIT_GRACE_S` ๋ฅผ loop SKILL.md ์— ๋ฌธ์„œํ™”. 5. **F5 ์ œํ’ˆ ์ฝ”๋“œ์˜ ํ…Œ์ŠคํŠธ ํ›… ์ œ๊ฑฐ (`stall_watch.py`)**: `MAM_TEST_UNRELIABLE_AGENT` ๋ถ„๊ธฐ๋ฅผ ์—†์• ๊ณ , S13 ์€ ํ…Œ์ŠคํŠธ seam ์œผ๋กœ ๊ฐ™์€ ์ƒํ™ฉ(`agent_status_reliable=False`)์„ ๋งŒ๋“ ๋‹ค. 6. **F6 ๋ฌธ์„œยทํ…Œ์ŠคํŠธ ์ •๋ฐ€๋„**: ๊ฐ์‹œ์ž ์ˆ˜๋ช… ๊ณ„์•ฝ์˜ "โ‰ค 60 s" ๋ฅผ herdr wait ์— ํ•œ์ •. "๋Œ€๊ธฐ ์ค‘ ๋ž˜ํผ ์†Œ๋ฉธ" ํ…Œ์ŠคํŠธ๊ฐ€ ๋‘ ๋ฒˆ์งธ wait ์•ˆ์—์„œ ์†Œ๋ฉธ์„ ์ผ์œผํ‚จ๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **ํšŒ๊ท€ ํ…Œ์ŠคํŠธ**: ์ œํ’ˆ ์ฝ”๋“œ๋ฅผ ๋ฐ”๊พธ๋Š” ํ•ญ๋ชฉ(F2ยทF3ยทF4ยทF5)์€ ์ œํ’ˆ ํŒŒ์ผ๋งŒ main ์œผ๋กœ ๋˜๋Œ๋ฆฐ ์‚ฌ๋ณธ์—์„œ 47 ๊ฑด ์‹คํŒจ(claude ๋…๋ฆฝ ํ™•์ธ). F2 ์žฌํ˜ธ์ถœ ์บ์‹œ ํšŒ๊ท€๋Š” ๋ณ„๋„. - **๋ฆฌ๋ทฐ (์ด ์›Œํฌ์ŠคํŽ˜์ด์Šค, ๋„ค ๋ฆฌ๋ทฐ์–ด)**: 1ํšŒ์ฐจ claudeยทagyยทgrok PASS, codex NOT PASS(R1 ์‹คํŒจ ์บ์‹œ). 75f364e ์—์„œ ์ˆ˜์ •. 2ํšŒ์ฐจ **4/4 PASS**. ๋ฆฌํฌํŠธ์™€ 2ํšŒ์ฐจ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`, creator ๋ฆฌํฌํŠธ๋Š” `.agents/reports/creator-claude-01/`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ธŒ๋กœ์ปค ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด, ๋ณ„๋„ ์›ŒํฌํŠธ๋ฆฌ)**: 75f364e ์ „์ฒด **1380 passed / 0 failed / 3 skipped**(61 ๋ถ„, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด). ๋งค๋‹ฌ 1~9 ์ผ์— ์‹คํŒจํ•˜๋˜ `test_a8` ์ด 10์›” 3์ผ์— ํ†ต๊ณผ(F1 ํ™•์ธ). ์ฒซ ์‹คํ–‰์€ ๋งฅ ์ด๋™์œผ๋กœ 93% ์—์„œ ์ค‘๋‹จ๋ผ ๋‹ค์‹œ ๋Œ๋ ธ๋‹ค. - **ํ›„์†**: `tests/test_o2_race_free_lock.py` ์˜ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š” `get_lstart` ํ—ฌํผ(์ •๊ทœํ™” ์—†์Œ) ์ •๋ฆฌ(claude T1); `reconcile.sh` ๋‚ด์žฅ ๊ตฌ๋…์ž๋Š” ์ง„์ž…์  ๋ชฉ๋ก ๋ฐ–์ด๋ผ ์ฝ๊ธฐ ์‹คํŒจ ์‹œ ์—ฌ์ „ํžˆ ํŠธ๋ ˆ์ด์Šค๋ฐฑ(claude T2). ### ๐Ÿš€ `v4.8.1` โ€” Issue #26 `.mam.env` ๋ฅผ Python ํŒŒ์„œ ํ•˜๋‚˜๋กœ ์ฝ๊ณ , ์ •์ˆ˜ ์„ค์ •๊ฐ’์„ 10์ง„์ˆ˜๋กœ, ํ…Œ์ŠคํŠธ๋ฅผ ๋กœ์ปฌ MQTT ๋ธŒ๋กœ์ปค๋กœ (2026-10-02, PR #43) > **ํŒจ์น˜ (PATCH Release)**: ๊ฐ™์€ `.mam.env` ๋ฅผ delegate-job ๋ž˜ํผ๋Š” bash `source` ๋กœ, Python ๋„๊ตฌ๋Š” `KEY=VALUE` ๋กœ ์ฝ์–ด์„œ, ํ•œ์ชฝ์—์„œ ์ •์ƒ์ธ ์ค„(`KEY= value`)์ด ๋‹ค๋ฅธ ์ชฝ์—์„œ ๋ž˜ํผ๋ฅผ ์ฆ‰์‹œ ์ข…๋ฃŒ์‹œํ‚ค๊ณ  ๊ฐ’ ์† `$(...)` ๋Š” ์‹คํ–‰๋˜๋˜ ๋ฌธ์ œ(Issue #26)๋ฅผ ๊ณ ์นœ๋‹ค. 0 ์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ •์ˆ˜ ์„ค์ •๊ฐ’์ด bash ์—์„œ 8์ง„์ˆ˜๋กœ ์ฝํžˆ๋˜ ๋ฌธ์ œ์™€, ๊ณต๊ฐœ MQTT ๋ธŒ๋กœ์ปค์— ๊ธฐ๋Œ€๋˜ ํ…Œ์ŠคํŠธ๋„ ํ•จ๊ป˜ ์ •๋ฆฌํ–ˆ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **ํŒŒ์„œ๋Š” ํ•˜๋‚˜ (`lib_py/delegation/envfile.py`, ๋ž˜ํผ, `mqtt_common.py`)**: ์ฃผ์„ยท๋นˆ ์ค„ ๋ฌด์‹œ, ํ‚ค์™€ ๊ฐ’์˜ ์•ž๋’ค ๊ณต๋ฐฑ ์ œ๊ฑฐ, ๊ฐ์‹ธ๋Š” ๋”ฐ์˜ดํ‘œ ํ•œ ์Œ ์ œ๊ฑฐ, ์…ธ ํ•ด์„ ์—†์Œ. ํ‚ค๋Š” `[A-Za-z_][A-Za-z0-9_]*`. ํ˜•์‹์ด ์–ด๊ธ‹๋‚œ ์ค„, `export` ์ ‘๋‘์–ด, NUL ์ด ๋“  ์ค„, bash ์ž์ฒด ๋ณ€์ˆ˜(`IFS`, `PS4`, `RANDOM`, `BASH_*` ๋“ฑ)๋Š” ๊ฐ’ ์—†์ด ์ค„ ๋ฒˆํ˜ธ๋กœ ๊ฒฝ๊ณ ํ•˜๊ณ  ๊ฑด๋„ˆ๋›ด๋‹ค. ๋ž˜ํผ๋Š” `source` ๋Œ€์‹  ํŒŒ์„œ๊ฐ€ ๋งŒ๋“  ์…ธ ์ธ์šฉ `export` ๋ฌธ์„ eval ํ•œ๋‹ค. ์ด๋ฏธ ์žˆ๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ํŒŒ์ผ๋ณด๋‹ค ์šฐ์„ ํ•œ๋‹ค(์ „์—๋Š” ๋ž˜ํผ๋งŒ ํŒŒ์ผ์ด ๋ฎ์–ด์ผ๋‹ค). ์กด์žฌํ•˜์ง€๋งŒ ์ฝ์„ ์ˆ˜ ์—†๋Š” ํŒŒ์ผ์€ ์˜ค๋ฅ˜๋‹ค: ๋ž˜ํผ๋Š” rc 1, Python ์ง„์ž…์ ์€ `EnvFileError` ๋กœ ๋ฉˆ์ถ˜๋‹ค. 2. **์ •์ˆ˜ ์„ค์ •๊ฐ’์€ 10์ง„์ˆ˜ (`lib.sh` `mam_int`/`mam_int_knob`, `runner.py` `_positive_int`)**: `MAM_JOB_TIMEOUT_S=0700` ์ด ๋ž˜ํผ์—์„œ 448 ์ดˆ๊ฐ€ ๋˜๋˜ ๋ฌธ์ œ. ํ•œ ๊ทœ์น™(ASCII ๊ณต๋ฐฑยท์ˆซ์ž๋งŒ, 10์ง„์ˆ˜, ์œ ํšจ 9 ์ž๋ฆฌ ์ดํ•˜, ๊ฐ’๋ณ„ ์ตœ์†Ÿ๊ฐ’)์„ ๋กœ์ผ€์ผ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ์ ์šฉํ•œ๋‹ค. ์ž˜๋ชป๋œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋Š” ๊ฒฝ๊ณ  ํ›„ ๊ธฐ๋ณธ๊ฐ’, ์ž˜๋ชป๋˜๊ฑฐ๋‚˜ ๋นˆ ๊ฐ’์œผ๋กœ ๋ช…์‹œํ•œ ํ”Œ๋ž˜๊ทธ๋Š” rc 1. 3. **ํ…Œ์ŠคํŠธ๊ฐ€ ์™ธ๋ถ€ ๋ธŒ๋กœ์ปค์— ๊ธฐ๋Œ€์ง€ ์•Š๋Š”๋‹ค (`tests/_local_mqtt.py`)**: ๊ณต๊ฐœ `broker.hivemq.com` ์™•๋ณต์— ๊ธฐ๋Œ€๋˜ ํ…Œ์ŠคํŠธ 22 ๊ฐœ๋ฅผ ํ…Œ์ŠคํŠธ ์•ˆ์˜ ๋กœ์ปฌ MQTT 3.1.1 ๋ธŒ๋กœ์ปค๋กœ ์˜ฎ๊ฒผ๋‹ค. "๋ธŒ๋กœ์ปค์— ๋‹ฟ์ง€ ์•Š์Œ" ๊ฑด๋„ˆ๋›ฐ๊ธฐ๋Š” ์‹คํŒจ๊ฐ€ ๋๊ณ , ์ •์  ๊ฒ€์‚ฌ๊ฐ€ ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค ์ฃผ์†Œ์˜ ์žฌ์œ ์ž…์„ ๋ง‰๋Š”๋‹ค. 4. **๋ฌธ์„œ**: `.mam.env.example` ๋จธ๋ฆฌ๋ง์— ํ˜•์‹ ๊ทœ์น™๊ณผ ์šฐ์„ ์ˆœ์œ„. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ**: `tests/test_issue26_envfile.py`(๋‘ ์‹ค์ œ ๋กœ๋”์˜ ๊ฐ™์€ ์ž…๋ ฅยท๊ฐ™์€ ๊ฒฐ๊ณผ, ์˜ˆ์•ฝ ๋ณ€์ˆ˜, ์ฝ๊ธฐ ์‹คํŒจยทNUL, ๋ž˜ํผ์—์„œ ๋ช…๋ น ๋ฏธ์‹คํ–‰), `tests/test_issue26_ints.py`(๋ž˜ํผยทlib.shยท๋Ÿฌ๋„ˆ์˜ ์ •์ˆ˜ ๊ทœ์น™ ํ‘œ, ๋‘ ๋กœ์ผ€์ผ), `tests/test_local_mqtt.py`(๋กœ์ปฌ ๋ธŒ๋กœ์ปค์˜ 3.1.1 ์ ํ•ฉ์„ฑ๊ณผ ์‹ค์ œ publish_event โ†’ job_subscriber ๊ฒฝ๋กœ). - **๋ฆฌ๋ทฐ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค)**: 1ํšŒ์ฐจ(@b58edac) claudeยทagy PASS, codex NOT PASS(R1 ์ฝ์„ ์ˆ˜ ์—†๋Š” ํŒŒ์ผ์ธ๋ฐ ๋ž˜ํผ๊ฐ€ rc 0 ์œผ๋กœ ๊ธฐ๋ณธ๊ฐ’ ์ง„ํ–‰, R2 ๋กœ์ผ€์ผ์— ๋”ฐ๋ผ bashยทPython ์˜ ์ •์ˆ˜ ํ•ด์„์ด ๋‹ค๋ฆ„, R3 ๋นˆ ๊ฐ’์œผ๋กœ ๋ช…์‹œํ•œ ์ •์ˆ˜ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ฒ€์ฆ์„ ์šฐํšŒ, R4 NUL ์ด ๋“  ๊ฐ’์„ ๋‘ ๋กœ๋”๊ฐ€ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ), grok ์€ ์ปจํ…์ŠคํŠธ ์••์ถ• ์ค‘ ์‘๋‹ต ์‹œ๊ฐ„ ์ดˆ๊ณผ๋กœ ํŒ์ • ์—†์Œ(๋Šฆ๊ฒŒ ๋‚ธ ๋ฆฌํฌํŠธ๋„ R1 ๊ณผ ๊ฐ™์€ ์ง€์ ). 0eec795 ์—์„œ ๋„ค ๊ฑด๊ณผ claude ๋น„์ฐจ๋‹จ R1ยทR2(๋ฌธ์„œยท๋ฉ”์‹œ์ง€)๋ฅผ ๊ณ ์น˜๊ณ  ํšŒ๊ท€ ํ…Œ์ŠคํŠธ 8 ๊ฑด(๋ชจ๋‘ ์ˆ˜์ • ์ „ ์ฝ”๋“œ์—์„œ ์‹คํŒจ)์„ ์ถ”๊ฐ€. 2ํšŒ์ฐจ(@0eec795, grok ์€ PR ์ „์ฒด) **4/4 PASS**. ๋„ค ๋ฆฌ๋ทฐ์–ด ๋ชจ๋‘ "์ฝ์„ ์ˆ˜ ์—†๋Š” ํŒŒ์ผ์ด๋ฉด Python ์ง„์ž…์ ๋„ ๋ฉˆ์ถ˜๋‹ค"์— ๋™์˜. ๋ฆฌํฌํŠธ์™€ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ๋ธŒ๋กœ์ปค ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด)**: 0eec795 ์ „์ฒด 1315 passed / 1 failed / 3 skipped(60 ๋ถ„, MQTT ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์ด). ์‹คํŒจ 1๊ฑด์€ `test_issue21_acceptance.py::test_a8` ๋กœ ์ด PR ๊ณผ ๋ฌด๊ด€ํ•œ ๋‚ ์งœ ์˜์กด ํ…Œ์ŠคํŠธ ๋ฒ„๊ทธ(main ์—์„œ๋„ ๊ฐ™์Œ, ์•„๋ž˜ ํ›„์†). 1ํšŒ์ฐจ head b58edac ์ „์ฒด๋„ 1302 passed / ๊ฐ™์€ 1๊ฑด ์‹คํŒจ. ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค ๋•Œ๋ฌธ์— ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ๊ฑด๋„ˆ๋›ด ํ…Œ์ŠคํŠธ ์—†์Œ. PR ๋ณธ๋ฌธ์˜ "purge 12๊ฑด ๊ธฐ์ค€ ์‹คํŒจ"๋Š” ์ด ํ˜ธ์ŠคํŠธ์—์„œ ์žฌํ˜„๋˜์ง€ ์•Š์Œ. - **ํ›„์†**: Python ์ง„์ž…์ ์˜ `EnvFileError` ๋ฅผ ํŠธ๋ ˆ์ด์Šค๋ฐฑ ๋Œ€์‹  `ERROR:` ํ•œ ์ค„๋กœ(claude S1); ํŒŒ์ผ ์ฒซ ์ค„์˜ BOM ์ด "์ž˜๋ชป๋œ ํ‚ค"๋กœ ๊ฑด๋„ˆ๋›ฐ์–ด์ง(grok); ํ•œ ํŒŒ์ผ ์•ˆ ์ค‘๋ณต ํ‚ค๋Š” ์ฒซ ๊ฐ’์ด ์ด๊น€ โ€” ๋ฌธ์„œํ™”(grok); `MESSAGING.md` ์˜ "์—†๋Š” `MAM_ENV_FILE` ์€ ์˜ค๋ฅ˜" ์„ค๋ช…์ด ์‹ค์ œ(์กฐ์šฉํžˆ ๊ฑด๋„ˆ๋œ€)์™€ ๋‹ค๋ฆ„(grok); `MAM_WAIT_GRACE_S`ยท`MAM_CHILD_WAIT_S`ยท`MAM_JOB_TREE_MAX` ๋ฅผ `_positive_int` ๋กœ(grok); `[ -ge ]` ์ „์šฉ ๋…ธ๋ธŒ์˜ ๋น„์ˆซ์ž ๊ฐ’(claude R3); `mam_int` ์™€ ํ”Œ๋ž˜๊ทธ ๊ฒ€์ฆ์„ #17 ํ•ญ๋ชฉ 4 ์ด์ „ ๋ชฉ๋ก์—(claude R5); `test_issue21_acceptance.py::test_a8` ๊ฐ€ ๋งค๋‹ฌ 1~9 ์ผ์— ์‹คํŒจ โ€” ํ…Œ์ŠคํŠธ๊ฐ€ ๋งˆ์ปค์— ์“ฐ๋Š” `ps -o lstart` ์˜ ๋‘ ์นธ ๊ณต๋ฐฑ(`Oct 1`)์„ ์ œํ’ˆ ์ฝ”๋“œ์ฒ˜๋Ÿผ ํ•œ ์นธ์œผ๋กœ ์ค„์ด์ง€ ์•Š๋Š”๋‹ค(์ œํ’ˆ ๋™์ž‘์€ ์ •์ƒ). ### ๐Ÿš€ `v4.8.0` โ€” Issue #33 ์œ„์ž„ ์žก ์˜ˆ์‚ฐ ์กฐ์ ˆ๊ณผ ๋ฒฝ์‹œ๊ณ„ ๋ผ๋ฒจ ๋ถ„๋ฆฌ, Issue #30 ๋ฉˆ์ถ˜ ์—์ด์ „ํŠธ ์•Œ๋ฆผ(์•Œ๋ฆผ ์ „์šฉ) (2026-09-26, PR #41) > **๋งˆ์ด๋„ˆ ๋งˆ์ผ์Šคํ†ค (MINOR Release)**: ์œ„์ž„ ์žก์˜ ๋ฒฝ์‹œ๊ณ„ ์˜ˆ์‚ฐ(๊ธฐ๋ณธ 3600 ์ดˆ)์„ ์กฐ์ ˆํ•  ์ˆ˜ ์—†๊ณ  ๋ฒฝ์‹œ๊ณ„ ์ดˆ๊ณผ์™€ ๋ฌด์‘๋‹ต์ด ๊ฐ™์€ `deadline_timeout` ์œผ๋กœ ๊ธฐ๋ก๋˜๋˜ ๋ฌธ์ œ(Issue #33)์™€, ์‚ด์•„ ์žˆ์ง€๋งŒ ๋ฉˆ์ถ˜ ์—์ด์ „ํŠธ(๊ถŒํ•œ ํ™•์ธ ์ฐฝ์— ๋ง‰ํž˜, ์ข…๊ฒฐ ์ด๋ฒคํŠธ ์—†์ด ํ„ด ์ข…๋ฃŒ)๋ฅผ ์žก ์ œํ•œ ์‹œ๊ฐ„์ด ์ง€๋‚˜์•ผ๋งŒ ์•Œ ์ˆ˜ ์žˆ๋˜ ๋ฌธ์ œ(Issue #30)๋ฅผ ํ•œ ๋ธŒ๋žœ์น˜๋กœ ๊ณ ์ณค๋‹ค. ์ƒˆ ๋ชจ๋“ˆ `lib_py/stall_watch.py`, ๋ฃจํ”„ ์˜ต์…˜ `--job-timeout`, ์ƒˆ ์ข…๊ฒฐ ์‚ฌ์œ  `wall_clock_timeout`, ๊ตฌ๋…์ž ์ข…๋ฃŒ ์ฝ”๋“œ 4 ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด MINOR ๋กœ ์˜ฌ๋ฆฐ๋‹ค. ์˜ˆ์‚ฐ **์—ฐ์žฅ**์€ ๋ฒ”์œ„์—์„œ ๋นผ๊ณ  #39(์—์ด์ „ํŠธ๊ฐ€ ํ”„๋กœํ† ์ฝœ ์ด๋ฒคํŠธ๋กœ ์š”์ฒญ)๋กœ ๋ถ„๋ฆฌํ–ˆ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **์žก ์˜ˆ์‚ฐ์ด ์ด๋™ํ•œ๋‹ค (#33, delegate-job ๋ž˜ํผ, `lib_py/loop/runner.py`, `remote.py`)**: ๋ž˜ํผ์˜ ์˜ˆ์‚ฐ์€ `--timeout` > `MAM_JOB_TIMEOUT_S` > 3600. ๋ฃจํ”„๋Š” `--job-timeout ` ์„ ๋ฐ›์•„ ๋ชจ๋“  ์œ„์ž„ ์žก์— ๋„˜๊ธด๋‹ค. ๋Ÿฌ๋„ˆ์˜ ์žก ๋Œ€๊ธฐ ์ƒํ•œ๊ณผ #34 ํ•˜์œ„ ์žก ๋Œ€๊ธฐ(`MAM_CHILD_WAIT_S`) ๊ธฐ๋ณธ๊ฐ’์€ ์ƒ์ˆ˜ 3900 ์ด ์•„๋‹ˆ๋ผ **์žก ์˜ˆ์‚ฐ + 300 ์ดˆ**๋กœ ๊ณ„์‚ฐํ•œ๋‹ค(๋ช…์‹œ๊ฐ’์€ ์กด์ค‘). ์˜ˆ์‚ฐ์€ ์žก ํ•˜๋‚˜ ๋‹จ์œ„๋‹ค. ์›๊ฒฉ ์žก์„ ํฌํ•จํ•œ ๋ชจ๋“  transport ์— ์ ์šฉ. 2. **๋‘ ์‹œ๊ณ„๊ฐ€ ๋‹ค๋ฅธ ๋ผ๋ฒจ์„ ๋ฐ›๋Š”๋‹ค (#33, `job_subscriber.py`, ๋ž˜ํผ, `registry.py`, `engine.py`)**: ๊ตฌ๋…์ž๋Š” ์›๋ž˜ ๋‘ ์‹œ๊ณ„๋ฅผ ๋”ฐ๋กœ ์…Œ์ง€๋งŒ ๋‘˜ ๋‹ค rc 2 ๋ฅผ ๋Œ๋ ค์ค˜ ๋ž˜ํผ๊ฐ€ ๋ชจ๋‘ `deadline_timeout` ์œผ๋กœ ๊ธฐ๋กํ–ˆ๋‹ค. ์ด์ œ ๋ฒฝ์‹œ๊ณ„ ์ดˆ๊ณผ๋Š” rc 4 โ†’ `wall_clock_timeout`, ๋ฌด์‘๋‹ต(Kร—N ์ดˆ ์ด๋ฒคํŠธ ์—†์Œ)์€ rc 2 โ†’ `deadline_timeout`. ์ข…๋ฃŒ ์ฝ”๋“œ๋Š” ๊ตฌ๋…์ž์™€ ๋ž˜ํผ ์–‘์ชฝ์—์„œ ์ด๋ฆ„ ๋ถ™์€ ์ƒ์ˆ˜(`RC_*` / `SUB_RC_*`)๋กœ ์ฐธ์กฐํ•˜๊ณ  ๋‘ ์ชฝ ๊ฐ’์˜ ์ผ์น˜๋ฅผ ํ…Œ์ŠคํŠธ๋กœ ๊ณ ์ •. ๋””์Šคํฌ heartbeat ์œ ์˜ˆ๊ฐ€ deadline ์— ๋‹ฟ์•„ ๋๋‚˜๋Š” ๊ฒฝ์šฐ๋„ ๋ฒฝ์‹œ๊ณ„(`FINALIZE_CLOCK`). ๋“ฑ๋ก๋ถ€์˜ `close --request` ์ข…๋ฅ˜์™€ #34 ํ•˜์œ„ ์žก ํ‘œ์˜ `CLOSED-UNCONFIRMED` ๋ถ„๋ฅ˜์— ์ƒˆ ๋ผ๋ฒจ ๋ฐ˜์˜. `delegate-job wait` ๋Š” ๋‘ ์‹œ๊ณ„ ๋ชจ๋‘ ๊ธฐ์กด๋Œ€๋กœ rc 2(ํ•˜์œ„ ํ˜ธํ™˜). 3. **๋ฉˆ์ถค ๊ฐ์‹œ์ž โ€” ์•Œ๋ฆผ ์ „์šฉ (#30, `lib_py/stall_watch.py`)**: ๋ž˜ํผ ์˜†์—์„œ ์žก์˜ ์—์ด์ „ํŠธ pane ์„ herdr `agent wait` ๋กœ ์ง€์ผœ๋ณธ๋‹ค. **์žก์„ ๋‹ซ๋Š” ์ฝ”๋“œ ๊ฒฝ๋กœ๊ฐ€ ์—†๋‹ค** โ€” ์œ ์ผํ•œ ๋ฐœํ–‰์€ `progress` ์ด๋ฒคํŠธ(`_notify`)์™€ ์žฌ์ฃผ์ž… ํ•œ ๋ฒˆ์ด๋ฉฐ ํ…Œ์ŠคํŠธ๋กœ ๊ณ ์ •. `blocked` โ†’ `progress --detail stalled:blocked` + herdr detection ํ™”๋ฉด์˜ ๋ฌธ๊ตฌ ์ฒจ๋ถ€, **์ž๋™ ์‘๋‹ต ์—†์Œ**(#30 ์˜ ๋™๊ธฐ ์‚ฌ๋ก€๊ฐ€ "Dangerous rm operationโ€ฆ proceed?" ์ฐฝ). `idle` ์ด `MAM_STALL_IDLE_S`(๊ธฐ๋ณธ 600 ์ดˆ) ์ด์–ด์ง€๊ณ  ์—์ด์ „ํŠธ์—๊ฒŒ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž์‹ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์—†๊ณ  ์ž…๋ ฅ์ฐฝ์ด ๋น„์–ด ์žˆ์œผ๋ฉด โ†’ `stalled:idle` + "์ด์–ด์„œ ํ•˜๋ผ" ์‹ ํ˜ธ ํ•œ ๋ฒˆ(์žก๋‹น 1ํšŒ). ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž์‹์€ herdr ๋กœ ์–ป์€ ์—์ด์ „ํŠธ pid ์˜ OS ์ž์†์œผ๋กœ ํŒ์ •ํ•˜๊ณ (Claude Code ๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ช…๋ น์„ ๋ณ„๋„ ํ”„๋กœ์„ธ์Šค ๊ทธ๋ฃน์œผ๋กœ ๋„์›Œ herdr ์—๋Š” ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค), ์ƒ์ฃผ ์ž์‹(codex ์˜ `codex-code-mode-host` ๋“ฑ)์€ ์–ด๋Œ‘ํ„ฐ ํ•„๋“œ(`resident_children`)๋กœ ์ œ์™ธ. `agent_status` ์‹ ๋ขฐ๋„๋„ ์–ด๋Œ‘ํ„ฐ ํ•„๋“œ. ์›๊ฒฉยทhuman ์žก์€ ๊ฐ์‹œํ•˜์ง€ ์•Š๊ณ  ๋กœ๊ทธ ํ•œ ์ค„. herdr ์‹คํŒจ๋Š” `MAM_STALL_HERDR_FAIL_S`(120 ์ดˆ) ๋’ค ๊ฒฝ๊ณ . `MAM_STALL_WATCH=0` ์œผ๋กœ ๋”. 4. **์–ด๋Œ‘ํ„ฐยท์ž…๋ ฅ์ฐฝ (`lib_py/agents/*`, `input_region.py`)**: ์ƒ์ฃผ ์ž์‹ ๋ชฉ๋ก๊ณผ `agent_status` ์‹ ๋ขฐ๋„ ํ•„๋“œ, ์ž…๋ ฅ์ฐฝ์ด ๋น„์—ˆ๋Š”์ง€ ํŒ์ •(์—ฌ๋Ÿฌ ์ค„ ์ดˆ์•ˆ, ํ”„๋กฌํ”„ํŠธ ๊ธ€๋ฆฌํ”„๊ฐ€ ์ดˆ์•ˆ ์•ˆ์— ์žˆ๋Š” ๊ฒฝ์šฐ). pane ํ”ฝ์Šค์ฒ˜ 5์ข…. 5. **๋ฌธ์„œ**: delegate-job/loop SKILL.md ์— `--job-timeout`, `MAM_JOB_TIMEOUT_S`, `wall_clock_timeout`, ๊ฐ์‹œ์ž ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ–‰. #### ๐Ÿงญ ์„ค๊ณ„ ๊ฒฐ์ • (์ด์Šˆ ๋Œ“๊ธ€์˜ ๊ณ„ํš์—์„œ ๋‹ฌ๋ผ์ง„ ์ ) - ์˜ˆ์‚ฐ ์—ฐ์žฅ(๋ฒฝ์‹œ๊ณ„์— ๋‹ฟ์œผ๋ฉด ๋ฉˆ์ถฐ์„œ ๋ฌป๊ณ  ์ •์ฑ…์ด ์—ฐ์žฅ)์€ ๋บ๋‹ค: ๋ฃจํ”„ ๋Ÿฌ๋„ˆ๊ฐ€ ๋ž˜ํผ๋ฅผ ๋™๊ธฐ ํ˜ธ์ถœํ•ด ๊ฒฐ์ •์„ ์“ธ ์œ„์น˜์— ์—†๊ณ , ์—ฐ์žฅ ๊ฑฐ๋ถ€ ๊ทผ๊ฑฐ๋กœ ์ œ์•ˆ๋œ `state_change_seq` ์ •์ฒด๋Š” ํ•œ ์‹œ๊ฐ„ ๋„˜๊ฒŒ ๊ฐ™์€ ์ƒํƒœ๋กœ ์ „์ฒด ์Šค์œ„ํŠธ๋ฅผ ๋„๋Š” ์ •์ƒ creator ๋„ ๊ฑฐ๋ถ€ํ•œ๋‹ค. #39 ๋Š” ์—์ด์ „ํŠธ๊ฐ€ ์Šค์Šค๋กœ ์—ฐ์žฅ์„ ์š”์ฒญํ•˜๋Š” ๋ฐฉ์‹์„ ๋‹ค๋ฃฌ๋‹ค. - ๋ฉˆ์ถค ํŒ์ •์€ ์•Œ๋ฆผ ์ „์šฉ(#30 ๋Œ“๊ธ€ ์ˆ˜์ •๋ณธ, 2026-09-25): ์žฌ์ฃผ์ž… ์‹คํŒจ ํ›„ `stalled` ์ข…๊ฒฐ๊ณผ ๋‹ค์ด์–ผ๋กœ๊ทธ ์ž๋™ ์ˆ˜๋ฝ์€ ์ฒ ํšŒ. - #17 ํ•ญ๋ชฉ 4(๋ž˜ํผ ์ข…๊ฒฐ ์ฒ˜๋ฆฌ์˜ Python ์ด์ „)๋Š” ํ•˜์ง€ ์•Š์•˜๋‹ค. bash ๋ณ€๊ฒฝ์€ ๋ผ๋ฒจ์„ ๋‚˜๋ฅด๋Š” ๋ถ€๋ถ„์œผ๋กœ ์ตœ์†Œํ™”. ๋ฆฌ๋ทฐ์–ด ํ•ฉ์˜(Aโ€“D, claudeยทagyยทgrokยทcodex): A ์•Œ๋ฆผ ์ „์šฉ 4/4 ๋™์˜. B #39 ๋ถ„๋ฆฌ ๋™์˜ โ€” ์—ฐ์žฅ ํšŸ์ˆ˜ยท๋ˆ„์  ์˜ˆ์‚ฐ ์ƒํ•œ์€ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ์ชฝ, token/attempt ๋กœ ์ธ์ฆ๋œ ์š”์ฒญ๋งŒ ๋ฉฑ๋“ฑ ์ฒ˜๋ฆฌํ•ด ์ƒˆ ์ ˆ๋Œ€ deadline ํ™•์ •, heartbeat ๋งŒ ์žˆ๊ณ  `state_change_seq` ๊ฐ€ ๊ทธ๋Œ€๋กœ๋ฉด ๊ฑฐ์ ˆ, ์›๊ฒฉ ์žก๋„ ๊ฐ™์€ ๊ทœ์น™. C #40 ํŠธ๋ฆฌ๊ฑฐ๋Š” `completed` ๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ์ˆ˜์‹  ์ธก โ€” ๋ž˜ํผ ๊ตฌ๋…์ž๊ฐ€ ๊ธฐ๋ณธ ์ง„์ž…์ , ๋ฃจํ”„ `wait_for_job` ์€ ๋ˆ„๋ฝ ๋ณต๊ตฌ ๊ฒฝ๋กœ(job/token/attempt ๋‹จ์œ„ ์ค‘๋ณต ๋ฐฉ์ง€), #17 ํ•ญ๋ชฉ 4 ์— ํก์ˆ˜ํ•˜๊ฑฐ๋‚˜ ๋ฐ”๋กœ ๋‹ค์Œ. D ํ•ญ๋ชฉ 4 ๋ฅผ ๋ฏธ๋ฃฌ ๊ฒƒ ๋™์˜ โ€” ์˜ฎ๊ธธ bash ํŒ์ •: `FINALIZE_CLOCK`ยท๋””์Šคํฌ ์œ ์˜ˆ ์ข…๋ฃŒ ๊ฒฐ์ •, ๊ฐ์‹œ ์—ฌ๋ถ€ ํŒ์ •, `MAM_JOB_TIMEOUT_S` ๊ฒ€์ฆ, rcโ†’close_req/๋ผ๋ฒจ ๋งคํ•‘, `_map_sub_rc`ยท`cmd_wait` ์ •๊ทœํ™”, ๊ฐ์‹œ์ž ์ˆ˜๋ช… ๊ด€๋ฆฌ. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ** (`tests/test_issue30_stall_watch.py` 789์ค„, `tests/test_issue33_budget.py` 358์ค„): ์˜ˆ์‚ฐ ์šฐ์„ ์ˆœ์œ„ยท์ „๋‹ฌยท๋Œ€๊ธฐ ์ƒํ•œ, ๋‘ ์‹œ๊ณ„์˜ ๋ผ๋ฒจ๊ณผ rc ์ƒ์ˆ˜ ์ผ์น˜, ๊ฐ์‹œ์ž์˜ ํŒ์ •(`judge`)ยท๋ฐœํ–‰์ด `progress` ๋ฟ์ž„ยท๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž์‹ยท์ž…๋ ฅ์ฐฝยท์žฌ์ฃผ์ž… 1ํšŒยท์›๊ฒฉ ์ œ์™ธยทherdr ์‹คํŒจ. ๊ธฐ์กด ํ…Œ์ŠคํŠธ ์กฐ์ •: `test_tier1_unit.py` G-8(๋‘ ์‹œ๊ณ„๋ฅผ ๊ฐ™์€ ๊ฐ’์œผ๋กœ ์ฃผ๋ฉด ๋ฒฝ์‹œ๊ณ„๊ฐ€ ๋จผ์ € ๋‹ฟ์•„ ์ƒˆ ์ฝ”๋“œ๋ฅผ ๋ฐ›๋˜ ๊ฒฐ์ •์  ํšŒ๊ท€ โ†’ ๊ฐ’ ๋ถ„๋ฆฌ), registry CLI ์Šค๋ƒ…์ƒทยทhelp ์บก์ฒ˜, `test_issue18_*`, `conftest.py`. - **์‹ค์ œ ์—์ด์ „ํŠธ ์‹œํ—˜ (์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค, claude, `MAM_STALL_IDLE_S=45`)**: A. `started` ๋งŒ ๋ณด๋‚ด๊ณ  ์™„๋ฃŒ ์—†์ด ํ„ด์„ ๋๋‚ธ ์—์ด์ „ํŠธ โ†’ 48 ์ดˆ ๋’ค `stalled:idle` ๊ฒฝ๊ณ ์™€ ์‹ ํ˜ธ ํ•œ ๋ฒˆ, ์—์ด์ „ํŠธ๊ฐ€ ๋ฐ›์ž๋งˆ์ž `completed`. B. 150 ์ดˆ ๋ช…๋ น์„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ๋Œ๋ฆฌ๋ฉฐ ์‰ฌ๋Š” ์—์ด์ „ํŠธ โ†’ ๊ฒฝ๊ณ ๋„ ์‹ ํ˜ธ๋„ ์—†์ด ๋ช…๋ น์ด ๋๋‚œ ๋’ค ์Šค์Šค๋กœ `completed`. `blocked` ๋Š” ํ…Œ์ŠคํŠธ๋กœ๋งŒ ํ™•์ธ. - **๋ฆฌ๋ทฐ**: ์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค claudeยทagyยทgrokยทcodex ์ „์› PASS(opencode ๋Š” Ollama ํ•œ๋„๋กœ ์ฒซ ํšŒ์ฐจ PASS ๋’ค ํƒˆ๋ฝ). ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ PR head ๋ฅผ detached ์›ŒํฌํŠธ๋ฆฌ์— ๋ฐ›์•„ ์žฌ๊ฒ€ํ† : 1ํšŒ์ฐจ(@1673818) claude NOT PASS(N1: ๋ž˜ํผ๊ฐ€ ๊ฐ์‹œ์ž๋ฅผ ๋ฌดํ•œ wait), codex NOT PASS(R1: ๋ž˜ํผ๊ฐ€ TERM ์œผ๋กœ ์ฃฝ์œผ๋ฉด ๊ฐ์‹œ์ž ๊ณ ์•„, R2: herdr ๋Œ€๊ธฐ ์ค‘ ๋‹ซํžŒ ์žก์— ์žฌ์ฃผ์ž…), agyยทgrok PASS โ†’ af0a2a2 ์—์„œ ์œ ๊ณ„ stopยท`--watch-pid`ยท`_dispatch_open` ์žฌ๊ฒ€์‚ฌ. 2ํšŒ์ฐจ(๋ธํƒ€) claudeยทagy PASS, grok NOT PASS(TERM 5 ์ดˆ ์˜ˆ์‚ฐ์ด close ์‹œ๊ณ„๋ฅผ ๋Šฆ์ถค), codex NOT PASS(D1: ์•Œ๋ฆผ ๋ฐœํ–‰ ์ค‘ ๋‹ซํžŒยท์žฌ๋””์ŠคํŒจ์น˜๋œ ์žก์— ์žฌ์ฃผ์ž…, D2: ๋Œ€๊ธฐ ์ค‘ ๋ž˜ํผ ์†Œ๋ฉธ ๋’ค 1ํšŒ ์žฌ์ฃผ์ž…) โ†’ 9200562 ์—์„œ KILL ์ฆ‰์‹œ ์ •์ง€ยทprompt ์ง์ „ strict ๊ฒŒ์ดํŠธ(`_may_prompt`)ยทwait ์งํ›„ ๋ž˜ํผ ํ™•์ธ. 3ํšŒ์ฐจ(๋ธํƒ€ @9200562) 4/4 PASS. ํšŒ๊ท€ ํ…Œ์ŠคํŠธ `tests/test_issue30_stall_watch_stop.py` 11๊ฑด์€ ๋ชจ๋‘ ์ˆ˜์ • ์ „ ์ฝ”๋“œ์—์„œ hang/์‹คํŒจ. 2ํšŒ์ฐจ ์ฒซ ์‹คํ–‰์€ claude ์„ธ์…˜ ์‚ฌ์šฉ๋Ÿ‰ ํ•œ๋„๋กœ ๋Š๊ฒจ ๊ทธ ์žก์„ `cancelled` ๋กœ ๋‹ซ๊ณ  ๋‹ค์‹œ ๋Œ๋ ธ๋‹ค. ๋ฆฌํฌํŠธ์™€ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ์ž์ฒด venv ๊ฐ€ ์žˆ๋Š” ์›ŒํฌํŠธ๋ฆฌ)**: af0a2a2 ์ „์ฒด 1230 passed / 4 failed / 5 skipped(73 ๋ถ„) โ€” ์‹คํŒจ 3๊ฑด์€ ๋‹จ๋… ์žฌ์‹คํ–‰ ํ†ต๊ณผ, 1๊ฑด์€ B3(๊ณต๊ฐœ ๋ธŒ๋กœ์ปค heartbeat ์ง€์—ฐ, 1ํšŒ์ฐจ head ์—์„œ๋„ ๊ฐ™์Œ). ์ตœ์ข… head 9200562 ์˜ํ–ฅ ํŒŒ์ผ(stall_watch_stopยทstall_watchยทissue14ยทissue33ยทtier1ยทversion) 159 passed / 1 failed(B3) / 1 skipped(S6, Linux /proc). - **ํ›„์†**: #17 ํ•ญ๋ชฉ 4 ๋ชฉ๋ก(ํ•ฉ์˜ D), #39 ์—ฐ์žฅ ๋ณด์™„์ฑ…(ํ•ฉ์˜ B), #40 ํŠธ๋ฆฌ๊ฑฐ(ํ•ฉ์˜ C); `MAM_TEST_UNRELIABLE_AGENT` ํ…Œ์ŠคํŠธ ํ›…(M7); B3ยทS9ยทS10ยทissue14 idle-graceยทissue18:497 ์˜ ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค ์˜์กด(๋กœ์ปฌ events.log ๋กœ ๋‹จ์–ธํ•˜๊ฑฐ๋‚˜ ๋“ฑ๋ก๋ถ€ํ„ฐ ๋ธŒ๋กœ์ปค ๊ฒฉ๋ฆฌ); `MAM_JOB_TIMEOUT_S=08` ์ด bash ์—์„œ 8์ง„์ˆ˜๋กœ ์ฝํž˜; `MAM_WAIT_GRACE_S` ๋ฏธ๋ฌธ์„œยท๋Ÿฌ๋„ˆ์˜ ์ž˜๋ชป๋œ `MAM_JOB_TIMEOUT_S` ๋ฌด์–ธ; SKILL.md ์˜ โ‰ค60 s ๋ฌธ๊ตฌ๋ฅผ herdr wait ๋กœ ํ•œ์ •; ๋ถ€๋ชจ ์†Œ๋ฉธ ํ…Œ์ŠคํŠธ์˜ alive ๋Œ€์—ญ์„ wait ์•ˆ์—์„œ ๋ฐ”๊พธ๊ธฐ. ### ๐Ÿš€ `v4.7.4` โ€” Issue #34 ๋ฃจํ”„๊ฐ€ creator ์˜ ํ•˜์œ„ ์œ„์ž„ ์žก๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ๋ฆฌ๋ทฐ์–ด์—๊ฒŒ ํ•˜์œ„ ์žก ์ƒํƒœ๋ฅผ ๊ทผ๊ฑฐ๋กœ ์ œ๊ณต (2026-09-25, PR #38) > **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: creator ๊ฐ€ ์ž‘์—… ์ผ๋ถ€๋ฅผ delegate-job ์œผ๋กœ ๋‹ค๋ฅธ ์—์ด์ „ํŠธ์—๊ฒŒ ๋งก๊ธฐ๋ฉด, ๊ทธ ํ•˜์œ„ ์žก์ด ์•„์ง ๋Œ๊ณ  ์žˆ์–ด๋„ ๋ฃจํ”„๊ฐ€ ๋ฆฌ๋ทฐ๋กœ ๋„˜์–ด๊ฐ€ **๋ฏธ์™„์„ฑ ์‚ฐ์ถœ๋ฌผ์ด PASS** ๋˜๋˜ ๋ฌธ์ œ(Issue #34)๋ฅผ ๊ณ ์ณค๋‹ค. ๋ฃจํ”„๋Š” ์ž์‹ ์ด ๋งŒ๋“  ์žก๋งŒ ๊ฐ์‹œํ–ˆ๊ณ  ์žก ๋ ˆ์ฝ”๋“œ์—๋Š” ๋ถ€๋ชจยท์ž์‹ ๊ด€๊ณ„๊ฐ€ ์—†์—ˆ๋‹ค. ํ•„๋“œ ํ•˜๋‚˜(`parent_job`)์™€ ์˜ต์…˜ ๋‘˜(`--parent-job`, `--wait-open-jobs`)์ด ๋Š˜์ง€๋งŒ ์ž˜๋ชป๋œ ํŒ์ • ๊ฒฝ๋กœ๋ฅผ ๊ณ ์น˜๋Š” ์ˆ˜์ •์ด๋ผ PATCH ๋กœ ์˜ฌ๋ฆฐ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **์žก์ด ๋ถ€๋ชจ๋ฅผ ๊ธฐ๋ก (`lib_py/delegation/registry.py`, delegate-job ๋ž˜ํผ)**: `submit --parent-job ` ๊ฐ€ ์ด ์žก์ด ์–ด๋А ์žก์—์„œ ์œ„์ž„๋๋Š”์ง€ ๋‚จ๊ธด๋‹ค. ๋ถ€๋ชจ๊ฐ€ ๋“ฑ๋ก๋ถ€์— ์—†์œผ๋ฉด ๋“ฑ๋ก์„ ๊ฑฐ๋ถ€ํ•œ๋‹ค(์˜คํƒ€๊ฐ€ ์กฐ์šฉํžˆ ๋ฃจํŠธ ์žก์ด ๋˜์ง€ ์•Š๋„๋ก). ๊ธฐ๋ณธ๊ฐ’์€ ์›๊ฒฉ ๋Ÿฌ๋„ˆ๊ฐ€ ์ฃผ๋Š” `MAM_JOB_ID` ์ด๋ฉฐ ์ด ๋“ฑ๋ก๋ถ€์— ์—†์œผ๋ฉด ๊ฒฝ๊ณ ๋งŒ ํ•˜๊ณ  ๋ถ€๋ชจ ์—†์ด ๋“ฑ๋กํ•œ๋‹ค. `registry.py list --parent `, `children_of`/`descendants_of`(๋„ˆ๋น„ ์šฐ์„ , ํ•œ ๋ฒˆ ๋ฐฉ๋ฌธ, ์ˆœํ™˜ ์•ˆ์ „, `depth`). 2. **์—์ด์ „ํŠธ์—๊ฒŒ ๋ถ€๋ชจ ID ์ „๋‹ฌ**: ๋กœ์ปฌ ์—์ด์ „ํŠธ๋Š” pane ์— ์˜ค๋ž˜ ๋–  ์žˆ์–ด ์žก๋งˆ๋‹ค ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋„ฃ์„ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ์žก๋งˆ๋‹ค ์ƒˆ๋กœ ์ฃผ์ž…๋˜๋Š” ์•ˆ๋‚ด๋ฌธ๊ณผ `brief.md` ์— "ํ•˜์œ„ ์œ„์ž„ ์‹œ `--parent-job <์ด ์žก ID>` ๋ฅผ ๋ถ™์—ฌ๋ผ" ๋ฅผ ์ ๋Š”๋‹ค(`create_session.sh --submit-job` ๋„ ๊ฐ™์Œ). ์‹ค์ œ creator ์—๊ฒŒ ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ์ง€ ์•Š๊ณ  ์œ„์ž„์„ ์‹œ์ผฐ์„ ๋•Œ ์Šค์Šค๋กœ ๋ถ™์˜€๋‹ค. 3. **๋ฃจํ”„๊ฐ€ ์ž์† ์žก๊นŒ์ง€ ๋Œ€๊ธฐ (`lib_py/loop/runner.py: wait_for_job_tree / wait_for_descendants`)**: creator ์žก(๊ตฌํ˜„ยท์ˆ˜์ •ยท์ˆœ์‘)์ด ๋๋‚œ ๋’ค ์ž์† ์žก์ด ๋ชจ๋‘ ์ข…๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ ๋‹ค์Œ ๋ฆฌ๋ทฐ๋กœ ๋„˜์–ด๊ฐ„๋‹ค. ์ž์†์€ ํด๋ง๋งˆ๋‹ค ๋‹ค์‹œ ์ฝ์–ด ์†์ž ์žก๋„ ์žก๋Š”๋‹ค. ์ƒํ•œ์€ ๋‘˜: `MAM_CHILD_WAIT_S`(creator ์žก ์ข…๊ฒฐ๋ถ€ํ„ฐ, ๊ธฐ๋ณธ 3900 ์ดˆ; ๊ฐ ํ•˜์œ„ ์žก์€ #33 ์˜ 3600 ์ดˆ ์˜ˆ์‚ฐ์œผ๋กœ ๋‹ซํžŒ๋‹ค)์™€ `MAM_JOB_TREE_MAX`(๊ธฐ๋ณธ 50, ๋„˜์œผ๋ฉด ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์Œ). `--wait-open-jobs` ๋Š” ๋ถ€๋ชจ ์—†์ด ๋“ฑ๋ก๋œ ์—ด๋ฆฐ ์žก๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค(๋ฌด๊ด€ํ•œ ์žก์ด ๋ฃจํ”„๋ฅผ ๋ถ™์žก์„ ์ˆ˜ ์žˆ์–ด ๊ธฐ๋ณธ ๊บผ์ง). 4. **๋ฆฌ๋ทฐ์–ด๊ฐ€ ํ•˜์œ„ ์žก ์ƒํƒœ๋ฅผ ๋ด„ (`_subjobs_block`)**: ๋ฆฌ๋ทฐ ํ”„๋กฌํ”„ํŠธ ์•ž์— ํ•˜์œ„ ์žก ํ‘œ(์„ธ์…˜ยท์—ญํ• ยท์ƒํƒœยท์ข…๋ฃŒ ์‚ฌ์œ ยท๊นŠ์ด)๋ฅผ ๋„ฃ๊ณ , ์—ด๋ฆฐ(OPEN)ยท๋ฏธํ™•์ธ ์ข…๊ฒฐ(CLOSED-UNCONFIRMED: `deadline_timeout`/`agent_blocked` ๋กœ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๊ฐ€ ๋‹ซ์€ ์žก, ์—์ด์ „ํŠธ๋Š” ๊ณ„์† ์ผํ•˜๊ณ  ์žˆ์„ ์ˆ˜ ์žˆ์Œ)ยท์‹คํŒจ ํ•˜์œ„ ์žก์ด ์žˆ์œผ๋ฉด NOT PASS ๋ฅผ ๋‚ด๋„๋ก ๊ทœ์น™์„ ์ค€๋‹ค. `session_busy`/`target_not_found`/๋กค๋ฐฑ์€ NOT-DISPATCHED. ์ˆ˜์ • ๋ผ์šด๋“œ์—์„œ๋„ ์•ž์„  creator ์žก์˜ ํ•˜์œ„ ์žก์ด ํ‘œ์— ๋‚จ๋Š”๋‹ค. ๋ถ€๋ชจ ๋ฏธํ‘œ๊ธฐ ์—ด๋ฆฐ ์žก์€ `[๊ท€์†๋˜์ง€ ์•Š์€ ์—ด๋ฆฐ ์žก]` ์œผ๋กœ ๋”ฐ๋กœ ๋ณด์—ฌ์ค€๋‹ค. ํ•˜์œ„ ์œ„์ž„์ด ์ „ํ˜€ ์—†์œผ๋ฉด ํ”„๋กฌํ”„ํŠธ๋Š” ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ์ด์ „๊ณผ ๊ฐ™๋‹ค. 5. **ํ•˜์œ„ ์žก์ด ์žˆ์„ ๋•Œ ๋ฆฌ๋ทฐ ์ค‘ ์†Œ์Šค ๋ณ€๊ฒฝ์€ ์Šน์ธ ๋ฌดํšจ (`_fingerprint`)**: ํ•˜์œ„ ์žก์ด ๋ฆฌ๋ทฐ ๋„์ค‘ ํŒŒ์ผ์„ ๊ณ ์น  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, `--worktree`/`--base` ์—†๋Š” ๋ฃจํ”„์—๋„ identity ๋ชจ๋“œ์˜ B7/B10 ์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ€๋“œ๋ฅผ ๋‘”๋‹ค. ์ œ์–ด ํ‰๋ฉด(`.mam`, jobs, ๊ฐ์‚ฌ ๋กœ๊ทธ) ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ์ง€๋ฌธ์—์„œ ์ œ์™ธํ•˜๊ณ , **ํ•˜์œ„ ์žก์ด ์žˆ๋Š” ์‹คํ–‰์—์„œ๋งŒ** ์ผ ๋‹ค(ํ•˜์œ„ ์žก ์—†๋Š” ๊ธฐ์กด ๋ฃจํ”„์˜ ๋™์ž‘ ๋ถˆ๋ณ€; ์ผœ ๋‘๋ฉด ์ƒŒ๋“œ๋ฐ•์Šค e2e ๊ฐ€ ๋งค ๋ผ์šด๋“œ ๋ฌดํšจํ™”๋๋‹ค). 6. **๊ฐ™์€ ์„œ๋ฒ„์˜ ๋‹ค๋ฅธ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์—์ด์ „ํŠธ์—๊ฒŒ ์œ„์ž„ (`lib.sh: _herdr_scope_is_pane_inherited`)**: herdr pane ์ด ๋‚ด๋ณด๋‚ด๋Š” `HERDR_WORKSPACE_ID` ๊ฐ€ ์ด๋ฆ„ ์กฐํšŒ(`agent`/`has-session`/`kill-session`)์˜ ์Šค์ฝ”ํ”„๋กœ ์“ฐ์—ฌ ๋‹ค๋ฅธ ์›Œํฌ์ŠคํŽ˜์ด์Šค์˜ ์—์ด์ „ํŠธ๊ฐ€ ๋ณด์ด์ง€ ์•Š์•˜๋‹ค(์›ŒํฌํŠธ๋ฆฌ creator ์˜ ํ•˜์œ„ ์œ„์ž„์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฑธ๋ฆผ). pane ์•ˆ์˜ ํ˜ธ์ถœ์ž๊ฐ€ ์ด๋ฆ„์œผ๋กœ ๋Œ€์ƒ์„ ์ฐพ์„ ๋•Œ ๊ทธ ID ๊ฐ€ `HERDR_PANE_ID` ์˜ ์›Œํฌ์ŠคํŽ˜์ด์Šค์™€ ๊ฐ™์œผ๋ฉด ๋—€๋‹ค(์‚ด์•„ ์žˆ๋Š” ์—์ด์ „ํŠธ ์ด๋ฆ„์€ ์„œ๋ฒ„ ๋‹จ์œ„๋กœ ์œ ์ผ). pane ๋ฐ–์˜ ๋ช…์‹œ ์Šค์ฝ”ํ”„ยทpane ๊ณผ ์–ด๊ธ‹๋‚˜๋Š” ์Šค์ฝ”ํ”„ยทpane/์„ธ์…˜ ์ƒ์„ฑ ํ˜ธ์ถœ์€ ๊ทธ๋Œ€๋กœ. ๋Œ€์ƒ์ด ๋ณด์ด์ง€ ์•Š์•„ ์ทจ์†Œ๋œ ์žก์€ `session_busy` ๊ฐ€ ์•„๋‹ˆ๋ผ `target_not_found` ๋กœ ๊ธฐ๋ก๋œ๋‹ค. 7. **๋ฌธ์„œ**: delegate-job/loop SKILL.md, MULTI_AGENT_RULES(ko). #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ** (`tests/test_issue34_job_tree.py`, 598์ค„): ์‹ค์ œ ๋ž˜ํผ๋กœ ํ•˜์œ„ ์žก์„ ๋“ฑ๋กํ•˜๊ณ  ์‹ค์ œ ๋“ฑ๋ก๋ถ€๋ฅผ ์ฝ๋Š”๋‹ค โ€” ๋ถ€๋ชจ ๊ฒ€์ฆยท`descendants_of`ยท์ž์† ๋Œ€๊ธฐยท์ƒํ•œยทํ•˜์œ„ ์žก ํ‘œ์™€ ์ƒํƒœ ๋ถ„๋ฅ˜ยทstale ๊ฐ€๋“œยท๊ฐ™์€ ์„œ๋ฒ„ ๋‹ค๋ฅธ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์กฐํšŒ. ์ฒซ ๋ฃจํ”„ ์‹œ์ ์˜ 14๊ฑด์€ ์ˆ˜์ • ์ „ ์ฝ”๋“œ(7a4a0c2)์—์„œ ๋ชจ๋‘ ์‹คํŒจ. - **๋ฆฌ๋ทฐ**: ์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค 5์ธ ์ „์ฒด ๋ฃจํ”„ 2ํšŒ์ฐจ ์ „์› PASS + ๋ธํƒ€ ์ „์› PASS(codex 1์ฐจ: ์ˆ˜์ • ๋ผ์šด๋“œ์—์„œ ์•ž์„  creator ์žก์˜ ํ•˜์œ„ ์žก์ด ํ‘œ์—์„œ ๋น ์ง โ†’ 3fdbb62; ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ: stale ๊ฐ€๋“œ๋ฅผ ์˜ต์…˜ ์—†๋Š” ๋ฃจํ”„์— ๋ฌด์กฐ๊ฑด ์ผœ์ž `test_tier4_e2e.py` ์‹œ๋‚˜๋ฆฌ์˜ค 5 ํšŒ๊ท€ โ†’ 1d4f338; ์‹ค์ œ ์‹œํ—˜: ๊ฐ™์€ ์„œ๋ฒ„ ๋‹ค๋ฅธ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์Šค์ฝ”ํ”„ โ†’ ed293dc). ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ PR head ๋ฅผ detached ์›ŒํฌํŠธ๋ฆฌ์— ๋ฐ›์•„ ์žฌ๊ฒ€ํ† : 1ํšŒ์ฐจ @ed293dc claude PASS(๋น„์ฐจ๋‹จ K1โ€“K4)ยทagy PASS, grokยทcodex NOT PASS โ€” ๋ž˜ํผ๊ฐ€ ํˆฌ์ž… ์ „์— `cancelled --detail target_not_found|session_busy` ๋กœ ๋‹ซ์€ ํ•˜์œ„ ์žก์˜ ์‚ฌ์œ ๋ฅผ `get_feedback` ์ด ์ฝ์ง€ ์•Š์•„ ํ‘œ์— ์‚ฌ์œ  ์—†๋Š” `cancelled` ๋กœ๋งŒ ๋‚˜์˜ด(grok N1ยทcodex R1), ์›๊ฒฉ ๋Ÿฌ๋„ˆ์˜ ๋งˆ๊ฐ ๋ฌธ๊ตฌ `deadline_timeout (remote runner)` ๊ฐ€ ์™„์ „ ์ผ์น˜ ๋น„๊ต์— ๊ฑธ๋ ค ์ผ๋ฐ˜ ์‹คํŒจ๋กœ ์ง‘๊ณ„๋จ(codex R2), ๊ท€์†๋˜์ง€ ์•Š์€ ์—ด๋ฆฐ ์žก์— ํŒ์ • ๊ทœ์น™ ๋ฌธ์žฅ์ด ์—†์Œ(claude K1) โ†’ 8a9101eยทe80186f ๋กœ ์ •์ •(`get_feedback(events=โ€ฆ)`, ์‚ฌ์œ ์˜ ์ฒซ ๋‹จ์–ด๋กœ ์ข…๋ฅ˜ ํŒ์ •, `STRAY_RULE`; ํšŒ๊ท€ T13ยทT14ยทT6 ์€ ์ •์ •์„ ๋˜๋Œ๋ฆฌ๋ฉด ์‹คํŒจ). ๋ธํƒ€ ๋ฆฌ๋ทฐ @e80186f claudeยทagyยทgrokยทcodex **4์ธ ์ „์› PASS**(identity 4๊ฑด ๋ชจ๋‘ base ed293dc โ†’ head e80186f, evidence review-tree). codex ๋Š” 1ํšŒ์ฐจ์—์„œ ์ž์† 502๊ฐœ ๋“ฑ๋ก๋ถ€์˜ `descendants_of` ์กฐํšŒ๊ฐ€ ์•ฝ 0.01 ์ดˆ์ž„์„ ์‹ค์ธก. ๋ฆฌํฌํŠธ์™€ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ์ž์ฒด venv ๊ฐ€ ์žˆ๋Š” ์›ŒํฌํŠธ๋ฆฌ @ed293dc)**: @ed293dc 1156 ํ†ต๊ณผ / 3 skip / ์‹คํŒจ 2(๊ณต๊ฐœ ๋ธŒ๋กœ์ปค `no CONNACK` ์žฌ์‹œ๋„๋กœ 11 ์ดˆ ํƒ€์ด๋ฐ ํ•œ๊ณ„๋ฅผ ๋„˜๊ธด flake 2๊ฑด, ๋‹จ๋… ์žฌ์‹คํ–‰ ํ†ต๊ณผ; 1์‹œ๊ฐ„ 1๋ถ„). @e80186f ๊ด€๋ จ ์Šค์œ„ํŠธ(job-treeยทdelegation ๊ณ„์•ฝยทtransportยทloop CLIยทe2eยทlivenessยทversion) 125 + 54 ํ†ต๊ณผ. - **๋น„์ฐจ๋‹จ ํ›„์†**: `MAM_JOB_TREE_MAX` ์ดˆ๊ณผ ์‹œ ๋Œ€๊ธฐ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋ฆฌ๋ทฐ๋กœ ๊ฐ€๋ฉฐ ํ‘œ์— 50ํ–‰ ๋„˜๊ฒŒ ๋ถ™์„ ์ˆ˜ ์žˆ์Œ(K2); `target_not_found` ๋„ ๋ž˜ํผ rc ๊ฐ€ `session_busy` ์™€ ๊ฐ™์€ 5 ๋ผ ๋ฃจํ”„์˜ busy 1ํšŒ ์žฌ์‹œ๋„๊ฐ€ ์—†๋Š” ๋Œ€์ƒ์—๋„ ๋Œ์•„๊ฐ(K3, rc ๋ถ„๋ฆฌ ๊ถŒ๊ณ ); `descendants_of`/๊ท€์†๋˜์ง€ ์•Š์€ ์žก ์กฐํšŒ๊ฐ€ ํด๋ง(3 ์ดˆ)๋งˆ๋‹ค ๋“ฑ๋ก๋ถ€ ์ „์ฒด๋ฅผ ์ฝ์Œ(K4, ๋“ฑ๋ก๋ถ€ ์ •๋ฆฌ ์ •์ฑ… ์ „๊นŒ์ง€ ์ •๋ณด); ๊ท€์†๋˜์ง€ ์•Š์€ ์—ด๋ฆฐ ์žก ๋ชฉ๋ก์€ ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ํœด๋ฆฌ์Šคํ‹ฑ์ด๋ผ ๋‹ค๋ฅธ ๋ฃจํ”„์˜ ์žก์ด ์„ž์ผ ์ˆ˜ ์žˆ์Œ(codex). - **๋ฒ”์œ„ ๋ฐ–**: ๋ฉˆ์ถ˜ ์—์ด์ „ํŠธ ๊ฐ์ง€(#30)์™€ ์žก ์ œํ•œ ์‹œ๊ฐ„ ์กฐ์ ˆ(#33). ํ•˜์œ„ ์žก์ด ๊ถŒํ•œ ํ™•์ธ ์ฐฝ์— ๋ฉˆ์ถ”๋ฉด ๋ฃจํ”„๋Š” ๊ทธ ์žก์ด #33 ์˜ ์˜ˆ์‚ฐ์œผ๋กœ ๋‹ซํž ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค. ### ๐Ÿš€ `v4.7.3` โ€” Issue #36 ์œ„์ž„ ํ”„๋กฌํ”„ํŠธ๋ฅผ argv ๊ฐ€ ์•„๋‹Œ ํŒŒ์ผ๋กœ ์ „๋‹ฌ, ์œ„์ž„ ์‹คํŒจ๋Š” ์ข…๋ฃŒ ์ฝ”๋“œ 1 (2026-09-24, PR #37) > **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: ๋ฆฌ๋ทฐ ๋ฃจํ”„๊ฐ€ diff ๋ฅผ ๋ฆฌ๋ทฐ ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ์–ด bash ์— **๋‹จ์ผ argv ์›์†Œ**๋กœ ๋„˜๊ธฐ๋˜ ํƒ“์—, ํ”„๋กฌํ”„ํŠธ๊ฐ€ Linux ์˜ ๋‹จ์ผ ์ธ์ž ์ƒํ•œ(`MAX_ARG_STRLEN` 131,072 ๋ฐ”์ดํŠธ)์„ ๋„˜์œผ๋ฉด `OSError: [Errno 7]` ๋กœ ์ฃฝ๊ณ  **์ข…๋ฃŒ ์ฝ”๋“œ 0** ์œผ๋กœ ๋๋‚˜๋˜ ๋ฌธ์ œ(Issue #36)๋ฅผ ๊ณ ์ณค๋‹ค. ์ˆ˜์ง‘๊ธฐ ์ƒํ•œ `MAM_DIFF_MAX_BYTES`(๊ธฐ๋ณธ 200000)๊ฐ€ ์ปค๋„ ์ƒํ•œ๋ณด๋‹ค ๋†’์•„ ๊ทธ ์‚ฌ์ด ํฌ๊ธฐ๋Š” ๊ฑธ๋Ÿฌ์ง€์ง€ ์•Š์•˜๊ณ , ์ปค๋ฐ‹ 10์—ฌ ๊ฐœ ๊ทœ๋ชจ์˜ ๋ธŒ๋žœ์น˜(PR #35 ๊ฐ€ ์‹ค์ œ๋กœ 133 KB)๋Š” ๋ฃจํ”„๋กœ ๋ฆฌ๋ทฐํ•  ์ˆ˜ ์—†์—ˆ๋‹ค. ์˜ต์…˜ ํ•˜๋‚˜(`--prompt-file`)๊ฐ€ ๋Š˜์ง€๋งŒ ์ž˜๋ชป๋œ ์ „๋‹ฌ ๊ฒฝ๋กœ๋ฅผ ๊ณ ์น˜๋Š” ์ˆ˜์ •์ด๋ผ PATCH ๋กœ ์˜ฌ๋ฆฐ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **ํ”„๋กฌํ”„ํŠธ๋Š” ํŒŒ์ผ๋กœ ์ „๋‹ฌ (`lib_py/loop/runner.py`, delegate-job ๋ž˜ํผ, `lib_py/delegation/registry.py`ยท`cli.py`, `lib.sh`, `run_loop.sh`)**: Python ๋Ÿฌ๋„ˆ๋Š” ๋ชจ๋“  ์œ„์ž„ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ œ์–ด ๋ฃจํŠธ `.mam/` ์•„๋ž˜ ์ž„์‹œ ํŒŒ์ผ์— ์“ฐ๊ณ  ๋ž˜ํผ์— `--prompt-file` ๋กœ ๋„˜๊ธด ๋’ค ๋ž˜ํผ๊ฐ€ ๋๋‚˜๋ฉด ์ง€์šด๋‹ค. ๋ž˜ํผ์™€ ๋“ฑ๋ก๋ถ€๋Š” `--prompt`/`--prompt-file` ์„ ์ƒํ˜ธ ๋ฐฐํƒ€๋กœ ๋ฐ›๊ณ , ๋ž˜ํผ๋Š” ๋ฐ›์€ ๊ฒฝ๋กœ๋ฅผ ๋“ฑ๋ก๋ถ€์— ๊ทธ๋Œ€๋กœ ๋„˜๊ธฐ๋ฉฐ loop ํ˜• ๋ฐ˜๋ณต์˜ `update` ๋„ `prompt.md` ํŒŒ์ผ๋กœ ๋„˜๊ธด๋‹ค. `lib.sh` ์˜ ์ œ์ถœ ๋„์šฐ๋ฏธ(`delegate_submit_job` ๊ณ„์—ด)์™€ `cli.py` ์‚ฌ์Šฌ, ์˜› ์…ธ ๋Ÿฌ๋„ˆ(`MAM_LOOP_PY=0`)์˜ ์œ„์ž„ 11๊ณณ์ด ๊ฑฐ์น˜๋Š” `delegate_job_safe` ๋„ ํŒŒ์ผ๋กœ ๋„˜๊ธด๋‹ค(์“ฐ๊ธฐ ์‹คํŒจ ์‹œ ๋ถ€๋ถ„ ํŒŒ์ผ์„ ์ง€์šฐ๊ณ  stderr ๋กœ ์ง„๋‹จ). `create_session.sh` ๋Š” ์ฃผ์ž… ์•ˆ๋‚ด๋ฌธ์— ๊ณผ์ œ ๋ณธ๋ฌธ ๋Œ€์‹  ์žก ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ `brief.md` ์ ˆ๋Œ€๊ฒฝ๋กœ๋งŒ ๋„ฃ๋Š”๋‹ค(herdr ์ฃผ์ž…๋ฌธ๋„ argv ํ•˜๋‚˜๋‹ค). ๊ธฐ์กด `--prompt` ๋Š” ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค. 2. **์œ„์ž„ ์‹คํŒจ๋Š” ์„ฑ๊ณต์ฒ˜๋Ÿผ ๋๋‚˜์ง€ ์•Š์Œ (`runner.py`)**: ๋ž˜ํผ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์œผ๋ฉด(OSError) rc 126 ์˜ ์‹คํŒจํ•œ ์ œ์ถœ๋กœ ๋‹ค๋ฃจ๊ณ  ์ฒซ ์‹œ๋„ยท์žฌ์‹œ๋„ ๋ชจ๋‘ ์›์ธ์„ ๋‚จ๊ธฐ๋ฉฐ ๋ฃจํ”„๋Š” rc 1 ๋กœ ๋๋‚œ๋‹ค. ๋ฐ˜๋ก  ์ดํ›„์˜ ์žฌ์‹ฌยท์ค‘์žฌ ์žก์ด ๋“ฑ๋ก๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์™€ ์ˆœ์‘ ์žก์ด ๋“ฑ๋ก๋˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋๋‚˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๋„ ํŒ์ •์ด ์•„๋‹ˆ๋ผ ์‹คํŒจ(rc 1)๋‹ค. 3. **์ˆ˜์ง‘๊ธฐ ์ƒํ•œ์˜ ์˜๋ฏธ (`diff_collect.sh`)**: `MAM_DIFF_MAX_BYTES`ยท`MAM_DIFF_MAX_LINES` ๋Š” ๋ฆฌ๋ทฐ์–ด์—๊ฒŒ ๋ณด์—ฌ์ค„ ์ฆ๊ฑฐ์˜ ํฌ๊ธฐ๋งŒ ์ œํ•œํ•œ๋‹ค. ์ „๋‹ฌ ์ž์ฒด์—๋Š” ํฌ๊ธฐ ํ•œ๊ณ„๊ฐ€ ์—†๊ณ , ์ž˜๋ฆฐ diff ๋ฅผ ์ฆ๊ฑฐ๋กœ ์“ธ ๋•Œ์˜ ๊ธฐ์กด ๊ทœ์น™(diff-only ์›๊ฒฉ ๋ฆฌ๋ทฐ์–ด ๊ฑฐ๋ถ€)์€ ๊ทธ๋Œ€๋กœ๋‹ค. 4. **๋ฌธ์„œ**: delegate-job SKILL.md(`--prompt-file`), loop SKILL.md. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ 22๊ฐœ** (`tests/test_issue36_prompt_transport.py`): 131,072 ๋ฐ”์ดํŠธ๋ฅผ ๋„˜๋Š” ์‹ค์ œ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์‹ค์ œ subprocess/exec ๊ฒฝ๋กœ(๋Ÿฌ๋„ˆ โ†’ ๋ž˜ํผ โ†’ ๋“ฑ๋ก๋ถ€, `lib.sh` ๋„์šฐ๋ฏธ, ์˜› ์…ธ ๋Ÿฌ๋„ˆ, `create_session`)๋ฅผ ์ง€๋‚˜๋Š”์ง€, ์ž„์‹œ ํŒŒ์ผ์˜ ์ƒ์„ฑยท์ •๋ฆฌยท์“ฐ๊ธฐ ์‹คํŒจ ์ฒ˜๋ฆฌ, ๋ž˜ํผ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์„ ๋•Œ rc 1 ๊ณผ ํŠธ๋ ˆ์ด์Šค๋ฐฑ ๋ถ€์žฌ. ์ˆ˜์ • ์ „ ์ฝ”๋“œ์—์„œ๋Š” 13๊ฑด ์ค‘ 11๊ฑด์ด ์‹คํŒจ. - **๋ฆฌ๋ทฐ**: ์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค 5์ธ 3๋ผ์šด๋“œ ์ „์› PASS(codex 1์ฐจ: ๋ฐ˜๋ก  ๊ฒฝ๋กœ์—์„œ ์žก ๋“ฑ๋ก ์‹คํŒจ ์‹œ ๋ฃจํ”„ ๊ณ„์† โ†’ 14d11bf; ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ: ์˜› ์…ธ ๋Ÿฌ๋„ˆยท`lib.sh` ๋„์šฐ๋ฏธ์˜ argv ์ž”์กด โ†’ f26952c; codex ๋ธํƒ€: strace ๋กœ `create_session` ์˜ E2BIG ์™€ ์…ธ ๋Ÿฌ๋„ˆ์˜ ๋ถ€๋ถ„ ํŒŒ์ผ ์ž”์กด ์žฌํ˜„ โ†’ 59be1c8). ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ PR head ๋ฅผ detached ์›ŒํฌํŠธ๋ฆฌ์— ๋ฐ›์•„ ์žฌ๊ฒ€ํ† : 1ํšŒ์ฐจ @59be1c8 claude PASS(๋น„์ฐจ๋‹จ H1โ€“H5), agyยทgrokยทcodex NOT PASS โ€” ์ƒˆ ํ…Œ์ŠคํŠธ S9 ๊ฐ€ stderr ์˜ ๋ฆฌํ„ฐ๋Ÿด `File too large` ๋ฅผ ์š”๊ตฌํ•ด macOS bash 3.2 ์—์„œ ์‹คํŒจ(agyยทgrokยทcodex R2), Python ๋Ÿฌ๋„ˆ์˜ ํ”„๋กฌํ”„ํŠธ ํŒŒ์ผ **์“ฐ๊ธฐ** ์‹คํŒจ๊ฐ€ ์ •๋ฆฌยท์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฒ”์œ„ ๋ฐ–์ด๋ผ ๋ถ€๋ถ„ ํŒŒ์ผ์ด ๋‚จ๊ณ  ์˜ˆ์™ธ๊ฐ€ ๋ฃจํ”„๊นŒ์ง€ ์˜ฌ๋ผ์˜ด(claude H1ยทcodex R1), ๋ž˜ํผ๊ฐ€ ๋นˆ `--prompt-file` ์„ ๋ฐ›์•„๋“ค์ž„(claude H3) โ†’ 708262a ๋กœ ์ •์ •(์“ฐ๊ธฐ ์‹คํŒจ๋Š” exec ์‹คํŒจ์™€ ๊ฐ™์€ rc 126 ์‹คํŒจ ์ œ์ถœ, libc ๋ฌธ๊ตฌ๋Š” Linux ์—์„œ๋งŒ ๋‹จ์–ธ, ๋นˆ ํŒŒ์ผ ๊ฑฐ๋ถ€; ํšŒ๊ท€ S4cยทS1e ๋Š” ์ •์ •์„ ๋˜๋Œ๋ฆฌ๋ฉด ์‹คํŒจ). ๋ธํƒ€ ๋ฆฌ๋ทฐ @708262a claudeยทagyยทgrokยทcodex **4์ธ ์ „์› PASS**(identity 4๊ฑด ๋ชจ๋‘ base 59be1c8 โ†’ head 708262a, evidence review-tree). codex ๋Š” 1ํšŒ์ฐจ์—์„œ ์ด ํ˜ธ์ŠคํŠธ์˜ `SC_ARG_MAX` ๋ฅผ ๋„˜๋Š” 1,114,112 ๋ฐ”์ดํŠธ ํ”„๋กฌํ”„ํŠธ๊ฐ€ argv ๋กœ๋Š” `OSError(errno=7)` ์ด๊ณ  ํŒŒ์ผ๋กœ๋Š” ๊ณต๋ฐฑ ๊ฒฝ๋กœยท๋‹ค๋ฅธ `--workdir` ์กฐํ•ฉ์—์„œ๋„ ์›๋ฌธ ๊ทธ๋Œ€๋กœ ๋“ฑ๋ก๋จ์„ ์‹ค์ธก. ๋ฆฌํฌํŠธ์™€ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ์ž์ฒด venv ๊ฐ€ ์žˆ๋Š” ์›ŒํฌํŠธ๋ฆฌ @59be1c8)**: @59be1c8 1139 ํ†ต๊ณผ / 2 skip / ์‹คํŒจ 1(S9, ์œ„ R2; 54๋ถ„ 7์ดˆ) โ†’ @708262a ๊ด€๋ จ ์Šค์œ„ํŠธ(transportยทdelegation ๊ณ„์•ฝยทloop CLIยทdelivery ackยทversion) 125 ํ†ต๊ณผ / 1 skip(Linux ์ „์šฉ S1b). - **๋น„์ฐจ๋‹จ ํ›„์†**: `MAM_DIFF_MAX_*` ์˜ "์ฆ๊ฑฐ ์ƒํ•œ, ์ „๋‹ฌ ๋ฌด์ œํ•œ" ์˜๋ฏธ๊ฐ€ loop SKILL.md ์— ์—†์Œ(H2); ๋ž˜ํผ์˜ `$( **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: `create_session.sh` ๋กœ ์ƒˆ ์„ธ์…˜์„ ๋งŒ๋“œ๋Š” ์ฒซ 1๋ถ„ ์•ˆ์˜ ๊ฒฐํ•จ ๋‘˜์„ ๊ณ ์ณค๋‹ค. ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ pane ์ด ๋ฌผ๋ ค์ฃผ๋Š” herdr ํ™˜๊ฒฝ(`HERDR_WORKSPACE_ID`ยท`HERDR_PANE_ID`ยท`HERDR_TAB_ID`)์ด ๊ฒฉ๋ฆฌ ์„œ๋ฒ„ ์กฐํšŒ๋ฅผ ์˜ค์—ผ์‹œ์ผœ ์˜จ๋ณด๋”ฉ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š๊ณ , ์ „๋‹ฌ ํŒ์ •์ด ํ™”๋ฉด ๊ธฐ์ค€์ด๋ผ ์‚ด์•„ ์žˆ๋Š” ์—์ด์ „ํŠธ๋ฅผ ์‹คํŒจ๋กœ ํŒ์ •ํ•œ ๋’ค ๋กค๋ฐฑ๊นŒ์ง€ ์‹คํŒจํ•˜๋˜ ๋ฌธ์ œ(#23)์™€, ์‹œ์ž‘ ์‹ ๋ขฐ(trust) ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ claude ์—๊ฒŒ๋งŒ ์ฒ˜๋ฆฌํ•ด agyยทgrokยทcodex ๊ฐ€ ์ค€๋น„ ๋Œ€๊ธฐ ์ดˆ๊ณผ๋กœ ์˜จ๋ณด๋”ฉ์„ ๊ฑด๋„ˆ๋›ฐ๋˜ ๋ฌธ์ œ(#24)๋‹ค. ์˜ต์…˜ ํ•˜๋‚˜(`--no-auto-trust`)์™€ ์ข…๋ฃŒ ์ฝ”๋“œ ๋‘˜(42ยท43)์ด ๋Š˜์ง€๋งŒ ์ž˜๋ชป๋œ ๊ธฐ๋ณธ ๋™์ž‘์„ ๊ณ ์น˜๋Š” ์ˆ˜์ •์ด๋ผ PATCH ๋กœ ์˜ฌ๋ฆฐ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **์‹œ์ž‘ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—์ด์ „ํŠธ ๋ฌด๊ด€ (#24, `lib_py/agents/base.py: startup_dialogs`, ์–ด๋Œ‘ํ„ฐ 4์ข…, `lib.sh: handle_startup_dialogs`)**: ๋ชจ๋‹ฌ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜ยท์ˆ˜๋ฝ ํ‚คยท์ข…๋ฅ˜(`trust`/`notice`)๋ฅผ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์ œ๊ณตํ•˜๊ณ (`MAM_STARTUP_DIALOGS`), TUI ์ค€๋น„ ๋Œ€๊ธฐ ์ค‘ ๊ฐ์ง€๋˜๋ฉด ์ˆ˜๋ฝํ•œ๋‹ค. agy(Enter)ยทgrok(`y`)ยทcodex(`1. Yes, continue` Enter)์˜ ์‹ ๋ขฐ ์ฐฝ๊ณผ codex 0.155.1 ์—…๋ฐ์ดํŠธ ์•ˆ๋‚ด(Skip)๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์ˆ˜๋ฝ ํŒ์ •์€ ๋‹ค์ด์–ผ๋กœ๊ทธ ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์ค€๋น„ ํ† ํฐ์œผ๋กœ ํ•œ๋‹ค(`--source visible` ์ด ์ˆ˜๋ฝ๋œ ์ฐฝ์„ ํ•œ๋™์•ˆ ๊ณ„์† ๋ณด์—ฌ์ค€๋‹ค). `--no-auto-trust` / `MAM_AUTO_TRUST=0` ์€ ์‹ ๋ขฐ ์ฐฝ๋งŒ ์‚ฌ๋žŒ์—๊ฒŒ ๋‚จ๊ธฐ๊ณ  ์•ˆ๋‚ด๋Š” ๊ณ„์† ์ฒ˜๋ฆฌํ•œ๋‹ค(codex ์˜ trust ๋Š” ํ”„๋กœ์ ํŠธ ๋กœ์ปฌ ์„ค์ •ยทํ›…ยทexec ์ •์ฑ…์„ ๋กœ๋“œํ•œ๋‹ค). resume ๋„ create ์™€ ๊ฐ™์€ ์Šค์œ„์น˜ยท๊ณ„์•ฝ์ด๋‹ค. 2. **๊ต์ฐจ ์„œ๋ฒ„ ํ˜ธ์ถœ์˜ ์Šค์ฝ”ํ”„ ์ œ๊ฑฐ (#23, `lib.sh`)**: herdr pane ์ด ๋‚ด๋ณด๋‚ด๋Š” ์„ธ id ๋Š” ๋ฐœ๊ธ‰ํ•œ ์„œ๋ฒ„์—์„œ๋งŒ ๋œป์ด ์žˆ์œผ๋ฏ€๋กœ **๋‹ค๋ฅธ ์„œ๋ฒ„๋ฅผ ๊ฒจ๋ƒฅํ•œ ํ˜ธ์ถœ์—์„œ๋งŒ** `env -u` ๋กœ ์ œ๊ฑฐํ•œ๋‹ค. ํ˜ธ์ถœ์ž ์„œ๋ฒ„๋Š” `HERDR_SESSION` ์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ, ์—†์œผ๋ฉด `HERDR_SOCKET_PATH` ์˜ ๋ชจ์–‘์—์„œ ๊ตฌํ•œ๋‹ค(herdr 0.8.2 ๋Š” ๊ธฐ๋ณธ ์„œ๋ฒ„ pane ์— `HERDR_SESSION` ์„ ๋‚ด๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค). 3. **์˜จ๋ณด๋”ฉ ์ „๋‹ฌ ํŒ์ • = ์—์ด์ „ํŠธ ์ž์‹ ์˜ `started` (#23, `lib.sh: delegate_publish_cmd / delegate_started_recorded / delegate_wait_started`, `create_session.sh`, delegate-job ๋ž˜ํผ)**: create ๋Š” ๋” ์ด์ƒ ์—์ด์ „ํŠธ ๋Œ€์‹  `started` ๋ฅผ ๋ฐœํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฐœํ–‰ ๋ช…๋ น ๋นŒ๋” ํ•˜๋‚˜๊ฐ€ create ์˜จ๋ณด๋”ฉ๊ณผ ๋ž˜ํผ์— ๊ฐ™์€ ๋ชจ์–‘(dispatch_tokenยทattempt ํฌํ•จ)์„ ์ฃผ๊ณ , `started` ํŒ์ • ํ•˜๋‚˜๋ฅผ ๋‘ ๋Œ€๊ธฐ ์ง€์ ์ด ๊ณต์œ ํ•œ๋‹ค. ์ด๋ฒˆ dispatch ์˜ `started` ๋งŒ ์ฆ๊ฑฐ์ด๋ฉฐ(๋กœ์ปฌ: events.log ์˜ dispatch_token, ์›๊ฒฉ: ๊ตฌ๋…์ž๊ฐ€ HMACยทํ† ํฐ ๊ฒ€์ฆ ๋’ค ๊ฐ์‚ฌ ๊ธฐ๋ก์— ๋‚จ๊ธฐ๋Š” `dispatch_fingerprint`(๊ฒ€์ฆํ•œ ํ† ํฐ์˜ SHA-256)+attempt), ์‹ ์› ์—†๋Š” ๊ณผ๊ฑฐ ๊ธฐ๋ก์€ fail closed ๋‹ค. ๋ž˜ํผ์˜ "๋ ˆ์ฝ”๋“œ ์ƒํƒœ๋งŒ ๋ณด๊ณ  ๋จผ์ € ์„ฑ๊ณต" ์šฐํšŒ๋Š” ์‚ฌ๋ผ์กŒ๋‹ค. send_keys ์˜ rc 4(Enter ์ˆ˜๋ฝ ๋ฏธ๊ด€์ธก)๋Š” ๊ฒฝ๊ณ ๋กœ ๋‚ฎ์ถ”๊ณ  ์—์ด์ „ํŠธ์˜ `started` ๊ฐ€ ๊ฒฐ์ •ํ•œ๋‹ค. 4. **์‹คํŒจ ์ฒ˜๋ฆฌ (#23, `create_session.sh`)**: ๋กค๋ฐฑ์ด kill ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๊ณ  `has-session` ์œผ๋กœ ์žฌํ™•์ธํ•œ๋‹ค. ๋ชป ์ฃฝ์˜€์œผ๋ฉด ํ–‰์— `rollback-failed` ๋ฅผ ์ ๊ณ  exit 43. ์˜จ๋ณด๋”ฉ์ด `MAM_ONBOARD_ACK_S`(๊ธฐ๋ณธ: ๋ž˜ํผ์˜ ์ฃผ์ž… ack ์œ ์˜ˆ โ†’ ์žก์˜ ack timeout 180) ์•ˆ์— ํ™•์ธ๋˜์ง€ ์•Š์•„๋„ ์„ธ์…˜์€ ๋ณด์กดํ•˜๊ณ  ์žก๋งŒ error ๋กœ ํ‘œ์‹œํ•œ๋‹ค(์‚ด์•„ ์žˆ๋Š” ์—์ด์ „ํŠธ๋ฅผ ๋А๋ฆฐ ์˜จ๋ณด๋”ฉ ๋•Œ๋ฌธ์— ์ฃฝ์ด์ง€ ์•Š๋Š”๋‹ค; ์ด๋ฏธ ์ข…๋ฃŒ๋œ ์žก์€ ๋ฎ์–ด์“ฐ์ง€ ์•Š๋Š”๋‹ค). ๋กค๋ฐฑ ์‹œ ์˜จ๋ณด๋”ฉ ์žก์€ `cancelled`, YAML ํ–‰์€ `terminated`/`rollback-failed` ๋กœ ํ‘œ์‹œํ•œ๋‹ค. TUI ๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š๊ณ  ํ”„๋กœ์„ธ์Šค๋„ ์—†์œผ๋ฉด exit 42. 5. **๋ฌธ์„œ**: create/resume SKILL.md(`--no-auto-trust`, `started` ๊ธฐ๋ฐ˜ ํŒ์ •, exit 42ยท43, `MAM_ONBOARD_ACK_S`, ๋ฏธํ™•์ธ ์˜จ๋ณด๋”ฉ ์‹œ ์„ธ์…˜ ๋ณด์กด ๊ทœ์น™). #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ** (`tests/test_issue23_24_startup_path.py` 1,268์ค„, `tests/test_issue24_resume_dialogs.py`): ์–ด๋Œ‘ํ„ฐ ๋‹ค์ด์–ผ๋กœ๊ทธ facts, ์ค€๋น„ ํ† ํฐ ๊ธฐ๋ฐ˜ ์ˆ˜๋ฝ, `--no-auto-trust`, ๊ต์ฐจ ์„œ๋ฒ„ ์Šค์ฝ”ํ”„ ์ œ๊ฑฐ(๊ฐ™์€ ์„œ๋ฒ„๋Š” ์œ ์ง€), ๊ณต์œ  `started` ํŒ์ •(์ด์ „ ์‹œ๋„ยท์‹ ์› ์—†๋Š” ๊ธฐ๋ก ๊ฑฐ๋ถ€, ์ข…๋ฃŒ๋œ ์žก ๋ณด์กด), ๋กค๋ฐฑ์˜ kill ํ™•์ธยทexit 43, ๋ฏธํ™•์ธ ์˜จ๋ณด๋”ฉ ์‹œ ์„ธ์…˜ ๋ณด์กด. ํ”ฝ์Šค์ฒ˜๋Š” ์‹ค์ œ `registry.py`ยท`publish_event.py` ๋ฅผ ์“ด๋‹ค(์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ฆฌ๋ทฐ์—์„œ ์†์œผ๋กœ ๋งŒ๋“  ํ”ฝ์Šค์ฒ˜๊ฐ€ ๊ฒฐํ•จ 3๊ฑด์„ ๊ฐ€๋ ธ๋˜ ๊ตํ›ˆ). ๊ธฐ์กด ํ…Œ์ŠคํŠธ 6ํŒŒ์ผ ์กฐ์ •(G9 ๊ฐ€๋“œ๋Š” ์ฝ”๋“œ๋งŒ ์ฝ์Œ). - **์‹ค์ œ herdr ์ˆ˜์šฉ ์‹œํ—˜ (์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค)**: ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ pane ์˜ ์ƒ์† ํ™˜๊ฒฝ์„ ๊ทธ๋Œ€๋กœ ๋‘” ์ฑ„ ๊ฒฉ๋ฆฌ ์„œ๋ฒ„์— agyยทgrokยทcodexยทopencode ๋ฅผ ์ˆœ์ฐจ ์ƒ์„ฑ โ€” 4๊ฑด ๋ชจ๋‘ ์‚ฌ๋žŒ ๊ฐœ์ž… ์—†์ด TUI ์ค€๋น„, ์˜จ๋ณด๋”ฉ ์ „๋‹ฌ, `started`(dispatch_token ํฌํ•จ) โ†’ `completed`. - **๋ฆฌ๋ทฐ**: ์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค 5์ธ 5๋ผ์šด๋“œ(์ฐจ๋‹จ ์ง€์  3๊ฑด โ€” `HERDR_SESSION` ์—๋งŒ ์˜์กดํ•œ ๊ต์ฐจ ์„œ๋ฒ„ ํŒ์ •, `data.dispatch_token` ์„ ์š”๊ตฌํ–ˆ์œผ๋‚˜ ์‹ค์ œ ๋ฐœํ–‰ ๋ช…๋ น์—” ์—†๋˜ ์˜จ๋ณด๋”ฉ ack, ๋ž˜ํผ์˜ ๋ ˆ์ฝ”๋“œ ์ƒํƒœ ์šฐํšŒ์™€ ์‹ ์› ์—†๋Š” ์›๊ฒฉ ๊ฐ์‚ฌ ๊ธฐ๋ก โ€” ๋ชจ๋‘ ํ•ด์†Œ) ์ „์› PASS. ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ PR head ๋ฅผ detached ์›ŒํฌํŠธ๋ฆฌ์— ๋ฐ›์•„ ์žฌ๊ฒ€ํ† : @f4ba205 claudeยทagyยทgrok PASS; codex ๋Š” ๋ฆฌ๋ทฐ ๋„์ค‘ ํ˜ธ์ŠคํŠธ๊ฐ€ ๋ฐ˜๋ณต ์ ˆ์ „(pmset ๋กœ๊ทธ)์— ๋“ค์–ด๊ฐ€ API ์ŠคํŠธ๋ฆผ์ด ๋Š๊ธฐ๋Š” ๋ฐ”๋žŒ์— ๋ฆฌํฌํŠธ๋ฅผ ๋‚จ๊ธฐ์ง€ ๋ชปํ–ˆ๊ณ , ๋Œ€ํ™” ๊ธฐ๋ก์—์„œ ๋ณต์›ํ•œ ํƒ์นจ 2๊ฑด(ํ™˜๊ฒฝ๋ณ€์ˆ˜ `MAM_AUTO_TRUST=0` ์ด ์Šคํฌ๋ฆฝํŠธ์˜ ๊ธฐ๋ณธ๊ฐ’ export ์— ๋ฎ์—ฌ ํ•ธ๋“ค๋Ÿฌ์— 1 ๋กœ ๋„๋‹ฌ, ์ฟจ๋‹ค์šด์— ๊ฑธ๋ฆฐ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๊ฐ™์€ ํ™”๋ฉด์˜ ๋‹ค์Œ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ๊ฐ€๋ฆผ)์„ 05b0ea4 ๋กœ ์ •์ •. ๋ธํƒ€ ๋ฆฌ๋ทฐ @05b0ea4 claudeยทagyยทgrokยทcodex 4์ธ ์ „์› PASS(identity 4๊ฑด ๋ชจ๋‘ base f4ba205 โ†’ head 05b0ea4, evidence review-tree; codex ๋Š” ๋‘ ์ •์ •์„ ๋˜๋Œ๋ฆฐ ์‚ฌ๋ณธ์—์„œ ์ƒˆ ํšŒ๊ท€๊ฐ€ ์‹คํŒจํ•จ์„ ์—ญ๊ฒ€์ฆ). ๋ฆฌํฌํŠธ์™€ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ์ž์ฒด venv ๊ฐ€ ์žˆ๋Š” ์›ŒํฌํŠธ๋ฆฌ @f4ba205)**: 1111 ํ†ต๊ณผ / 1 skip / ์‹คํŒจ 0 (51๋ถ„ 21์ดˆ, ๋ฆฌ๋ทฐ ๋ฃจํ”„์™€ ๋ณ‘ํ–‰); 05b0ea4 ์—์„œ startup-pathยทresume-dialogsยทTUI readinessยทheadless reconcileยทversion ์Šค์œ„ํŠธ 123 ํ†ต๊ณผ. - **๋ณ„๊ฑด**: ๋ฆฌ๋ทฐ ๋ฃจํ”„๊ฐ€ diff ๋ฅผ ๋‹จ์ผ argv ๋กœ ๋„˜๊ฒจ Linux ์˜ ๋‹จ์ผ ์ธ์ž ์ƒํ•œ(131,072 ๋ฐ”์ดํŠธ)์„ ๋„˜๋Š” ๋ธŒ๋žœ์น˜ diff ์—์„œ `OSError: [Errno 7]` ๋กœ ์ฃฝ๊ณ  rc 0 ์œผ๋กœ ๋๋‚œ๋‹ค(์ˆ˜์ง‘๊ธฐ ์ƒํ•œ `MAM_DIFF_MAX_BYTES` ๊ธฐ๋ณธ 200000 ์ด ๋” ๋†’๋‹ค). ์ž‘์„ฑ ์ธก์ด ๋ณ„๋„ ์ด์Šˆ๋กœ ๋“ฑ๋ก. ๋น„์ฐจ๋‹จ: ๊ฐ์‚ฌ ๊ธฐ๋ก์˜ ์ƒˆ ํ•„๋“œ(`dispatch_verified`ยท`dispatch_fingerprint`ยท`attempt_id`)๊ฐ€ MESSAGING.mdยทdelegate-job SKILL.md ์— ์—†์Œ, `MAM_INJECT_ACK_GRACE_S` ๊ฐ€ create ์˜ ๋Œ€๊ธฐ์—๋„ 2์ˆœ์œ„๋กœ ์ž‘์šฉํ•œ๋‹ค๋Š” ์ ์ด ๋ฌธ์„œ์— ์—†์Œ, `MAM_AUTO_TRUST` ๋Š” `0` ๋งŒ ๋ˆ๋‹ค๋Š” ์ ๊ณผ ํ”Œ๋ž˜๊ทธ ์šฐ์„  ๊ทœ์น™์ด resume SKILL.md ์— ์—†์Œ, codex ์—…๋ฐ์ดํŠธ ์•ˆ๋‚ด์— `2`(์ด๋ฒˆ๋งŒ ๊ฑด๋„ˆ๋›ฐ๊ธฐ)๋ฅผ ๊ณ ๋ฅด๋ฏ€๋กœ ๋‹ค์Œ ์‹œ์ž‘ ๋•Œ ๋‹ค์‹œ ๋œธ(`3` ์ด๋ฉด ๋‹ค์Œ ๋ฒ„์ „๊นŒ์ง€ ์กฐ์šฉํ•จ), ๋‹ค์ด์–ผ๋กœ๊ทธ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ํ™”๋ฉด์˜ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ๊ฒ€์‚ฌํ•˜์ง€ ์•Š์Œ(pane ์ด `-c $WORKSPACE` ๋กœ ๋œจ๋ฏ€๋กœ ์‹ค๋ฌด ์œ„ํ—˜ ๋‚ฎ์Œ), `test_issue24_resume_dialogs.py` 4๊ฑด์ด ๊ฐ ~40 s. ### ๐Ÿš€ `v4.7.1` โ€” Issue #25 ์Šน๊ฒฉ๋œ ๊ณ„ํš์„œ ์žฌ์‚ฌ์šฉ์€ `--plan-file` ๋กœ๋งŒ: ์•”๋ฌต์  ์ž๋™ ๋กœ๋“œ ์ œ๊ฑฐ, ๋นˆ ๊ณ„ํš ๋ฆฌํฌํŠธ๋Š” ์กฐ์šฉํžˆ ๋„˜์–ด๊ฐ€์ง€ ์•Š์Œ (2026-09-22, PR #32) > **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: `run_loop.sh` ๋ฅผ `--plan` ์—†์ด ์‹คํ–‰ํ•˜๋ฉด ๋Ÿฌ๋„ˆ๊ฐ€ planner ๋ฅผ ์ž๋™์œผ๋กœ ๊ณ ๋ฅด๊ณ  `.agents/reports//plan-*.md` ์ค‘ mtime ์ด ๊ฐ€์žฅ ์ตœ๊ทผ์ธ ํŒŒ์ผ์„ "์ด๋ฏธ ์ˆ˜๋ฆฝ๋œ ๊ณ„ํš์„œ"๋กœ creator ์—๊ฒŒ ๋„˜๊ธฐ๋˜ ๋ฌธ์ œ(Issue #25)๋ฅผ ๊ณ ์ณค๋‹ค. ์ €์žฅ์†Œ์—๋Š” ๋‹ค๋ฅธ ์ด์Šˆ์˜ ๊ณ„ํš์„œ๊ฐ€ ์ˆ˜์‹ญ ๊ฐœ ์ปค๋ฐ‹๋ผ ์žˆ๊ณ  ์ฒดํฌ์•„์›ƒ ์งํ›„์˜ mtime ์€ ์˜๋ฏธ๊ฐ€ ์—†์–ด, ๋ฌด๊ด€ํ•œ ๊ณ„ํš์ด ๊ตฌํ˜„ ํ”„๋กฌํ”„ํŠธ์— ์‹ค๋ ธ๋‹ค(v4.7.0 ๋ณ‘ํ•ฉ ๋ฃจํ”„์—์„œ๋„ Issue #6 ๊ณ„ํš์„œ๊ฐ€ ์‹ค๋ฆฐ ๊ฒƒ์„ ๊ด€์ธก). ์˜ต์…˜์ด ํ•˜๋‚˜ ๋Š˜์ง€๋งŒ ์ž˜๋ชป๋œ ๊ธฐ๋ณธ ๋™์ž‘์„ ์—†์• ๋Š” ์ˆ˜์ •์ด๋ผ PATCH ๋กœ ์˜ฌ๋ฆฐ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **์•”๋ฌต์  ๊ณ„ํš ๋กœ๋“œ ์ œ๊ฑฐ (`lib_py/loop/runner.py`, `multi-agent-mux-loop/scripts/run_loop.sh`)**: `--plan` ์—†๋Š” ์‹คํ–‰์€ ์–ด๋–ค ๊ณ„ํš์„œ๋„ ์ฝ์ง€ ์•Š๋Š”๋‹ค. Python ๋Ÿฌ๋„ˆ์™€ ๋ ˆ๊ฑฐ์‹œ ์…ธ ๋Ÿฌ๋„ˆ(`MAM_LOOP_PY=0`) ๋ชจ๋‘์—์„œ mtime ๊ธฐ๋ฐ˜ ์„ ํƒ์„ ์ง€์› ๊ณ , creator ๋Š” ์Šค์Šค๋กœ ๊ณ„ํšํ•œ๋‹ค. ์ฒดํฌํฌ์ธํŠธ์˜ `current_plan` ์€ ์“ฐ๊ธฐ ์ „์šฉ์ด๋ผ ์˜› ๊ณ„ํš์„ ๋˜์‚ด๋ฆฌ๋Š” ๊ฒฝ๋กœ๊ฐ€ ์—†๋‹ค. 2. **`--plan-file ` (Python ๋Ÿฌ๋„ˆ ์ „์šฉ)**: ์Šน๊ฒฉ๋œ ๊ณ„ํš์„œ์˜ ์žฌ์‚ฌ์šฉ์€ ํŒŒ์ผ์„ ๋ช…์‹œํ•  ๋•Œ๋งŒ ํ•œ๋‹ค. ์ƒ๋Œ€๊ฒฝ๋กœ๋Š” **์ œ์–ด ๋ฃจํŠธ ๊ธฐ์ค€**(์›ŒํฌํŠธ๋ฆฌ์—์„œ ๋Œ๋ ค๋„ ๊ฐ™์Œ)์ด๊ณ , ์žก์ด ํ•˜๋‚˜๋„ ๋“ฑ๋ก๋˜๊ธฐ ์ „์— ์ฝ์–ด ๊ฒ€์‚ฌํ•œ๋‹ค(์—†๋Š” ํŒŒ์ผยท๋””๋ ‰ํ„ฐ๋ฆฌยท๋นˆ ํŒŒ์ผยท์ฝ๊ธฐ ์‹คํŒจ๋Š” exit 1). `--plan` ๊ณผ ํ•จ๊ป˜ ์“ธ ์ˆ˜ ์—†๊ณ , `--review-only` ์—์„œ๋Š” ๊ฒฝ๊ณ  ํ›„ ๋ฌด์‹œํ•œ๋‹ค. ๋ ˆ๊ฑฐ์‹œ ์…ธ ๋Ÿฌ๋„ˆ๋Š” ์ด ์˜ต์…˜์„ ๋ฃจํ”„ ๋ฝ ์ „์— ๋ช…์‹œ์ ์œผ๋กœ ๊ฑฐ๋ถ€ํ•œ๋‹ค. 3. **๊ณ„ํš ํ…์ŠคํŠธ ๊ฒ€์‚ฌ ํ•˜๋‚˜๋ฅผ ๋ชจ๋“  ๋กœ๋“œ ๊ฒฝ๋กœ์— (`LoopRunner._plan_text`)**: planner ์˜ ์ฒซ ๋ฆฌํฌํŠธ๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ๋ฃจํ”„๋ฅผ ๋ฉˆ์ถ˜๋‹ค(๊ณ„ํš ๋‹จ๊ณ„์— ๋น„์šฉ์„ ์“ฐ๊ณ ๋„ ์กฐ์šฉํžˆ ์ž์œจ ๊ณ„ํš์œผ๋กœ ๊ฐ•๋“ฑ๋˜๋˜ ๋ฌธ์ œ). ํ† ๋ก (refine)ยท์žฌ๊ณ„ํš ๋ฆฌํฌํŠธ๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ๊ฒฝ๊ณ ํ•˜๊ณ  ๊ธฐ์กด ๊ณ„ํš์„ ์œ ์ง€ํ•œ๋‹ค. 4. **๋ฌธ์„œ**: loop SKILL.md ์˜ ์˜ต์…˜ ํ‘œยทWorkflowยท"๊ณ„ํš์„œ๊ฐ€ ๋น„์–ด ์žˆ์„ ๋•Œ" ์ ˆ. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ 17๊ฐœ** (`tests/test_issue25_plan_reuse.py`, P1โ€“P11): ์˜ต์…˜ ์—†๋Š” ์‹คํ–‰์ด ์Šน๊ฒฉ๋œ ๊ณ„ํš์„œ๋ฅผ ์ฝ์ง€ ์•Š์Œ(Pythonยท์…ธ), `--plan-file` ์˜ ์ œ์–ด ๋ฃจํŠธ ๊ธฐ์ค€ ํ•ด์„ยท์‹คํŒจ ๊ฒฝ๋กœยท`--plan`/`--review-only` ์กฐํ•ฉยท์…ธ ๋Ÿฌ๋„ˆ์˜ ๊ฑฐ๋ถ€, ๋นˆ planner ๋ฆฌํฌํŠธ์˜ ์ค‘๋‹จ, ๋นˆ refine ์˜ ๊ธฐ์กด ๊ณ„ํš ์œ ์ง€. ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ฆฌ๋ทฐ์—์„œ 17๊ฑด ์ค‘ 13๊ฑด์ด ์ˆ˜์ • ์ „ ์†Œ์Šค์—์„œ ์‹คํŒจํ•จ์„ ํ™•์ธ. - **๋ฆฌ๋ทฐ**: ์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ฃจํ”„(`--worktree --base --review-only`) 1ํšŒ์ฐจ @5fa5923 claudeยทagyยทopencode PASS, grokยทcodex NOT PASS(์ƒ๋Œ€๊ฒฝ๋กœ `--plan-file` ์ด ์‹คํ–‰ cwd ๊ธฐ์ค€์œผ๋กœ ํ•ด์„) โ†’ 1e82651 ๋กœ ์ •์ •, 2ํšŒ์ฐจ 5์ธ ์ „์› PASS. ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ PR head ๋ฅผ detached ์›ŒํฌํŠธ๋ฆฌ์— ๋ฐ›์•„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์žฌ๊ฒ€ํ† : @1e82651 claudeยทagyยทgrokยทcodex 4์ธ ์ „์› PASS(1ํšŒ์ฐจ, identity 4๊ฑด ๋ชจ๋‘ base 6fc9194 โ†’ head 1e82651, evidence review-tree). ๋ฆฌํฌํŠธ์™€ identity ์‚ฌ์ด๋“œ์นด๋Š” `.agents/reports//`. - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ์ž์ฒด venv ๊ฐ€ ์žˆ๋Š” ์›ŒํฌํŠธ๋ฆฌ @1e82651)**: 1037 ํ†ต๊ณผ / 1 skip / ์‹คํŒจ 0 (35๋ถ„ 26์ดˆ, ๋ฆฌ๋ทฐ ๋ฃจํ”„์™€ ๋ณ‘ํ–‰). - **๋น„์ฐจ๋‹จ ํ›„์†**: ๋ ˆ๊ฑฐ์‹œ ์…ธ ๋Ÿฌ๋„ˆ์˜ plan ๋ชจ๋“œ ์ฝ๊ธฐ์—๋Š” ๋นˆ ๊ณ„ํš์„œ ๊ฒ€์‚ฌ๊ฐ€ ์—†์Œ(E1), `--plan-file` ์‹คํ–‰์—์„œ `[ESCALATE: PLANNER]` ๊ฐ€ ์žฌ๊ณ„ํš์œผ๋กœ ์ด์–ด์ง€์ง€ ์•Š์Œ(E2, ๊ธฐ์กด ๊ฒŒ์ดํŠธ), ๋ฆฌ๋ทฐ ๊ฒฉ๋ฆฌ ์›ŒํฌํŠธ๋ฆฌ์— `.venv` ๊ฐ€ ์—†์–ด `run_loop.sh` ๋ฅผ ํ•˜์œ„ ํ”„๋กœ์„ธ์Šค๋กœ ๋„์šฐ๋Š” ํ…Œ์ŠคํŠธ๋Š” `AGENT_PYTHON_BIN` ์—†์ด๋Š” ์‹คํŒจ(E3, #21 ํ•ญ๋ชฉ 5 ์šด์˜ ํ›„์†), Phase 1 ์˜ `getmtime` ๊ฒฝํ•ฉ(E4), ๊ณต๋ฐฑ์ด ๋“  ๊ฒฝ๋กœ ์ „์šฉ ํ…Œ์ŠคํŠธ ์—†์Œ. ### ๐Ÿš€ `v4.7.0` โ€” Issue #21 ์›ŒํฌํŠธ๋ฆฌ ์šด์˜ ๋ชจ๋ธ 2๋‹จ๊ณ„: ์›ŒํฌํŠธ๋ฆฌ ํ—ฌํผ, ๋ฆฌ๋ทฐ identity, ์ˆ˜์šฉ ์‚ฌ๋ก€ A1โ€“A8, #20 ํ…Œ์ŠคํŠธ ๊ฒฉ๋ฆฌ, #27, #28 ํ˜ธ์ŠคํŠธ ์ด์‹์„ฑ (2026-09-20, PR #29) > **๋งˆ์ด๋„ˆ ๋งˆ์ผ์Šคํ†ค (MINOR Release)**: ์›ŒํฌํŠธ๋ฆฌ ์šด์˜ ๋ชจ๋ธ(#21, `docs/design/worktree-creator-model.md`)์˜ ์ž‘์—… ํ•ญ๋ชฉ 4ยท5ยท7 ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค. ์ด์Šˆ๋ณ„ ์›ŒํฌํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  ์ง€์šฐ๋Š” ํ—ฌํผ 2๊ฐœ, ๋ฆฌ๋ทฐ ๋ผ์šด๋“œ๋ฅผ `(loop_id, base, head, tree)` ์— ๋ฌถ๋Š” ๋ฆฌ๋ทฐ identity ์™€ ๋ฃจํ”„ ์˜ต์…˜ 2๊ฐœ(`--review-isolation`, `--remote-review`), ์ˆ˜์šฉ ์‚ฌ๋ก€ 8๊ฑด์ด ์ถ”๊ฐ€๋˜์–ด MINOR ๋กœ ์˜ฌ๋ฆฐ๋‹ค. ํ•ญ๋ชฉ 8(๋‹ค์ค‘ ๋ฃจํ”„ยท๋ฆฌ๋ทฐ์–ด ์Šค์ผ€์ค„๋Ÿฌ)์€ ๋ณด๋ฅ˜๋กœ ๋‚จ์•„ #21 ์€ ์—ด์–ด ๋‘”๋‹ค. ํ•จ๊ป˜ #20(ํ…Œ์ŠคํŠธ๊ฐ€ ์‹ค์ œ `.mam` ์„ ์˜ค์—ผ), #27(`.gitignore` ๊ด€๋ฆฌ ๋ธ”๋ก์˜ `.venv`), #28(Linux/py3.14 ํ˜ธ์ŠคํŠธ์˜ ํ…Œ์ŠคํŠธ ์‹คํŒจ 33๊ฑด)์„ ๋‹ซ๋Š”๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **์›ŒํฌํŠธ๋ฆฌ ์ค€๋น„ยทํ•ด์ฒด ํ—ฌํผ (ํ•ญ๋ชฉ 4, `multi-agent-mux-create/scripts/worktree_prepare.sh`, `multi-agent-mux-stop/scripts/worktree_remove.sh`)**: prepare ๋Š” ์ œ์–ด ๋ฃจํŠธ ์˜†์— ์›ŒํฌํŠธ๋ฆฌ๋ฅผ ๋ฉฑ๋“ฑํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ (์•ˆ์ชฝ ๊ฒฝ๋กœ๋Š” ๊ฑฐ๋ถ€), ์›ŒํฌํŠธ๋ฆฌ **์ž์ฒด์˜** `.venv` ๋ฅผ ์ œ์–ด ๋ฃจํŠธ์˜ `.venv/bin/python` ์œผ๋กœ ๋งŒ๋“ค์–ด `requirements*.txt` ๋ฅผ ์„ค์น˜ํ•˜๋ฉฐ(์‹ฌ๋งํฌ๋Š” ๋งŒ๋“ค์ง€ ์•Š๊ณ  ๋‚จ์•„ ์žˆ์œผ๋ฉด exit 1 โ€” ๋งํฌ๊ฐ€ `git add -A` ๋กœ ์ปค๋ฐ‹๋œ ์‚ฌ๊ณ ์˜ ์žฌ๋ฐœ ๋ฐฉ์ง€), `/.venv`ยท`.pytest_cache/` ๋ฅผ ๊ณต์œ  `info/exclude` ์— ๋„ฃ๊ณ , ์„œ๋ธŒ๋ชจ๋“ˆ์€ `--submodules` ๋กœ๋งŒ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค(์‹คํŒจํ•œ ์‹œ๋„๋Š” ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฒดํฌ์•„์›ƒ์œผ๋กœ ๋กค๋ฐฑ). `--creator-agent` ๋Š” ์ œ์–ด ๋ฃจํŠธ ๋ฐ”์ธ๋”ฉ๊ณผ `MAM_NO_SUBSCRIBER_DAEMON=1` ๋กœ creator ์„ธ์…˜์„ ๋งŒ๋“ค๊ณ  `BASE=` ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. remove ๋Š” ์ž ๊ธˆยท์‹คํ–‰ ์ค‘ ์„ธ์…˜(`--stop`)ยท์—ด๋ฆฐ ์žกยท์ˆ˜์ •(untracked ํฌํ•จ)ยท์„œ๋ธŒ๋ชจ๋“ˆ(๋ฏธpush ์ปค๋ฐ‹: HEAD ์™€ tagยทnotes ๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ๋กœ์ปฌ ref, stash, deinit ๋œ ์ €์žฅ์†Œ, ๋ถ€๋ถ„ ์ €์žฅ์†Œ) ์„ **์•„๋ฌด๊ฒƒ๋„ ๋ฐ”๊พธ๊ธฐ ์ „์—** ํŒ์ •ํ•˜๊ณ , `--stop` ๋’ค ์ œ๊ฑฐ ์ง์ „์— ํ•œ ๋ฒˆ ๋” ํŒ์ •ํ•œ ๋’ค, ์„œ๋ธŒ๋ชจ๋“ˆ ์ƒํƒœ๊ฐ€ ์žˆ๋Š” ์›ŒํฌํŠธ๋ฆฌ๋งŒ ๊ฒ€์ฆ๋œ ๋‹จ์ผ `--force` ๋กœ ์ง€์šด๋‹ค. 2. **resume ๊ณ„์•ฝ (ํ•ญ๋ชฉ 4, `multi-agent-mux-resume`)**: ์„ธ์…˜์€ ๊ธฐ๋ก๋œ ์›๋ž˜ cwd ์—์„œ๋งŒ ์žฌ๊ฐœํ•œ๋‹ค(๋‹ค๋ฅธ ๊ฒฝ๋กœ๋Š” exit 2, cwd ์—†๋Š” ๊ธฐ์กด ํ–‰์€ ํ˜ธํ™˜). ์ด๋ฏธ ์‹คํ–‰ ์ค‘ ๊ฒฝ๋กœ์—์„œ๋„ ์ œ์–ด ํ‰๋ฉด ๋ฐ”์ธ๋”ฉ์„ ์˜์†ํ™”ํ•˜๊ณ , ์›ŒํฌํŠธ๋ฆฌ ๋ฐ”์ธ๋”ฉ ๊ณ„์‚ฐ ์‹คํŒจ๋Š” ์‚ผํ‚ค์ง€ ์•Š๊ณ  exit 2 ๋กœ ์•Œ๋ฆฐ๋‹ค. create ๋Š” ์ฆ‰์‹œ reconcileยทwatchdog ์„ ์ œ์–ด ๋ฃจํŠธ์˜ ์ฝ”๋“œ๋กœ ๋„์šด๋‹ค. 3. **๋ฆฌ๋ทฐ identity (ํ•ญ๋ชฉ 5, `lib_py/loop/runner.py`, `run_loop.sh --review-isolation worktree|env`, `--remote-review refuse|diff-only`)**: `--worktree`/`--base` ์—์„œ ๋งค ๋ฆฌ๋ทฐ ๋ผ์šด๋“œ ์ „์— ์†Œ์Šค์˜ ๋ฏธ์ปค๋ฐ‹ยทuntracked ์ƒํƒœ๊นŒ์ง€ ๋‹ด์€ ref ์—†๋Š” ์Šค๋ƒ…์ƒท ์ปค๋ฐ‹์„ ๋งŒ๋“ค๊ณ , ๋ฆฌ๋ทฐ์–ด์˜ diff ๋„ `base..snapshot` ์—์„œ ๋งŒ๋“ค๋ฉฐ, ๊ธฐ๋ณธ์€ ์Šค๋ƒ…์ƒท์˜ detached ๋ฆฌ๋ทฐ ์›ŒํฌํŠธ๋ฆฌ์—์„œ ๊ฒ€์ฆํ•œ๋‹ค. ๋ผ์šด๋“œ๊ฐ€ ๋๋‚œ ๋’ค ์†Œ์Šค๊ฐ€ ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด PASS ๋ฅผ ๋ฒ„๋ฆฌ๊ณ  ๋‹ค์‹œ ๋ฆฌ๋ทฐํ•˜๊ณ (๋งˆ์ง€๋ง‰ ๋ฐ˜๋ณต์ด๋ฉด exit 1), ์Šน๊ฒฉ ์ง์ „์—๋„ ์žฌํ™•์ธํ•˜๋ฉฐ, ๋ฆฌํฌํŠธ ์˜†์— `report-.identity.json`(loop_idยทbaseยทheadยทtreeยท์Šค๋ƒ…์ƒทยท๋ฆฌ๋ทฐ์–ดยทtransportยทevidence)์„ ๋‘”๋‹ค. ์ถ”์ ๋˜์ง€ ์•Š๋Š” ์ค‘์ฒฉ ์ €์žฅ์†Œ์™€ dirty ์„œ๋ธŒ๋ชจ๋“ˆ(`--ignore-submodules=none --untracked-files=all`, ์–ด๋–ค `submodule.*.ignore`ยท`diff.ignoreSubmodules`ยท`status.showUntrackedFiles` ์„ค์ •๋„ ์ˆจ๊ธฐ์ง€ ๋ชปํ•จ)์€ ๊ฑฐ๋ถ€ํ•œ๋‹ค. ์›๊ฒฉ ๋ฆฌ๋ทฐ์–ด๋Š” ์ด ํ˜ธ์ŠคํŠธ์˜ ๋ฆฌ๋ทฐ ํŠธ๋ฆฌ์— ๋‹ฟ์„ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์–ด๋–ค ์žก๋„ ์œ„์ž„ํ•˜๊ธฐ ์ „์— ๊ฑฐ๋ถ€ํ•˜๊ฑฐ๋‚˜ `diff-only` ๋กœ๋งŒ ๋ฐ›๋Š”๋‹ค. ์˜ต์…˜์ด ์—†์œผ๋ฉด ์ด์ „ ๋™์ž‘ ๊ทธ๋Œ€๋กœ๋‹ค. 4. **ํ…Œ์ŠคํŠธ ๊ฒฉ๋ฆฌ (#20, `tests/conftest.py`)**: ์ˆ˜์ง‘ ์ „์— ambient ์ œ์–ด ํ‰๋ฉด ๋ฐ”์ธ๋”ฉ์„ ๊ธฐ๋กยท์ œ๊ฑฐํ•˜๊ณ  ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท๊ฐ์‚ฌ ๋กœ๊ทธ ๊ธฐ๋ณธ ๋ฃจํŠธ๋ฅผ tmp ๋กœ ๋ณด๋‚ธ๋‹ค. ๊ธฐ๋ก๋œ ์ œ์–ด ํ‰๋ฉด์˜ `.mam/jobs`ยท`.mam/delegate_job_logs` ์— ์ƒˆ ํ•ญ๋ชฉ์ด ์ƒ๊ธฐ๋ฉด ์„ธ์…˜ ๊ฐ€๋“œ๊ฐ€ ์‹คํŒจ์‹œํ‚ค๊ณ , ๊ธฐ๋ณธ ํ•ด์„ ๊ทœ์น™์„ ๊ฒ€์ฆํ•˜๋Š” #15 ํ…Œ์ŠคํŠธ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์˜ตํŠธ์•„์›ƒํ•œ๋‹ค. `MAM_ENV_FILE` ์€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ๋กœ๋กœ ๊ณ ์ •ํ•ด ์‹ค `.mam.env` ๋ฅผ ์ฝ์ง€ ์•Š๋Š”๋‹ค. 5. **ํ˜ธ์ŠคํŠธ ์ด์‹์„ฑ (#28)**: `tests/test_workspace_scope.py` ๋Š” `bash -c` ๋กœ ์‹คํ–‰(dash ์— `source` ์—†์Œ), argparse ๋„์›€๋ง ๋น„๊ต๋Š” `_normalize_help` ๋กœ py3.10+ ์˜ "options:" ๋ฅผ ํก์ˆ˜, `test_deploy_freshness.py` ์˜ private-server ๋ฌธ์„œยทdocker ์ž์‚ฐ ์˜์กด ํ…Œ์ŠคํŠธ๋Š” ์ž์‚ฐ์ด ์—†์œผ๋ฉด skip, `lib_py/agents/base.py`ยท`lib_py/delegation/heartbeat.py` ์˜ ํ”„๋กœ์„ธ์Šค ์Šค์บ”์€ POSIX `ps -eo pid=,ppid=,args=` ๋กœ ํ†ต์ผ(BSD ์ „์šฉ ์ธ์ž๊ฐ€ Linux ์—์„œ purge 12๊ฑด์„ ๊นจ๋œจ๋ฆฌ๋˜ ์›์ธ ๊ฐ€์„ค). `test_issue14_delivery_ack.py` ์˜ ๋ฒฝ์‹œ๊ณ„ close ์—ฌ์œ ๋Š” `timeout_s + 15`. 6. **`.gitignore` ๊ด€๋ฆฌ ๋ธ”๋ก (#27, `deploy/install.sh`, `deploy/install_mam.sh`)**: `.venv` ๋ฅผ ํŒŒ์ผยท๋””๋ ‰ํ„ฐ๋ฆฌยท๋งํฌ ์–ด๋А ํ˜•ํƒœ๋กœ๋„ ๋ฌด์‹œํ•œ๋‹ค(์žฌ์„ค์น˜๋Š” ์˜› ๊ทœ์น™๋งŒ ๋ฐ”๊พธ๊ณ  ์‚ฌ์šฉ์ž ์ค„์€ ๋ณด์กด). 7. **๋ฌธ์„œ**: ์„ค๊ณ„ ๋ฌธ์„œ(ํ•ญ๋ชฉ 4ยท5ยท7 ์ƒํƒœ, ์„œ๋ธŒ๋ชจ๋“ˆ ์ œ๊ฑฐ ๊ทœ์น™, resume cwd ๊ทœ์น™, ๋ฃจํŠธ 3์ข…ยท๋ฆฌ๋ทฐ identity), create/stop/resume/loop SKILL.md, ํ˜ธํ™˜์„ฑ ํ‘œ. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ** (`tests/test_issue21_phase2_loop_a.py`, `tests/test_issue21_loop_b.py`, `tests/test_issue21_acceptance.py`(A1โ€“A8), `tests/test_issue20_state_isolation.py`, `tests/test_issue21_worktree_prereqs.py` ํ™•์žฅ): prepare ์˜ ๋ฉฑ๋“ฑ์„ฑยท๊ฑฐ๋ถ€ยทdry-runยทvenv(์ƒ์„ฑ/์œ ์ง€/๋งํฌ ๊ฑฐ๋ถ€/์ธํ„ฐํ”„๋ฆฌํ„ฐ ๋ถ€์žฌ)ยท์„œ๋ธŒ๋ชจ๋“ˆ ๋กค๋ฐฑ, remove ์˜ R1โ€“R6(์ข…๋ฃŒ ์ค‘ ์“ฐ๊ธฐ, ์„œ๋ธŒ๋ชจ๋“ˆ stash, ๋ถ€๋ถ„ init ๋กค๋ฐฑ, ๊ธฐ์กด ํŒŒ์ผ, deinit ์ €์žฅ์†Œ, tag ๋กœ๋งŒ ๋ณด์กดํ•œ ์ปค๋ฐ‹)๊ณผ `status.showUntrackedFiles=no` ๋กœ ์ˆจ๊ธด untracked ํŒŒ์ผ, ๋ฆฌ๋ทฐ identity ์˜ ์Šค๋ƒ…์ƒทยทstaleยท์Šน๊ฒฉ ์žฌํ™•์ธยท๊ฒฉ๋ฆฌ ์‹คํŒจยท์›๊ฒฉ ๊ฑฐ๋ถ€ยทdirty ์„œ๋ธŒ๋ชจ๋“ˆ(์ˆจ๊น€ ์„ค์ • ํฌํ•จ), #20 ๊ฐ€๋“œ. ์ˆ˜์šฉ ์‚ฌ๋ก€ A1โ€“A8 ์€ mock herdr ๊ธฐ์ค€์ด๋ฉฐ ์‹ค์ œ pane ์˜ env ๊ฐ’ ํ™•์ธ์€ ๋‹จ๊ณ„ C ๋กœ ๋‚จ๊ธด๋‹ค. - **๋ฆฌ๋ทฐ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค, PR head ๋ฅผ detached ์›ŒํฌํŠธ๋ฆฌ์— ๋ฐ›์•„ 4์ธ ๋…๋ฆฝ ๊ฒ€ํ† )**: @46ea4cc claudeยทagyยทgrok PASS, codex NOT PASS(R1 `status.showUntrackedFiles=no` ๊ฐ€ ์ˆจ๊ธด ์„œ๋ธŒ๋ชจ๋“ˆ untracked ํŒŒ์ผ์ด force ์ œ๊ฑฐ๋ฅผ ํ†ต๊ณผ, R2 ๊ฐ™์€ ๊ฒฐํ•จ์œผ๋กœ ๋ฆฌ๋ทฐ ์Šค๋ƒ…์ƒท์ด ๋ฏธํฌ์ฐฉ ๋‚ด์šฉ์„ ์Šน์ธ, R3 prepare ์˜ `.venv` ์‹ฌ๋งํฌ) โ†’ 08db1da ๋กœ ์ •์ •. ๋ธํƒ€ ๋ฆฌ๋ทฐ @08db1da claudeยทagyยทgrok PASS(๋น„์ฐจ๋‹จ D1โ€“D3 โ†’ 80a5614 ๋ฐ˜์˜), codex ๋Š” ๋ฃจํ”„์˜ ํ”„๋กฌํ”„ํŠธ ์ฃผ์ž… ์‹คํŒจ(์žก 14c7dc38, `started` ์—†์Œ)๋กœ ์ง์ ‘ ์žฌ์œ„์ž„ํ•ด @80a5614 PASS. ๋ฆฌํฌํŠธ 8๊ฑด์€ `.agents/reports//report-.md`(291e38b5ยทf27519ffยท5f1486eeยทb880c1f2ยทf1174fc0ยทd9275506ยท19d56cdeยทf6b21a0d). - **์ „์ฒด ์Šค์œ„ํŠธ (๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค)**: @46ea4cc 1013 ํ†ต๊ณผ / 1 skip / ์‹คํŒจ 1๊ฑด(๋ถ€ํ•˜ ์‹œ ๋ฒฝ์‹œ๊ณ„ ๊ฒฝ๊ณ„, 90a289d ๋กœ ์—ฌ์œ  ํ™•๋Œ€). @08db1da(์ž์ฒด venv ๊ฐ€ ์—†๋Š” ์›ŒํฌํŠธ๋ฆฌ์—์„œ ์‹คํ–‰) 1015 ํ†ต๊ณผ / 1 skip / 4 ์‹คํŒจ โ€” 4๊ฑด ๋ชจ๋‘ ์›ŒํฌํŠธ๋ฆฌ์˜ `.venv` ๋ถ€์žฌ๋กœ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ system python(paho ์—†์Œ)์„ ์žก์€ ํƒ“์ด๋ฉฐ, ์›ŒํฌํŠธ๋ฆฌ ์ž์ฒด venv ๋ฅผ ๋งŒ๋“  ๋’ค 4๊ฑด ๋ชจ๋‘ ํ†ต๊ณผ(80a5614 ๊ฐ€ prepare ์˜ ์„ค์น˜ ๋ชฉ๋ก์— delegate-job requirements ๋ฅผ ๋„ฃ์€ ๋™๊ธฐ). - **#28 ํ›„์†**: Linux/py3.14 ํ˜ธ์ŠคํŠธ์—์„œ `ps -eo` ์ˆ˜์ •์ด purge 12๊ฑด์„ ์‹ค์ œ๋กœ ๊ณ ์น˜๋Š”์ง€ ํ™•์ธ์ด ํ•„์š”ํ•˜๋‹ค(๊ฐ€์„ค ๋‹จ๊ณ„). ### ๐Ÿš€ `v4.6.0` โ€” Issue #21 ์›ŒํฌํŠธ๋ฆฌ ์šด์˜ ๋ชจ๋ธ 1๋‹จ๊ณ„ ์„ ํ–‰ ์ž‘์—…: ๋ž˜ํผ ์ ˆ๋Œ€๊ฒฝ๋กœ, ๋ฃจํ”„ `--worktree`, ์ œ์–ด ํ‰๋ฉด ๋ฐ”์ธ๋”ฉ, ์„ค๊ณ„ ๋ฌธ์„œ (2026-09-14, PR #22) > **๋งˆ์ด๋„ˆ ๋งˆ์ผ์Šคํ†ค (MINOR Release)**: "์›Œํฌ์ŠคํŽ˜์ด์Šค = ์ฒดํฌ์•„์›ƒ ๊ฒฝ๋กœ" ๊ฐ€์ •์„ ๋А์Šจํ•˜๊ฒŒ ํ•˜๋Š” 1๋‹จ๊ณ„๋‹ค. creator ๋Š” ์ด์Šˆ๋ณ„ git ์›ŒํฌํŠธ๋ฆฌ์—์„œ, ๋ฆฌ๋ทฐ์–ด ํ’€์€ ๋ณธ ์ฒดํฌ์•„์›ƒ์—์„œ, ์žก ๋ธŒ๋ฆฌํ”„ยท๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท๋ฆฌํฌํŠธยท์ฒดํฌํฌ์ธํŠธ(์ œ์–ด ํ‰๋ฉด)๋Š” ๋ณธ ์ฒดํฌ์•„์›ƒ ํ•˜๋‚˜์— ๋‘๋Š” ์šด์˜ ๋ชจ๋ธ(#21, `docs/design/worktree-creator-model.md`)์˜ ์„ ํ–‰ ์กฐ๊ฑด 4๊ฑด(์ž‘์—… ํ•ญ๋ชฉ 1ยท2ยท3ยท6)์„ ๊ตฌํ˜„ํ–ˆ๋‹ค. ๋ฃจํ”„ ๋Ÿฌ๋„ˆ ์˜ต์…˜ 3๊ฐœ์™€ `lib_py/control_plane.py` ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด MINOR ๋กœ ์˜ฌ๋ฆฐ๋‹ค. ์˜ต์…˜ ์—†์ด ์“ฐ๋ฉด ์ข…์ „ ๋™์ž‘๊ณผ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ๊ฐ™๋‹ค. ํ•ญ๋ชฉ 4(์›ŒํฌํŠธ๋ฆฌ ์ค€๋น„/์ข…๋ฃŒ ํ—ฌํผ)ยท5(๋ฆฌ๋ทฐ identity)ยท7(์‹œํ—˜ ์šด์˜)ยท8(๋‹ค์ค‘ ๋ฃจํ”„)์€ #21 ์— ๋‚จ์•„ ์žˆ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **๋ž˜ํผ ์ ˆ๋Œ€๊ฒฝ๋กœํ™” (`multi-agent-mux-delegate-job`)**: ์ฃผ์ž… ๋ฌธ๊ตฌ์˜ brief ๊ฒฝ๋กœ, ์‹œ์ž‘/์„ฑ๊ณต/์˜ค๋ฅ˜ ๋ช…๋ น์˜ `--registry-dir`, ๊ฐ์‚ฌ ๋กœ๊ทธ ๋ฃจํŠธ(`DELEGATE_JOB_LOGS_DIR`), ๋ธŒ๋ฆฌํ”„์˜ Output Report Path ๊ฐ€ `$REGISTRY_DIR` ๊ธฐ์ค€ ์ ˆ๋Œ€๊ฒฝ๋กœ๊ฐ€ ๋๋‹ค(`_abs_registry_dir`ยท`_pub_cmd`ยท`_instructions`, `printf %q` ์ธ์šฉ). ์›ŒํฌํŠธ๋ฆฌ cwd ์˜ creator ๊ฐ€ `started` ๋ฅผ ๋ฐœํ–‰ํ•˜๊ณ  ๋ฆฌํฌํŠธ๋ฅผ ๋ณธ ์ฒดํฌ์•„์›ƒ์˜ ์žก ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์“ด๋‹ค. 2. **๋ฃจํ”„ ๋Ÿฌ๋„ˆ `--worktree ` / `--base ` / `--review-only` (`lib_py/loop/runner.py`, `run_loop.sh`)**: diff ์ˆ˜์ง‘ยท`base_commit`ยทhead ๋Š” ์›ŒํฌํŠธ๋ฆฌ ๊ธฐ์ค€, wrapperยท๋ ˆ์ง€์ŠคํŠธ๋ฆฌ(`MAM_JOBS_DIR` ์กด์ค‘)ยท์ฒดํฌํฌ์ธํŠธยท๋ฆฌํฌํŠธ ์Šน๊ฒฉ์€ ๋ณธ ์ฒดํฌ์•„์›ƒ ๊ธฐ์ค€. ์›ŒํฌํŠธ๋ฆฌ๋Š” ๊ฐ™์€ ์ €์žฅ์†Œ์˜ top-level ์ด์–ด์•ผ ํ•˜๊ณ  `--base` ๋Š” head ์˜ ์กฐ์ƒ์ด์–ด์•ผ ํ•œ๋‹ค. ์œ„์ž„ ํ”„๋กฌํ”„ํŠธ ์•ž์— `[์›ŒํฌํŠธ๋ฆฌ ์ปจํ…์ŠคํŠธ]`(์†Œ์Šค ๊ฒฝ๋กœยท๋ธŒ๋žœ์น˜ยทbaseยทheadยท์ปค๋ฐ‹ ์ˆ˜ยท๋ฏธ์ปค๋ฐ‹ ์—ฌ๋ถ€ยท์ œ์–ด ํ‰๋ฉด) ๋ธ”๋ก์„ ๋ถ™์ธ๋‹ค. ๋ณ€๊ฒฝ ์ง‘ํ•ฉ์ด ๋น„๋ฉด fail-closed. `MAM_LOOP_WORKTREE` env ๋™์น˜. 3. **์ œ์–ด ํ‰๋ฉด ๋ฐ”์ธ๋”ฉ (`lib_py/control_plane.py`, create/resume/stop/status/reconcile)**: `WORKSPACE_ROOT`/`AGENT_SESSIONS_YAML`/`MAM_JOBS_DIR`/`DELEGATE_JOB_LOGS_DIR` 4๊ฐœ ๊ฒฝ๋กœ๋ฅผ ์„ธ์…˜ ํ–‰์˜ `control_plane` ํ•„๋“œ๋กœ ์˜์†ํ™”ํ•˜๊ณ , `--workspace` ๋งŒ์œผ๋กœ ์œ ๋„๋˜๋Š” ๊ธฐ๋ณธ๊ฐ’๊ณผ ๋‹ค๋ฅผ ๋•Œ๋งŒ pane ์ƒ์„ฑ ํ˜ธ์ถœ์˜ `--env` ๋กœ ์ „ํŒŒํ•œ๋‹ค(์žฌ์‚ฌ์šฉ idle pane ์—๋Š” env ๋ฅผ ์—์ด์ „ํŠธ ์ธ์ž๋กœ ๋„˜๊ธฐ์ง€ ์•Š์œผ๋ฏ€๋กœ env ๊ฐ€ ํ•„์š”ํ•˜๋ฉด pane ์„ ์ƒˆ๋กœ ๋งŒ๋“ ๋‹ค โ€” h25 ๊ณ„์•ฝ ๊ฐฑ์‹ ). ์ƒ๋Œ€ `MAM_JOBS_DIR` ์€ jobs dir ํ•˜๋‚˜๋กœ ์ •๊ทœํ™”, stop ์€ ๊ฐ์‚ฌ ๋กœ๊ทธ ๋ฃจํŠธ๋ฅผ ์œ ์ง€, status/reconcile ์€ `MAM_JOBS_DIR` ์„ ์กด์ค‘ํ•œ๋‹ค. ๊ฒฝ๋กœ์— ๊ณต๋ฐฑ์ด ์žˆ์œผ๋ฉด ์ „ํŒŒ๋ฅผ ๊ฑฐ๋ถ€(rc 3)ํ•œ๋‹ค. 4. **์„ค๊ณ„ ๋ฌธ์„œ ์ •์ • (`docs/design/worktree-creator-model.md`)**: 4์ธ ๋ฆฌ๋ทฐ(#21 ๋Œ“๊ธ€, `docs/design/reviews/`)๋ฅผ ๋ฐ˜์˜ํ•ด "ํ˜„์žฌ ์ฝ”๋“œ ๊ธฐ์ค€" ์„ ๋ถ€๋ถ„ ์ง€์› + ํ•„์š”ํ•œ ์ˆ˜์ •์œผ๋กœ, 1๋‹จ๊ณ„ ๋ฆฌ๋ทฐ ์ „์šฉ ํ๋ฆ„, ์Šคํ‚ฌ๋ณ„ ํ˜ธํ™˜์„ฑ ํ‘œ, ๋ฃจํŠธ 3์ข…(์ œ์–ด/์†Œ์Šค/๋Ÿฐํƒ€์ž„)๊ณผ ๋ฆฌ๋ทฐ identity ๋ฅผ ๋ช…์‹œํ–ˆ๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ**: `tests/test_issue21_worktree_prereqs.py`(๋ž˜ํผ ์ ˆ๋Œ€๊ฒฝ๋กœยท๋Ÿฌ๋„ˆ ์˜ต์…˜ยท์›ŒํฌํŠธ๋ฆฌ ๊ฒ€์ฆยท์ œ์–ด ํ‰๋ฉด ๋ฐ”์ธ๋”ฉยท์ „ํŒŒยทํ˜ธํ™˜์„ฑ), `tests/test_herdr_shim_contract.py` h25 ๊ฐฑ์‹ , T2-8 ์ƒŒ๋“œ๋ฐ•์Šคํ™”. - **๋ฆฌ๋ทฐ**: ์ž‘์„ฑ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ฃจํ”„์—์„œ claudeยทagyยทgrok PASS @13259ed, codex PASS @4a81d15; ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ PR head ๋ฅผ detached ์›ŒํฌํŠธ๋ฆฌ์— ๋ฐ›์•„ claudeยทagyยทcodex ์žฌ๊ฒ€ํ†  PASS @4a81d15(๋ฆฌํฌํŠธ `.agents/reports/`). ๋ณ‘ํ•ฉ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์ „์ฒด ์Šค์œ„ํŠธ @4a81d15: 940 ํ†ต๊ณผ, ์‹คํŒจ 1๊ฑด์€ ๋ถ€ํ•˜ ์‹œ ๊ฒฝ๊ณ„๊ฐ’์— ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„ ํŒ์ •(๋‹จ๋… ์žฌ์‹คํ–‰ ํ†ต๊ณผ). - **ํ›„์†(๋น„์ฐจ๋‹จ, #21ยท#20)**: create/resume ์˜ `mam_control_plane_json โ€ฆ || true` ๊ฐ€ ์‹คํŒจ๋ฅผ ์‚ผํ‚ด โ†’ ๊ฒฝ๊ณ /exit 2; resume ์˜ "herdr ์ด๋ฏธ ์‚ด์•„ ์žˆ์Œ" ๊ฒฝ๋กœ๊ฐ€ `CONTROL_PLANE_JSON` ๋ฏธ์ „๋‹ฌ; T2-6 ์˜ `os.environ` ์ง์ ‘ ๋ณ€๊ฒฝ; issue14 `_job_for_inprocess_publish` ๊ฐ€ ambient `WORKSPACE_ROOT` ๋กœ ์‹ค์ œ ๊ฐ์‚ฌ ๋กœ๊ทธ์— ๊ธฐ๋ก; acceptance5 ๊ฐ€ ๊ณต์šฉ ๋ธŒ๋กœ์ปค์—์„œ ๋™์‹œ ์‹คํ–‰ ์‹œ ๊ฐ™์€ ์„ธ์…˜ ์ด๋ฆ„์˜ ๋Ÿฌ๋„ˆ๊ฐ€ ์„œ๋กœ์˜ dispatch ๋ฅผ ์†Œ๋น„(์„ธ์…˜ ์ด๋ฆ„ uuid4 ํ™” ํ•„์š”); create ๋ ˆ์‹œํ”ผ์— `MAM_NO_SUBSCRIBER_DAEMON=1`. ### ๐Ÿš€ `v4.5.0` โ€” Issue #18 ์ƒ์กดยท์ „๋‹ฌ ํŒ์ •์„ ํ”„๋กœํ† ์ฝœ ๊ณ„์•ฝ์œผ๋กœ ์™„๊ฒฐ: heartbeat ์ž๋™ํ™”, Kร—N ๋ฌด์‘๋‹ต, T ๋‚ด `started`, retained ์ƒํƒœ, ์›๊ฒฉ ์ „์†ก (2026-09-13) > **๋งˆ์ด๋„ˆ ๋งˆ์ผ์Šคํ†ค (MINOR Release)**: ์—์ด์ „ํŠธ์˜ ์ƒ์กดยท์ „๋‹ฌ ํŒ์ •์„ TUI ์Šคํฌ๋ž˜ํ•‘(herdr `agent_status`, pane ๋ฌธ์ž์—ด, ํ™”๋ฉด ๋ณ€ํ™”)์—์„œ ๋–ผ์–ด๋‚ด์–ด, ์—์ด์ „ํŠธ๊ฐ€ ์Šค์Šค๋กœ ๋ฐœํ–‰ํ•˜๋Š” ์ด๋ฒคํŠธ(`started`/`progress`/`completed`/`error`)์— **์˜๋ฌดยท์ฃผ๊ธฐยทํƒ€์ž„์•„์›ƒ ๊ทœ์น™์ด ์žˆ๋Š” ๊ณ„์•ฝ**์„ ๋ถ€์—ฌํ–ˆ๋‹ค(Issue #18, #17 ํ•ญ๋ชฉ 1). ์ƒˆ ์ด๋ฒคํŠธ ์ข…๋ฅ˜๋Š” ์—†๋‹ค. ์›๊ฒฉ ํ˜ธ์ŠคํŠธ์˜ ์—์ด์ „ํŠธ(paneยท๋””์Šคํฌ ๊ณต์œ  ์—†์Œ)๋ฅผ ๊ฐ™์€ ๊ณ„์•ฝ์œผ๋กœ ๋‹ค๋ฃจ๋Š” `remote:` ์ „์†ก์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ๋ž˜ํผ ์˜ต์…˜ 3๊ฐœ(`--heartbeat`, `--heartbeat-misses`, `--ack-timeout`)์™€ `publish_event --heartbeat`, `job_subscriber --remote`, `remote.py` CLI ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด MINOR ๋กœ ์˜ฌ๋ฆฐ๋‹ค. ๊ธฐ๋ณธ๊ฐ’(N=30, K=4 โ†’ idle 120 s, T=180)์€ ์ข…์ „ idle ๊ธฐ๋ณธ๊ฐ’๊ณผ ๊ฐ™์œผ๋ฏ€๋กœ ๊ธฐ์กด ํ˜ธ์ถœ์€ ๋™์ž‘์ด ์œ ์ง€๋œ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **heartbeat ์˜๋ฌด์˜ ์ž๋™ํ™” (`lib_py/delegation/heartbeat.py`, `publish_event.py --heartbeat N`)**: `started --heartbeat N` ์ด ๋ถ„๋ฆฌ ์„ธ์…˜์˜ ํƒ€์ด๋จธ๋ฅผ ๋„์›Œ N์ดˆ๋งˆ๋‹ค `progress`(detail `heartbeat`)๋ฅผ ๊ฐ™์€ `dispatch_token`/`attempt_id` ๋กœ ์„œ๋ช…ยท๋ฐœํ–‰ํ•œ๋‹ค. ํ„ฐ๋ฏธ๋„ ์ด๋ฒคํŠธ๋Š” ๋จผ์ € attempt ๋ฒ”์œ„์˜ barrier(`/.heartbeat.barrier`, seq ๋ฅผ ๋–ผ๋Š” ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ lock ์•ˆ์—์„œ ๊ธฐ๋ก)๋ฅผ ์„ธ์šฐ๊ณ , pidfile(`/.heartbeat.pid`)๋กœ ํƒ€์ด๋จธ๋ฅผ ๋ฉˆ์ถ˜ ๋’ค ์ง„ํ–‰ ์ค‘์ธ beat ๊ฐ€ ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ ๋‹ค์Œ(`MAM_HEARTBEAT_DRAIN_S`, ๊ธฐ๋ณธ 1 s ๋’ค ๊ฐ•์ œ ์ข…๋ฃŒ โ€” ์™€์ด์–ด์— ๋‚จ์€ beat ๋Š” ์ทจ์†Œ๋˜๋ฉฐ ํ„ฐ๋ฏธ๋„์„ ๋ถ™์žก์•„ ๋‘์ง€ ์•Š๋Š”๋‹ค; pid ์žฌ์‚ฌ์šฉ ๋Œ€๋น„ cmdline ํ™•์ธ) ์ž๊ธฐ seq ๋ฅผ ๋—€๋‹ค. beat ๋Š” ๊ฐ™์€ lock ์•ˆ์—์„œ barrierยทํ„ฐ๋ฏธ๋„ ๋ ˆ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด ๊ฑฐ๋ถ€๋˜๋ฏ€๋กœ ํ„ฐ๋ฏธ๋„ ๋’ค์—๋Š” heartbeat ๊ฐ€ ๋””์Šคํฌ์—๋„ ์™€์ด์–ด์—๋„ ์—†๊ณ , ๋Šฆ์€ ๋น„ํ„ฐ๋ฏธ๋„ ์ด๋ฒคํŠธ์˜ retained state ๊ฐ€ ํ„ฐ๋ฏธ๋„ ์Šค๋ƒ…์ƒท์„ `running` ์œผ๋กœ ๋ฎ์ง€ ์•Š๋Š”๋‹ค(๋ฆฌ๋ทฐ 2ํšŒ์ฐจ codex R3). ๊ฐ™์€ ์žก์— `started --heartbeat` ๋ฅผ ๋‹ค์‹œ ๋ณด๋‚ด ํƒ€์ด๋จธ๋ฅผ ๊ต์ฒดํ•  ๋•Œ๋„ ์ด์ „ ํƒ€์ด๋จธ๋ฅผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ drainยท๊ฐ•์ œ ์ข…๋ฃŒํ•œ ๋’ค์—์•ผ pidfile ์„ ๋„˜๊ธฐ๋ฏ€๋กœ, ์ด๋ฏธ admission ์„ ํ†ต๊ณผํ•ด ์™€์ด์–ด ์ง์ „์— ์žˆ๋˜ beat ๊ฐ€ ํ„ฐ๋ฏธ๋„ ๋’ค์— ๋‚˜๊ฐ€์ง€ ์•Š๋Š”๋‹ค(๋ฆฌ๋ทฐ 3ํšŒ์ฐจ codex R3). ํƒ€์ด๋จธ์˜ ์ƒ์„ฑยท๊ต์ฒดยท์ •์ง€๋Š” ์žก๋ณ„ lifecycle lock(`/.heartbeat.lock`, seq lock ๊ณผ ๋ณ„๊ฐœ)์œผ๋กœ ์ง๋ ฌํ™”๋˜์–ด ๋™์‹œ `started --heartbeat` ๋„ ์ถ”์ ๋˜๋Š” ์†Œ์œ ์ž ํ•˜๋‚˜๋งŒ ๋‚จ๊ธฐ๊ณ , ๋๋‚œ attempt ์—๋Š” ํƒ€์ด๋จธ๋ฅผ arm ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ข…๋ฃŒํ•˜๋Š” ํƒ€์ด๋จธ๋Š” ๊ทธ lock ์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์œผ๋ฉฐ(๋ฐ”์˜๋ฉด ์†Œ์œ  ์—ฐ์‚ฐ์ด pidfile ์„ ์น˜์šด๋‹ค), spawn ๋’ค pidfile ๊ธฐ๋ก์ด ์‹คํŒจํ•˜๋ฉด ์ƒˆ ํƒ€์ด๋จธ๋ฅผ ๊ณง๋ฐ”๋กœ ์ข…๋ฃŒยทํšŒ์ˆ˜ํ•œ๋‹ค(๋ฆฌ๋ทฐ 4ยท5ํšŒ์ฐจ codex). ํƒ€์ด๋จธ๋Š” ๋ ˆ์ฝ”๋“œ ํ„ฐ๋ฏธ๋„ยท์žฌdispatchยท`timeout_sec` ์ดˆ๊ณผยท**๊ฐ์‹œ ๋Œ€์ƒ ์—์ด์ „ํŠธ ํ”„๋กœ์„ธ์Šค ์†Œ๋ฉธ**(`--watch-pid`, ๊ธฐ๋ณธ์€ ์‹œ์ž‘ ๋ช…๋ น์„ ์‹คํ–‰ํ•œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์—์ด์ „ํŠธ CLI ์กฐ์ƒ; ์›๊ฒฉ ๋Ÿฌ๋„ˆ๋Š” ์ž๊ธฐ ์ž์‹ ) ์‹œ ์Šค์Šค๋กœ ๋๋‚œ๋‹ค. ์ฃฝ์€ ์—์ด์ „ํŠธ๋Š” heartbeat ๋ฅผ ๋ฉˆ์ถ”๊ณ  Kร—N ๋’ค ๋‹ซํžˆ๋ฉฐ, ์‚ด์•„ ์žˆ์ง€๋งŒ ๋ฉˆ์ถ˜ ์—์ด์ „ํŠธ๋Š” ๊ณ„์† heartbeat ๋ฅผ ๋‚ธ๋‹ค(ํ”„๋กœ์„ธ์Šค ์ƒ์กด ๊ณ„์•ฝ). ๊ฐ beat ๋Š” ์™€์ด์–ด ์ด์ „์— events.log ์— ๊ธฐ๋ก๋˜๋ฏ€๋กœ ๋””์Šคํฌ ์ƒ์กด ํŒ์ •์—๋„ ๊ทธ๋Œ€๋กœ ์“ฐ์ธ๋‹ค. ๋ž˜ํผ๊ฐ€ ์ฃผ์ž…ํ•˜๋Š” ์‹œ์ž‘ ๋ช…๋ น์ด `--heartbeat N` ์„ ํฌํ•จํ•˜๊ณ , ๋ธŒ๋ฆฌํ”„์— ๊ณ„์•ฝ(N/K/T)์ด ๋ช…์‹œ๋œ๋‹ค. 2. **๋ฌด์‘๋‹ต ํŒ์ • = ๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ๋กœ๋ถ€ํ„ฐ Kร—N (`registry.py`, ๋ž˜ํผ `finalize_unfinished`)**: ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋ ˆ์ฝ”๋“œ์— `heartbeat_sec`ยท`heartbeat_misses`ยท`ack_timeout_sec`ยท`transport` ๊ฐ€ ์ถ”๊ฐ€๋˜๊ณ  `idle_timeout_sec` ์€ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด Kร—N ์œผ๋กœ ์œ ๋„๋œ๋‹ค. ๋ž˜ํผ์˜ R1 ์œ ์˜ˆ๋Š” events.log ์˜ ์ตœ๊ทผ ์ด๋ฒคํŠธ(Kร—N ์ด๋‚ด)๋งŒ์„ ์ฆ๊ฑฐ๋กœ ์‚ผ๊ณ , herdr ์ƒํƒœยทpane busy ํ† ํฐ์€ ์ง„์ž… ์‹œ ํ•œ ๋ฒˆ `liveness: ... (diagnostic only)` ๋กœ ๊ธฐ๋ก๋งŒ ํ•œ๋‹ค. ํ—ค๋“œ๋ฆฌ์Šค ์—์ด์ „ํŠธ(ํ•ญ์ƒ idle, ๋นˆ pane)๋„ heartbeat ๋งŒ์œผ๋กœ Kร—N ์„ ๋„˜๊ฒจ ์‚ด์•„ ์žˆ๊ณ , heartbeat ๊ฐ€ ๋Š๊ธฐ๋ฉด `deadline_timeout` ์œผ๋กœ ๋‹ซํžˆ๋ฉฐ ์ดํ›„ `completed` ๋Š” CAS ๊ฐ€ ๊ฑฐ๋ถ€ํ•œ๋‹ค. 3. **์ „๋‹ฌ ํ™•์ธ = T ๋‚ด `started` (`_wait_started`, `lib.sh: _sks_job_started`)**: ์ฃผ์ž… ํ›„ T์ดˆ(`--ack-timeout`, ๊ธฐ๋ณธ 180 โ€” ์‹ค์ œ ์—์ด์ „ํŠธ๋Š” ๋ธŒ๋ฆฌํ”„๋ฅผ ์ฝ๊ณ  ๋‚˜์„œ ์‹œ์ž‘ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฏ€๋กœ ์—ฌ์œ  ์žˆ๊ฒŒ; ๋ฒฝ์‹œ๊ณ„ ์ƒํ•œ) ์•ˆ์— ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๊ฐ€ `pending` ์„ ๋ฒ—์–ด๋‚˜๊ฑฐ๋‚˜ events.log(์ด๋ฒˆ attempt ์˜ dispatch_token ์„ ๊ฐ€์ง„ `started` ๋งŒ)/๊ตฌ๋…์ž ๋กœ๊ทธ์— `started` ๊ฐ€ ์—†์œผ๋ฉด rc 1ยท`agent_blocked` ๋กœ fail-closed ํ•˜๊ณ  ์žฌ์ฃผ์ž…ํ•˜์ง€ ์•Š๋Š”๋‹ค. `send_keys_safe` ๋Š” accepted-but-unconfirmed ์ œ์ถœ์„ `agent_status working` ๋˜๋Š” job ์˜ `started` ๋กœ๋งŒ ํ™•์ธํ•˜๋ฉฐ pane ๋ณ€ํ™”ยทbusy ํ† ํฐ ์‹ ํ˜ธ๋Š” ์ œ๊ฑฐ๋๋‹ค(๋ฌด์‹ ํ˜ธ rc 4 ๋Š” ์œ ์ง€). `MAM_INJECT_ACK_GRACE_S` ๋Š” T ์˜ override ๋กœ ๋‚จ๋Š”๋‹ค. 4. **์™€์ด์–ด ๋‚ด๊ตฌ์„ฑ (`publish_event.py: build_state_payload`, `job_subscriber.py`)**: ๋ชจ๋“  ์ด๋ฒคํŠธ ๋’ค์— ๊ฐ™์€ ์—ฐ๊ฒฐ๋กœ retained `/state`(statusยทlast_eventยทseq, HMAC ์„œ๋ช…)๋ฅผ ๋ฐœํ–‰ํ•œ๋‹ค. ๊ตฌ๋…์ž๋Š” state ํ† ํ”ฝ๋„ ๊ตฌ๋…ํ•ด (์žฌ)์ ‘์† ์ฆ‰์‹œ ํ„ฐ๋ฏธ๋„ ์ƒํƒœ๋ฅผ ๋ณต๊ตฌํ•˜๊ณ , ์ด๋ฒคํŠธ, ๊ทธ๋ฆฌ๊ณ  ์ง€๊ธˆ๊นŒ์ง€ ๋ฐ›์€ ์–ด๋–ค ์ด๋ฒคํŠธยทstate ๋ณด๋‹ค seq ๊ฐ€ ์•ž์„  state ๋กœ๋งŒ idle ์ฐฝ์„ ๋ฆฌ์…‹ํ•˜๋ฉฐ(์žฌ์ ‘์† ๋•Œ ๋‹ค์‹œ ์˜ค๋Š” ์Šค๋ƒ…์ƒท์ด๋‚˜ ๋” ์˜ค๋ž˜๋œ state ๋Š” ํ„ฐ๋ฏธ๋„ ๋ณต๊ตฌ์—๋งŒ ์“ฐ๊ณ  ์ƒ์กด์œผ๋กœ ์น˜์ง€ ์•Š๋Š”๋‹ค โ€” ๋ฆฌ๋ทฐ 2ํšŒ์ฐจ codex R4), seq ์ ํ”„๋ฅผ ๊ฐ์‚ฌ ๋กœ๊ทธ `seq_gap` ์œผ๋กœ ๋‚จ๊ธฐ๊ณ , ์žฌ์ ‘์†์„ `reconnected` ๋กœ ๊ธฐ๋กํ•œ๋‹ค. `--no-state` ๋กœ ๋Œ ์ˆ˜ ์žˆ๋‹ค. 5. **์›๊ฒฉ ์ „์†ก (`lib_py/delegation/remote.py`, `scripts/remote.py`, ๋ž˜ํผ `remote:`)**: `--agent-session remote:` ์ด๋ฉด preflightยทidle ํ•ธ๋“œ์…ฐ์ดํฌยทpane ์ฃผ์ž…์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  `MAM_DISPATCH_SECRET` ๋กœ ์„œ๋ช…ํ•œ retained `/dispatch`(๋ธŒ๋ฆฌํ”„ยทauth_tokenยทdispatch_tokenยทattempt_idยท๊ณ„์•ฝ)๋ฅผ ๋ฐœํ–‰ํ•œ ๋’ค T ๋‚ด `started` ๋ฅผ ๊ธฐ๋‹ค๋ฆฐ๋‹ค. ๋‹ค๋ฅธ ํ˜ธ์ŠคํŠธ์˜ `remote.py run --session [--exec CMD] [--once]` ๋Ÿฌ๋„ˆ๊ฐ€ ์ž๊ธฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๊ฐ™์€ idยทidentity ๋กœ ์žก์„ ๋ฏธ๋Ÿฌ๋งํ•˜๊ณ , dispatch ๋ฅผ retire ํ•˜๊ณ , `started --heartbeat N` ํ›„ ๋ช…๋ น์„ ์‹คํ–‰ํ•ด ํ„ฐ๋ฏธ๋„ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•œ๋‹ค. ๊ตฌ๋…์ž์˜ `--remote`(transport=remote ์žก์€ ์ž๋™)๊ฐ€ `started`ยทํ„ฐ๋ฏธ๋„์„ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋ฐ˜์˜ํ•œ๋‹ค. ์„œ๋ช… ์—†๋Š” dispatch ๋Š” ๊ฑฐ๋ถ€๋œ๋‹ค. ๋Ÿฌ๋„ˆ๋Š” dispatch ๋ฅผ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ lock ์•ˆ์—์„œ ๋ฐ›์•„๋“ค์ธ๋‹ค: ๊ฐ™์€ ํ† ํฐ์€ ๋ฉฑ๋“ฑ(๋๋‚œ attempt ๋Š” ๋‹ค์‹œ ๋Œ๋ฆฌ์ง€ ์•Š์Œ), ๋” ๋‚ฎ์€ attempt ๋‚˜ ๊ฐ™์€ attempt ์˜ ๋‹ค๋ฅธ ํ† ํฐ์€ ๋ ˆ์ฝ”๋“œยทevents.log ๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ๊ฑฐ๋ถ€, ๋” ์ƒˆ attempt ๋งŒ ๊ต์ฒดํ•œ๋‹ค(`register_job(supersede_older_attempt=True)`, events.log ๋Š” ๋ณด์กด). `--exec` ๋ช…๋ น์€ ์ž๊ธฐ ์„ธ์…˜(ํ”„๋กœ์„ธ์Šค ๊ทธ๋ฃน)์—์„œ ๋Œ๊ณ , `timeout_sec` ์ด๋‚˜ ๋Ÿฌ๋„ˆ ์ค‘๋‹จ(SIGTERM/SIGINT) ๋•Œ ๊ทธ๋ฃน ์ „์ฒด๋ฅผ TERMโ†’KILL ๋กœ ๋๋‚ธ ๋’ค ์˜ค๋ฅ˜๋ฅผ ๋ฐœํ–‰ํ•œ๋‹ค(๋ฆฌ๋ทฐ 2ํšŒ์ฐจ codex R1ยทR2). ์›๊ฒฉ ํ–‰์€ ๊ธฐ๋ณธ Python ๋ฃจํ”„ ๋Ÿฌ๋„ˆ์—์„œ๋งŒ ์ง€์›๋œ๋‹ค(๋ ˆ๊ฑฐ์‹œ `MAM_LOOP_PY=0` ์€ `herdr:` ์ „์šฉ). ๋ฃจํ”„ ๋Ÿฌ๋„ˆ๋Š” YAML ํ–‰์˜ `transport: remote` ๋ฅผ `remote:` ์œผ๋กœ ๋„˜๊ธฐ๊ณ , `MAM_HEARTBEAT_SEC`/`MAM_HEARTBEAT_MISSES`/`MAM_ACK_TIMEOUT_S` ๋ฅผ ๊ณ„์•ฝ ์˜ต์…˜์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค. 6. **๋ฌธ์„œ**: MESSAGING.md ยง2.5 ๊ณ„์•ฝ ํ‘œยท์›๊ฒฉ ์ „์†ก, delegate-job SKILL.md(์˜ต์…˜ยท์ข…๋ฃŒ ์ฝ”๋“œยทํ”„๋กœํ† ์ฝœ), registry.md(๊ณ„์•ฝ ํ•„๋“œ), loop SKILL.md(ํ™˜๊ฒฝ๋ณ€์ˆ˜ยท์›๊ฒฉ ํ–‰). #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ 31๊ฐœ** (`tests/test_issue18_liveness_contract.py`): ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๊ณ„์•ฝ ํ•„๋“œยทidle ์œ ๋„(2), ์ž๋™ heartbeat ๋””์Šคํฌ ๊ธฐ๋กยทํ„ฐ๋ฏธ๋„ ์ •์ง€ยท์žฌdispatch ์ •์ง€ยทํ„ฐ๋ฏธ๋„ ์ด๋ฒคํŠธ์™€์˜ ์กฐํ•ฉ ๊ฑฐ๋ถ€(3), retained state ์„œ๋ช…ยทseq ๊ฐญยทstate ๋กœ ํ„ฐ๋ฏธ๋„ ๋ณต๊ตฌยท์œ„์กฐ/stale state ๋“œ๋กญ(3), `started` ๊ธฐ๋ฐ˜ ์ „๋‹ฌ ํ™•์ธ๊ณผ pane ์‹ ํ˜ธ ๋ฌด์‹œ(2), ์ˆ˜์šฉ ๊ธฐ์ค€ 1ยท2ยท3(ํ—ค๋“œ๋ฆฌ์Šค heartbeat ์ƒ์กด, T ๋‚ด ๋ฏธ`started` fail-closedยท๋‹จ์ผ ์ฃผ์ž…, heartbeat ์†Œ์‹ค ํ›„ deadline_timeoutยท๋Šฆ์€ completed ๊ฑฐ๋ถ€), ์ˆ˜์šฉ ๊ธฐ์ค€ 5(๋ธŒ๋กœ์ปค๋งŒ ๊ณต์œ ํ•˜๋Š” ์›๊ฒฉ ๋Ÿฌ๋„ˆ ์‹œ๋ฎฌ๋ ˆ์ด์…˜), ์„œ๋ช… ์—†๋Š” dispatch ๊ฑฐ๋ถ€, ๋ฃจํ”„ ๋Ÿฌ๋„ˆ ๊ณ„์•ฝยท`remote:` ๋ผ๋ฒจ. ๋ฆฌ๋ทฐ ๊ต์ •๋ถ„ โ€” 1ํšŒ์ฐจ: ๊ฐ์‹œ ์—์ด์ „ํŠธ ํ”„๋กœ์„ธ์Šค ์†Œ๋ฉธ ์‹œ ํƒ€์ด๋จธ ์ •์ง€, ์žฌ์‚ฌ์šฉ๋œ pid ๋ฏธ์‹ ํ˜ธ, ์ด๋ฒˆ attempt ์˜ `started` ๋งŒ ์ธ์ •, ๋๋‚œ attempt ์˜ ์žฌ์ „์†ก dispatch ๋ฌด์‹œ; 2ํšŒ์ฐจ: ๋” ๋‚ฎ์€ยท์ถฉ๋Œ attempt ์˜ dispatch ๊ฑฐ๋ถ€์™€ ๋ ˆ์ฝ”๋“œยท๋กœ๊ทธ ๋ฌด๋กค๋ฐฑ, ๋ช…๋ น ํƒ€์ž„์•„์›ƒ ์‹œ ํ”„๋กœ์„ธ์Šค ๊ทธ๋ฃน ์ „์ฒด ์ข…๋ฃŒ, in-flight beat ๊ฐ€ ํ„ฐ๋ฏธ๋„ ๋’ค์— ๋ฐœํ–‰๋˜์ง€ ์•Š์Œ, state ๋Š” seq ๊ฐ€ ์•ž์„ค ๋•Œ๋งŒ ์ƒ์กด ๊ฐฑ์‹ (๋‹จ์œ„ยท์žฌ์ „๋‹ฌ ์ข…๋‹จ 2๊ฑด); 3ํšŒ์ฐจ: ํƒ€์ด๋จธ ๊ต์ฒด ์‹œ ์ด์ „ ํƒ€์ด๋จธ๊ฐ€ ํ„ฐ๋ฏธ๋„ ๋’ค์— ๋ฐœํ–‰ํ•˜์ง€ ์•Š์Œ; 4ยท5ํšŒ์ฐจ: ๋™์‹œ `start_timer` ๋Š” ์ถ”์ ๋˜๋Š” ์†Œ์œ ์ž ํ•˜๋‚˜๋งŒ ๋‚จ๊น€, barrier ๋’ค start ๋Š” arm ์•ˆ ํ•จ, idle ํƒ€์ด๋จธ์˜ ์ •์ƒ ์ข…๋ฃŒ(drain ๊ธฐํ•œ ์ „ยทSIGKILL ์—†์Œ), spawn ์ „ยทํ›„ ์‹คํŒจ ์‹œ ์ถ”์ ๋˜์ง€ ์•Š๋Š” ํƒ€์ด๋จธ ์—†์Œ. - **๊ธฐ์กด ํ…Œ์ŠคํŠธ ์กฐ์ •** (`tests/test_issue14_delivery_ack.py`): busy ํ† ํฐยทpane ๋ณ€ํ™” ํ™•์ธ ํ…Œ์ŠคํŠธ๋ฅผ `started` ๊ธฐ๋ฐ˜์œผ๋กœ ๋’ค์ง‘๊ณ (์ˆ˜์šฉ ๊ธฐ์ค€ 6), R1 ์œ ์˜ˆ์˜ pane busy ํ† ํฐ ํ…Œ์ŠคํŠธ๋ฅผ "pane ์€ ์ƒ์กด ์ฆ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋‹ค"๋กœ ์ „ํ™˜. registry CLI ์Šค๋ƒ…์ƒทยทhelp ์บก์ฒ˜(publish_event/job_subscriber)๋ฅผ ์˜๋„๋œ ํ™•์žฅ์œผ๋กœ ๊ฐฑ์‹ . ### ๐Ÿš€ `v4.4.5` โ€” Issue #19 ๊ตฌ๋… ๋ฐ๋ชฌ ์ˆ˜๋ช… ๊ณ„์•ฝ: ์ƒŒ๋“œ๋ฐ•์Šค ์˜ตํŠธ์•„์›ƒยทํšŒ์ˆ˜, ์›Œํฌ์ŠคํŽ˜์ด์Šค ์†Œ์‹ค ์‹œ ์ž๊ธฐ ์ข…๋ฃŒ, SIGTERM ์ „๋‹ฌ (2026-09-13) > **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: `create_session.sh` ๊ฐ€ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋งˆ๋‹ค ๋ถ„๋ฆฌ ์„ธ์…˜์œผ๋กœ ๋„์šฐ๋Š” ๊ตฌ๋… ๋ฐ๋ชฌ(`reconcile.sh --subscribe --idle-timeout 0`)์ด ํ…Œ์ŠคํŠธ ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ํšŒ์ˆ˜๋˜์ง€ ์•Š์•„ ์Šค์œ„ํŠธ 1ํšŒ๋‹น ์•ฝ 30๊ฐœ์”ฉ ์Œ“์ด๋˜ ๋ฌธ์ œ(Issue #19, ์‹ค์ธก ๊ณ ์•„ ํ”„๋กœ์„ธ์Šค 202๊ฐœ)๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค. ๋ฐ๋ชฌ์€ ์ด์ œ ์ž๊ธฐ ์›Œํฌ์ŠคํŽ˜์ด์Šค๊ฐ€ ์‚ฌ๋ผ์ง€๋ฉด ์Šค์Šค๋กœ ๋๋‚˜๊ณ  `SIGTERM` ์— ์ฆ‰์‹œ ์‘๋‹ตํ•˜๋ฉฐ, ์ƒŒ๋“œ๋ฐ•์Šค๋Š” ๋ฐ๋ชฌ์„ ๋„์šฐ์ง€ ์•Š๊ณ  ๋„์›Œ์ง„ ๊ฒƒ์€ ํšŒ์ˆ˜ํ•œ๋‹ค. ๊ณต๊ฐœ ๊ณ„์•ฝยท์ข…๋ฃŒ ์ฝ”๋“œ ๋ณ€๊ฒฝ์€ ์—†๋‹ค(์˜ตํŠธ์•„์›ƒ ํ™˜๊ฒฝ๋ณ€์ˆ˜ 1๊ฐœ ์ถ”๊ฐ€). #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **`MAM_NO_SUBSCRIBER_DAEMON=1` ์˜ตํŠธ์•„์›ƒ (`lib_py/delegation/daemon.py`, `lib.sh: _start_watchdog_legacy`)**: ๋ฆฌํ„ฐ๋Ÿด `1` ์ผ ๋•Œ๋งŒ ๋ฐ๋ชฌ์„ ์ƒˆ๋กœ ๋„์šฐ์ง€ ์•Š๋Š”๋‹ค. ์ด๋ฏธ ๋„๋Š” ๋ฐ๋ชฌ์˜ PID ๋Š” ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๋ฏ€๋กœ `start_watchdog`/`ensure_subscriber_daemon` ์˜ dedup ๊ณ„์•ฝ์€ ์œ ์ง€๋˜๊ณ , ๋ฏธ๊ธฐ๋™ ์‹œ stderr ์— ํ•œ ์ค„ ๋‚จ๊ธด๋‹ค(stdout ์€ ๋น„์›€). ํ…Œ์ŠคํŠธ ์ƒŒ๋“œ๋ฐ•์Šค(`mam_sandbox`)๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์ผœ๊ณ , ๋ฐ๋ชฌ ์ž์ฒด๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ๋งŒ `0`/์‚ญ์ œ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค. 2. **๋ฐ๋ชฌ ์ž๊ธฐ ์ข…๋ฃŒ (`reconcile.sh --subscribe`)**: MQTT ๋ฃจํ”„๋Š” 5์ดˆ ์ฃผ๊ธฐ wake ๋งˆ๋‹ค ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋””๋ ‰ํ„ฐ๋ฆฌ ์กด์žฌ์™€ `.mam/monitor.lock` ์˜ `(st_dev, st_ino)` ๊ฐ€ ์ž์‹ ์ด ์žก์€ ๊ฒƒ๊ณผ ๊ฐ™์€์ง€ ํ™•์ธํ•˜๊ณ , ์‚ฌ๋ผ์กŒ๊ฑฐ๋‚˜ ๊ต์ฒด๋์œผ๋ฉด(`.mam/` ์žฌ์ƒ์„ฑ โ†’ ์ƒˆ ๋ฐ๋ชฌ์ด ์ƒˆ lock ์„ ์žก์„ ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ) ์ •๋ฆฌ ํ›„ rc 0 ์œผ๋กœ ๋๋‚œ๋‹ค. ๋ธŒ๋กœ์ปค ๋ถˆ๊ฐ€ ํด๋ง ํด๋ฐฑ์€ ๋งค ํšŒ ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ lock ํŒŒ์ผ์˜ **์กด์žฌ๋งŒ** ํ™•์ธํ•œ๋‹ค(python ์ž์‹์ด ๋๋‚˜๋ฉฐ lock ์„ ๋†“์•˜์œผ๋ฏ€๋กœ ํด๋ฐฑ ์ค‘ ๋‘ ๋ฒˆ์งธ ๋ฐ๋ชฌ์„ ๋ฐฐ์ œํ•˜์ง€๋Š” ๋ชปํ•œ๋‹ค โ€” ์ข…์ „ ๋™์ž‘). ์‚ญ์ œ๋œ pytest ์ž„์‹œ ๋””๋ ‰ํ„ฐ๋ฆฌ๋‚˜ `rm -rf` ๋œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฐ”๋ผ๋ณด๋ฉฐ ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค ๊ตฌ๋…์„ ์˜์›ํžˆ ์œ ์ง€ํ•˜๋˜ ๋ฐ๋ชฌ์ด ์‚ฌ๋ผ์ง„๋‹ค. 3. **`SIGTERM`/`SIGINT` ์ฆ‰์‹œ ์ „๋‹ฌ (`reconcile.sh`)**: python ๊ตฌ๋…์ž๋ฅผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž์‹์œผ๋กœ ๋‘๊ณ  bash ๊ฐ€ `wait` ํ•˜๋„๋ก ๋ฐ”๊ฟ” ์‹œ๊ทธ๋„์ด ๋ฏธ๋ค„์ง€์ง€ ์•Š๋Š”๋‹ค(์ข…์ „์—” ํฌ๊ทธ๋ผ์šด๋“œ ์ž์‹์ด ๋๋‚  ๋•Œ๊นŒ์ง€ bash ๊ฐ€ ์‹œ๊ทธ๋„์„ ๋ณด๋ฅ˜ํ•ด `SIGKILL` ๋งŒ ํ†ตํ–ˆ๋‹ค). ํŠธ๋žฉ์ด ์ž์‹์— `SIGTERM` ์„ ์ „๋‹ฌํ•˜๊ณ  rc 143 ์œผ๋กœ ๋๋‚˜๋ฉฐ(์ฒซ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž์‹ ์ „์˜ ์‹œ๊ทธ๋„ ํฌํ•จ), python ์ชฝ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋ธŒ๋กœ์ปค ์—ฐ๊ฒฐ(`connect`/`loop_start`) ์ „์— ์„ค์น˜๋˜์–ด ํ•ธ๋“œ์…ฐ์ดํฌยทCONNACK ๋Œ€๊ธฐ ์ค‘์ด๋‚˜ ๊ตฌ๋… ์งํ›„์˜ ์‹œ๊ทธ๋„๋„ ์ด๋ฒคํŠธ๋ฅผ ๊นจ์›Œ `loop_stop`/`disconnect` ๋ฅผ ๊ฑฐ์นœ ๋’ค rc 0 ์œผ๋กœ ๋๋‚œ๋‹ค(์‹œ๊ทธ๋„์„ ๋ฐ›์€ ๋’ค์—๋Š” ํด๋ง ํด๋ฐฑ์œผ๋กœ ๊ฐ€์ง€ ์•Š๋Š”๋‹ค). ํด๋ฐฑ ๋ฃจํ”„์˜ ๋Œ€๊ธฐ๋„ `sleep โ€ฆ & wait` ๋กœ ๋ฐ”๊ฟ” ์‹œ๊ทธ๋„์ด ๊ณง๋ฐ”๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค(๋Š๊ธด ๋Œ€๊ธฐ์˜ `sleep` ์ž์‹์€ ๋‚จ์€ ๊ฐ„๊ฒฉ ์•ˆ์— ์Šค์Šค๋กœ ๋๋‚˜๊ณ , ์ด๋ฏธ ๋Œ๊ณ  ์žˆ๋Š” `--once` ์กฐ์ • 1ํšŒ๋Š” ๋๋‚œ ๋’ค ์ฒ˜๋ฆฌ๋œ๋‹ค). 4. **์ƒŒ๋“œ๋ฐ•์Šค ํšŒ์ˆ˜ยทํšŒ๊ท€ ๊ฐ€๋“œ (`tests/conftest.py`)**: `mam_sandbox` finalizer ๊ฐ€ argv ํ˜•ํƒœ(`bash|sh <์ƒŒ๋“œ๋ฐ•์Šค>/โ€ฆ/reconcile.sh --subscribe`)๋กœ ์„ ์ •ํ•œ ๋ฐ๋ชฌ๊ณผ ๊ทธ ์ž์‹์„ `SIGTERM`โ†’`SIGKILL` ๋กœ ํšŒ์ˆ˜ํ•œ๋‹ค(๋ธŒ๋ฆฌํ”„๋ฅผ argv ์— ์‹ค์€ ๋ž˜ํผ๋‚˜ ์ ‘๋‘ ์ถฉ๋Œ ํ˜•์ œ ๋ฃจํŠธ๋Š” ์„ ์ •๋˜์ง€ ์•Š์Œ). `ps -o args=` ๋Š” argv ๋ฅผ ๋”ฐ์˜ดํ‘œ ์—†์ด ๊ณต๋ฐฑ์œผ๋กœ ์ด์–ด ๋ถ™์ด๋ฏ€๋กœ ์Šคํฌ๋ฆฝํŠธ ๊ฒฝ๋กœ๋Š” ๊ณต๋ฐฑ ํ† ํฐ์ด ์•„๋‹ˆ๋ผ ์…ธ ๋‹จ์–ด๋ถ€ํ„ฐ ์ฒซ `/reconcile.sh --subscribe` ๊นŒ์ง€ ํ†ต์งธ๋กœ ์žก๋Š”๋‹ค โ€” ๊ณต๋ฐฑ์ด ๋“  ์ƒŒ๋“œ๋ฐ•์Šคยท`--basetemp` ๊ฒฝ๋กœ์˜ ๋ฐ๋ชฌ๋„ ์„ ์ •๋˜๊ณ , ๊ณต๋ฐฑ์ด ๋“  ํ›„๋ณด๋Š” ์‹ค์ œ ํŒŒ์ผ์ผ ๋•Œ๋งŒ ์ธ์ •ํ•ด ๋’ค์ชฝ ์ธ์ž์— ๊ฐ™์€ ๋‚ฑ๋ง์„ ์‹ค์€ bash ๋ž˜ํผ๋Š” ๋ฐฐ์ œํ•œ๋‹ค. ์„ธ์…˜ ์Šค์ฝ”ํ”„ `subscriber_daemon_leak_guard` ๋Š” ์Šค์œ„ํŠธ ์ข…๋ฃŒ ์‹œ basetemp ์•„๋ž˜ ๋ฐ๋ชฌ์ด ๋‚จ์•„ ์žˆ์œผ๋ฉด ํšŒ์ˆ˜ํ•œ ๋’ค ์‹คํŒจ์‹œํ‚จ๋‹ค(#15 ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๊ฐ€๋“œ์™€ ๊ฐ™์€ ๋ฐฉ์‹). #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ 16๊ฐœ** (`tests/test_issue19_subscriber_daemon_lifecycle.py`): ์ƒŒ๋“œ๋ฐ•์Šค ๊ธฐ๋ณธ๊ฐ’์—์„œ ๋‘ helper ๋ณธ์ฒด ๋ชจ๋‘ ๋ฐ๋ชฌ ๋ฏธ๊ธฐ๋™(2), ์˜ตํŠธ์•„์›ƒ ์ค‘์—๋„ ๊ธฐ์กด ๋ฐ๋ชฌ PID ๋ณด๊ณ , ๋ฆฌํ„ฐ๋Ÿด `1` ๋งŒ ์ธ์ •, `create_session.sh` ์ƒŒ๋“œ๋ฐ•์Šค ์‹คํ–‰ ํ›„ ๋ฐ๋ชฌ ์—†์Œ, ์˜ตํŠธ์•„์›ƒ ํ•ด์ œ ์‹œ ๋ˆ„์ˆ˜ ์žฌํ˜„ + ํšŒ์ˆ˜๊ธฐ ์ œ๊ฑฐ, ํšŒ์ˆ˜๊ธฐ ๋‹จ์œ„ ๊ฒ€์ฆยทargv ์„ ์ • ๊ธฐ์ค€ ๊ฒ€์ฆ, ๊ณต๋ฐฑ์ด ๋“  ๊ฒฝ๋กœ์˜ argv ์„ ์ •ยท์‹ค์ œ ๋ฐ๋ชฌ ํšŒ์ˆ˜(2), ํด๋ง ํด๋ฐฑ์˜ lock ์†Œ์‹ค ์ข…๋ฃŒยท`SIGTERM` ์ฆ‰์‹œ ์ข…๋ฃŒ(2, ๋ธŒ๋กœ์ปค ๋ถˆํ•„์š”), CONNACK ๋Œ€๊ธฐ ์ค‘ `SIGTERM` ์ •์ƒ ์ข…๋ฃŒ(์‘๋‹ต ์—†๋Š” ๋กœ์ปฌ ๋ฆฌ์Šค๋„ˆ, ๋ธŒ๋กœ์ปค ๋ถˆํ•„์š”), ์ฒซ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž์‹ ์ „์˜ ์‹œ๊ทธ๋„์—๋„ `_sub_stop` ํŠธ๋žฉ rc 143(bash 3.2 `set -u`), MQTT ๋ฃจํ”„์˜ lock ๊ต์ฒด ์ข…๋ฃŒยท`SIGTERM` ์ •์ƒ ์ข…๋ฃŒ(2, ๋ธŒ๋กœ์ปค ๋ฏธ๋„๋‹ฌ ์‹œ skip). - **๊ธฐ์กด ํ…Œ์ŠคํŠธ ์กฐ์ •**: `tests/test_issue5_phase1_delegation.py` ์˜ watchdog/daemon ํŠน์„ฑํ™” ํ…Œ์ŠคํŠธ๊ฐ€ `MAM_NO_SUBSCRIBER_DAEMON=0`(๋˜๋Š” delenv)์œผ๋กœ ์˜ตํŠธ์•„์›ƒ์„ ํ•ด์ œ. - **์‹ค์ธก**: ์ „์ฒด ์Šค์œ„ํŠธ(870 ํ†ต๊ณผ) ํ›„ pytest basetemp ์•„๋ž˜ `bash โ€ฆ/reconcile.sh --subscribe` ๋ฐ๋ชฌ 0๊ฑด(argv ํ˜•ํƒœ ๊ธฐ์ค€; raw `pgrep -f` ๋Š” ๋ธŒ๋ฆฌํ”„๋ฅผ ์‹ค์€ ๋ฃจํ”„ ๋“œ๋ผ์ด๋ฒ„ยท๋ž˜ํผ๋„ ๋งค์น˜ํ•˜๋ฏ€๋กœ ํŒ์ •์— ์“ฐ์ง€ ์•Š์Œ). ### ๐Ÿš€ `v4.4.4` โ€” Issue #12 ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ œ๊ฑฐ, Issue #8 ์—…๋ฐ์ดํŠธ ์Šคํƒฌํ”„ ๋ณด์กด, Issue #7 ํ˜•์ œ ์›Œํฌ์ŠคํŽ˜์ด์Šค ํŒจํ‚น (2026-09-13) > **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: ๋ชจ๋“  ์„œ๋ธŒ์—์ด์ „ํŠธ๊ฐ€ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์žก ๋ ˆ์ฝ”๋“œ์— MQTT ๋ธŒ๋กœ์ปค ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‰๋ฌธ์œผ๋กœ ๋ณต์‚ฌ๋˜๋˜ ๋ฌธ์ œ(Issue #12), `deploy/update.sh` ๊ฐ€ ์ธ์Šคํ†จ๋Ÿฌ๊ฐ€ ์ƒˆ๋กœ ์“ด `version.txt`ยท`asset_hashes.txt` ๋ฅผ ์—…๋ฐ์ดํŠธ ์ „ ๊ฐ’์œผ๋กœ ๋˜๋Œ๋ฆฌ๋˜ ๋ฌธ์ œ(Issue #8), ๊ฐ™์€ cwd ์˜ ์ฒซ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋งŒ ๊ฒ€์‚ฌํ•ด ๋‹ค์„ฏ ๋ฒˆ์งธ ์—์ด์ „ํŠธ๋ถ€ํ„ฐ ์—์ด์ „ํŠธ๋งˆ๋‹ค ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ์ƒˆ๋กœ ์—ด๋˜ ๋ฌธ์ œ(Issue #7)๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค. ์ƒˆ `registry.py scrub-secrets` ๋Š” ์ถ”๊ฐ€ ์„œ๋ธŒ์ปค๋งจ๋“œ์ด๋ฉฐ ๊ณต๊ฐœ ๊ณ„์•ฝยท์ข…๋ฃŒ ์ฝ”๋“œ ๋ณ€๊ฒฝ์€ ์—†๋‹ค. ๋ธŒ๋กœ์ปค ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ด์ œ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ํ™˜๊ฒฝ(`MQTT_PASSWORD`, `.mam.env`)์—์„œ๋งŒ ์˜จ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **์žก ๋ ˆ์ฝ”๋“œ์˜ ๋ธŒ๋กœ์ปค ๋น„๋ฐ€๋ฒˆํ˜ธ ์ œ๊ฑฐ (Issue #12, `mqtt_common.BrokerConfig.to_registry_block`, `registry.py`)**: `to_registry_block` ์ด ๋” ์ด์ƒ `password` ๋ฅผ ๋ ˆ์ฝ”๋“œ์˜ `broker` ๋ธ”๋ก์— ๋„ฃ์ง€ ์•Š๋Š”๋‹ค. `broker_config_from_job` ์€ ๋ ˆ์ฝ”๋“œ ๋ธ”๋ก์„ ํ™˜๊ฒฝ ์„ค์ • ์œ„์— ๊ฒน์น˜๋ฏ€๋กœ ์ ‘์† ์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” `MQTT_PASSWORD`(`.mam.env`)์—์„œ ์ฑ„์›Œ์ง€๊ณ , `publish_event` ๋Š” ์—ฌ์ „ํžˆ ๋ ˆ์ฝ”๋“œ๋งŒ์œผ๋กœ ์ ‘์†ํ•œ๋‹ค. `_atomic_write_record` ๋Š” ์“ฐ๋Š” ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ์—์„œ `broker.password` ๋งŒ ์ง€์šฐ๋ฏ€๋กœ(๋‹ค๋ฅธ ํ•„๋“œ๋Š” ๊ทธ๋Œ€๋กœ) ์ด์ „ ๊ทœ์น™์œผ๋กœ ์“ฐ์ธ ๋ ˆ์ฝ”๋“œ๋Š” ๋‹ค์Œ ์“ฐ๊ธฐ ๋•Œ ์ •๋ฆฌ๋œ๋‹ค. `auth_token`ยท`dispatch_token` ์€ ์—์ด์ „ํŠธ ์ž์‹ ์˜ `publish_event` ๊ฐ€ ์„œ๋ช…๊ณผ attempt ๊ณ ์ •์— ์“ฐ๋ฏ€๋กœ ๋ ˆ์ฝ”๋“œ์— ๋‚จ๊ธด๋‹ค. 2. **`registry.py scrub-secrets [--dry-run]` ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ (Issue #12)**: ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ lock ์•„๋ž˜์—์„œ **๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ’์ด ์‹ค์ œ๋กœ ์ €์žฅ๋œ** ๋ ˆ์ฝ”๋“œ๋งŒ ๊ณจ๋ผ ์ง€์šฐ๊ณ  ์žฌ๊ธฐ๋กํ•œ๋‹ค. ์ด์ „ ๊ทœ์น™์ด ๋ฌด์ธ์ฆ ๋ชจ๋“œ์—์„œ๋„ ๋‚จ๊ธด `"password": null` ํ‚ค๋Š” ๋น„๋ฐ€์ด ์•„๋‹ˆ๋ฏ€๋กœ ๋Œ€์ƒ๋„ ๋ณด๊ณ ๋„ ์•„๋‹ˆ๋ฉฐ, ๊ทธ ๋ ˆ์ฝ”๋“œ์˜ ๋‹ค์Œ ์“ฐ๊ธฐ ๋•Œ `_atomic_write_record` ๊ฐ€ ์ •๋ฆฌํ•œ๋‹ค. `--dry-run` ์€ ๋Œ€์ƒ ์žก ๋ชฉ๋ก๋งŒ ์ถœ๋ ฅํ•˜๊ณ  ์•„๋ฌด๊ฒƒ๋„ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค. `registry.md` ๊ฐ€ ๋ธŒ๋กœ์ปค ๋ธ”๋ก ๊ทœ์น™(๋น„๋ฐ€๋ฒˆํ˜ธ ๋น„์ €์žฅ, ํ™˜๊ฒฝ์—์„œ ๋ณต์›, ํ† ํฐ ์œ ์ง€ ์ด์œ )์„ ๋ฌธ์„œํ™”ํ•œ๋‹ค. 3. **์—…๋ฐ์ดํŠธ ํ›„ ์„ค์น˜ ์Šคํƒฌํ”„ ๋ณด์กด (Issue #8, `deploy/update.sh`)**: ์„ฑ๊ณต ๊ฒฝ๋กœ๊ฐ€ ์—…๋ฐ์ดํŠธ ์ „ `asset_hashes.txt`ยท`version.txt` ๋ฅผ ๋˜๋Œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. ๊ทธ ๋ณต์› ํƒ“์— `version.txt` ๊ฐ€ ์ด์ „ ์ปค๋ฐ‹์„ ๊ฐ€๋ฆฌํ‚ค๊ณ  `remove.sh` ๊ฐ€ ๊ฐฑ์‹ ๋œ ์›๋ณธ ํŒŒ์ผ์„ "๋กœ์ปฌ ์ˆ˜์ •"์œผ๋กœ ์˜คํŒํ•ด ๋ฐฑ์—…ํ•˜๋˜ ๋ฌธ์ œ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค. `install_state` ๋Š” ์žฌ์„ค์น˜๊ฐ€ ์“ฐ์ง€ ์•Š์€ ํ‚ค(์˜ˆ: `gitignore_created`)๋งŒ `key=value` ์ค„ ๋‹จ์œ„๋กœ ๋ณ‘ํ•ฉํ•˜๋ฉฐ(์ด๋ฏธ ์žˆ๋Š” ํ‚ค๋Š” ๊ฑด๋„ˆ๋œ€, ๋งˆ์ง€๋ง‰ ์ค„์— ๊ฐœํ–‰์ด ์—†์–ด๋„ ์ฒ˜๋ฆฌ), ์‹คํŒจ ๊ฒฝ๋กœ(`restore_on_failure`)๋Š” ์ข…์ „๋Œ€๋กœ ์„ธ ํŒŒ์ผ์„ ๋ชจ๋‘ ๊ทธ๋Œ€๋กœ ๋ณต์›ํ•œ๋‹ค. 4. **๊ฐ™์€ cwd ํ˜•์ œ ์›Œํฌ์ŠคํŽ˜์ด์Šค ํŒจํ‚น (Issue #7, `lib.sh` ์‰ผ `new-session`)**: ์ฒซ ๋ฒˆ์งธ๋กœ ์ผ์น˜ํ•œ pane ์˜ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋งŒ ๋ณด๋˜ ํƒ“์— ๊ฐ€๋“ ์ฐฌ ์ฒซ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ๋งค๋ฒˆ overflow ๋กœ ํŒ์ •ํ•ด ์—์ด์ „ํŠธ๋งˆ๋‹ค ์ƒˆ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ์—ด๋˜ ๋ฌธ์ œ(4 + 1 + 1)๋ฅผ ๊ณ ์ณค๋‹ค. ์ด์ œ ๊ฐ™์€ cwd ์˜ ๋ชจ๋“  ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ pane ์ˆœ์„œ๋Œ€๋กœ ์ˆœํšŒํ•ด idle pane ์ด ์žˆ๊ฑฐ๋‚˜ 2xK ๊ทธ๋ฆฌ๋“œ์— ์—ฌ์œ ๊ฐ€ ์žˆ๋Š” ์ฒซ ์›Œํฌ์ŠคํŽ˜์ด์Šค์— ๋ฐฐ์น˜ํ•˜๊ณ (overflow ์ธ ํ›„๋ณด๋Š” ๋‹ค์Œ ํ›„๋ณด๋กœ ๋„˜์–ด๊ฐ), ๋ชจ๋‘ ๊ฐ€๋“ ์ฐผ์„ ๋•Œ๋งŒ ์ƒˆ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ ๋‹ค(4 + 2). #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ 8๊ฐœ**: `tests/test_issue7_workspace_packing.py` 3๊ฐœ(์—ฌ์œ  ์žˆ๋Š” ํ˜•์ œ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋กœ ํŒจํ‚น, ๋ชจ๋“  ํ›„๋ณด๊ฐ€ ๊ฐ€๋“ ์ฐผ์„ ๋•Œ๋งŒ ์ƒˆ ์›Œํฌ์ŠคํŽ˜์ด์Šค, ์—ฌ์œ  ์žˆ๋Š” ์ฒซ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์šฐ์„  โ€” mock herdr ์— ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ณ„ 2x2 ๊ธฐํ•˜๋ฅผ ํ‰๋‚ด ๋‚ด๋Š” opt-in `layout_geometry: "2x2"` ์ถ”๊ฐ€), `tests/test_issue8_update_state.py` 1๊ฐœ(์‹ค์ œ installโ†’update ์‚ฌ์ดํด์—์„œ ์ƒˆ `version.txt`ยทํ•ด์‹œ ์œ ์ง€์™€ `install_state` ๋ณ‘ํ•ฉ), `tests/test_issue12_registry_secrets.py` 4๊ฐœ(registry ๋ธ”๋ก์— ๋น„๋ฐ€๋ฒˆํ˜ธ ์—†์Œ, ๋“ฑ๋ก๋œ ๋ ˆ์ฝ”๋“œ์— ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์—†์–ด๋„ ํ™˜๊ฒฝ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ์ ‘์†, ๋ ˆ๊ฑฐ์‹œ ๋ ˆ์ฝ”๋“œ์˜ ๋‹ค์Œ ์“ฐ๊ธฐ ๋•Œ ์ •๋ฆฌ, `scrub-secrets` CLI ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜). - **๊ธฐ์กด ํ…Œ์ŠคํŠธ ์กฐ์ •**: registry CLI ๊ณ„์•ฝ ์Šค๋ƒ…์ƒท(`tests/test_issue5_phase1_delegation.py`)๊ณผ ๋„์›€๋ง ์บก์ฒ˜(`tests/fixtures/issue_5/phase1/help-registry.txt`)์— ์˜๋„๋œ `scrub-secrets` ์„œ๋ธŒ์ปค๋งจ๋“œ๋ฅผ ๋ฐ˜์˜ํ•˜๊ณ , G6 ๊ฐ€๋“œ์— `_cmd_scrub_secrets` ๋ฅผ ์„ ์–ธ๋œ ์˜ˆ์™ธ๋กœ ์ถ”๊ฐ€. - **์‹ค์ธก (์ปค๋ฐ‹ `e629cc1`ยท์œ„์ž„ ๋ธŒ๋ฆฌํ”„ ๊ธฐ๋ก)**: ๊ด€๋ จ ์Šค์œ„ํŠธ 325๊ฐœ์™€ ์ „์ฒด ์Šค์œ„ํŠธ(worktree ๊ธฐ์ค€) ํ†ต๊ณผ. ### ๐Ÿš€ `v4.4.3` โ€” Issue #10ยท#13 ๋Œ€ํ™” purge ์™„๊ฒฐ, Issue #11 ๋ฉ”์‹œ์ง€ ์ •์ •, Issue #15 ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๊ณ ์ • (2026-09-13) > **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: agy ๋Œ€ํ™” purge ๊ฐ€ resume ๊ฐ€๋Šฅํ•œ ๋ถ€์† ํŒŒ์ผ์„ ๋‚จ๊ธฐ๋˜ ๋ฌธ์ œ(Issue #10), herdr ์„ธ์…˜ ํ–‰์ด ์—†๋Š” ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ๋Œ€ํ™”๋ฅผ ์–ด๋–ค ์Šคํ‚ฌ๋กœ๋„ ์ง€์šธ ์ˆ˜ ์—†๋˜ ๋ฌธ์ œ(Issue #13), `orc_onboard.sh --remove` ์˜ "removeed" ์˜คํƒ€(Issue #11), ํ˜ธ์ถœ์ž cwd ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋˜ ์žก ๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท๊ฐ์‚ฌ ๋กœ๊ทธ ๋•Œ๋ฌธ์— pytest ์Šค์œ„ํŠธ๊ฐ€ ์‹ค์ œ `.mam/jobs` ์— ํ…Œ์ŠคํŠธ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋‚จ๊ธฐ๋˜ ๋ฌธ์ œ(Issue #15)๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค. ์ƒˆ ์„œ๋ธŒ์ปค๋งจ๋“œยท์˜ต์…˜์€ ๋ชจ๋‘ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์ด๋‹ค. ๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท๊ฐ์‚ฌ ๋กœ๊ทธ์˜ **๊ธฐ๋ณธ** ๊ฒฝ๋กœ๋Š” `WORKSPACE_ROOT` ๊ฐ€ ์žˆ์œผ๋ฉด cwd ๋Œ€์‹  ๊ทธ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ๋”ฐ๋ฅด๋ฉฐ, ๋ช…์‹œ์  `--registry-dir` ์™€ `MAM_JOBS_DIR`/`DELEGATE_JOB_LOGS_DIR` ๋Š” ๊ทธ๋Œ€๋กœ ์šฐ์„ ํ•œ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **agy purge ๋ถ€์† ํŒŒ์ผ ์™„๊ฒฐ (Issue #10, `lib_py/agents/adapters/agy.py`)**: `purge_artifacts` ๊ฐ€ ๋Œ€ํ™” DBยท`brain/` ์— ๋”ํ•ด SQLite ๋ถ€์† ํŒŒ์ผ(`-wal`/`-shm`/`-journal` โ€” DB ๋ฅผ ์—ด ๋•Œ๋งˆ๋‹ค ์ƒ๊ธฐ๊ณ  stop ๊ฒฝ๋กœ์˜ ID ์บก์ฒ˜๋„ ์ด๋ฅผ ๋งŒ๋“ค๋ฉฐ, ์ฒดํฌํฌ์ธํŠธ๋˜์ง€ ์•Š์€ WAL ์—๋Š” ๋Œ€ํ™” ๋‚ด์šฉ์ด ๋‚จ์„ ์ˆ˜ ์žˆ๋‹ค), ์ œ๋ชฉ annotation(`annotations/.pbtxt`), presence lock(`presence/.lock`)๊นŒ์ง€ ์ง€์šด๋‹ค. presence lock ์€ `conversation_in_use` ๊ฐ€ ๊ฑฐ์ง“์ผ ๋•Œ๋งŒ ์ง€์šฐ๊ณ , ์‚ฌ์šฉ ์ค‘์ด๋ฉด ๋‚จ๊ธด ์ฑ„ ๊ฒฝ๊ณ ํ•œ๋‹ค. agy ์˜ `conversation_in_use` ๋Š” DB ๋งŒ์ด ์•„๋‹ˆ๋ผ ๋ถ€์† ํŒŒ์ผยทannotationยทpresence lock ์ „๋ถ€๋ฅผ `lsof` ์ ์œ  ๊ฒ€์‚ฌ์— ๋„˜๊ธฐ๋ฏ€๋กœ, uuid ๋ฅผ ์–ด๋””์—๋„ ์ ์ง€ ์•Š์€ ์ฑ„ lock ๋งŒ ์—ด์–ด ๋‘” ๋ณด์œ ์ž๋„ ์žก๋Š”๋‹ค. DB ๊ฐ€ ์ด๋ฏธ ์‚ฌ๋ผ์ง€๊ณ  WALยทannotationยทlock ๋งŒ ๋‚จ์€ ๊ณ ์•„ ์ƒํƒœ๋„ `has_artifacts` ๊ฐ€ ์ธ์‹ํ•ด purge ๋Œ€์ƒ์ด ๋œ๋‹ค. ์ƒˆ `residual_artifacts()` ๋Š” `~/.gemini/antigravity-cli` ์•„๋ž˜์— ๊ทธ uuid ๋ฅผ ์ด๋ฆ„์— ๊ฐ€์ง„ ๊ฒฝ๋กœ๊ฐ€ ๋‚จ์•„ ์žˆ์œผ๋ฉด ๋ณด๊ณ ํ•œ๋‹ค. db+brain ๋งŒ ์žˆ๋Š” ๊ฒฝ์šฐ 2๊ฑด์„ ๋Œ๋ ค์ฃผ๋Š” ์–ด๋Œ‘ํ„ฐ ๊ณ„์•ฝ์€ ์œ ์ง€. 2. **์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ๋Œ€ํ™” purge CLI (Issue #13, `python -m lib_py.agents purge [workspace]`)**: `orchestrator_uuids` ๋Š” uuid ๋งŒ ์ €์žฅํ•˜๋ฏ€๋กœ ๋””์Šคํฌ์—์„œ ์–ด๋Œ‘ํ„ฐ๋ฅผ ํŒ๋ณ„ํ•œ๋‹ค. `auto` ๋Š” `agy โ†’ claude โ†’ hermes โ†’ grok โ†’ codex โ†’ opencode` ์ˆœ์œผ๋กœ(opencode ๋Š” ๋ชจ๋“  uuid ์— ๋Œ€ํ•ด ์กด์žฌํ•˜๋Š” ๊ณต์œ  DB ๋ผ ๋งˆ์ง€๋ง‰) ๊ฐ ์–ด๋Œ‘ํ„ฐ์˜ `verify_artifact`(revalidate ๋ชจ๋“œ โ€” ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ๋Œ€ํ™”๋Š” ์›Œํฌ์ŠคํŽ˜์ด์Šค ์บ์‹œ์— ์—†์Œ)๋ฅผ ๋จผ์ €, ๊ทธ๋‹ค์Œ uuid ๊ฐ€ ๋“ค์–ด๊ฐ„ artifact ๊ฒฝ๋กœ์˜ ์กด์žฌ๋ฅผ ํ™•์ธํ•œ๋‹ค. ์‚ด์•„ ์žˆ๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ทธ uuid ๋ฅผ ์ฐธ์กฐํ•˜๋ฉด ๊ฑฐ๋ถ€ํ•œ๋‹ค(`base.conversation_in_use`: โ‘  ํ˜ธ์ถœ ํ™˜๊ฒฝ์˜ ์„ธ์…˜ id โ€” `CLAUDE_CODE_SESSION_ID`ยท`ANTIGRAVITY_CONVERSATION_ID`ยท`HERMES_SESSION_ID`ยท`GROK_SESSION_ID`ยท`OPENCODE_SESSION_ID` โ€” ๊ฐ€ ๊ทธ uuid ์ด๋ฉด ์‚ฌ์šฉ ์ค‘, ์ฆ‰ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๊ฐ€ ์ž๊ธฐ ์„ธ์…˜ ์•ˆ์—์„œ ์ž๊ธฐ ๋Œ€ํ™”๋ฅผ purge ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ง‰๋Š”๋‹ค(claude ๋Š” transcript ๋ฅผ ์—ด์–ด ๋‘์ง€ ์•Š์•„ ์•„๋ž˜ ๋‘ ๊ฒ€์‚ฌ๋กœ๋Š” ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค). โ‘ก ํ”„๋กœ์„ธ์Šค ๋ช…๋ น์ค„ ๊ฒ€์‚ฌ์—์„œ purge ์ž์‹ ์˜ ํ—ฌํผ ์‚ฌ์Šฌ(pythonยท์…ธยท์Šคํฌ๋ฆฝํŠธ)๋งŒ ๋นผ๋˜ **๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์—์ด์ „ํŠธ CLI ์กฐ์ƒ์—์„œ ๋ฉˆ์ถ”๊ณ **, ๊ทธ ์กฐ์ƒ์˜ ๋ช…๋ น์ค„์— uuid ๊ฐ€ ์žˆ์œผ๋ฉด ๋ณด์œ ์ž๋กœ ์„ผ๋‹ค. โ‘ข `lsof` ๋กœ artifact ์ ์œ  ํ™•์ธ โ€” ์–ด๋Œ‘ํ„ฐ๊ฐ€ ๋„˜๊ธด ๋ชจ๋“  ๊ฒฝ๋กœ(agy ๋Š” DBยท๋ถ€์† ํŒŒ์ผยทannotationยทpresence lock)). ๊ฒฐ๊ณผ๋Š” JSON ํ•œ ์ค„(`agent`ยท`purged`ยท`residual`ยท`in_use`)์ด๋ฉฐ ์ข…๋ฃŒ ์ฝ”๋“œ๋Š” 0 purge ์™„๋ฃŒ / 4 ์‚ฌ์šฉ ์ค‘์ด๋ผ ๊ฑฐ๋ถ€(์•„๋ฌด๊ฒƒ๋„ ์‚ญ์ œํ•˜์ง€ ์•Š์Œ) / 5 ํ•ด๋‹น uuid ์˜ artifact ์—†์Œ. 3. **`orc_onboard.sh --remove --purge-conversation --yes [--agent ]` (Issue #13)**: ์œ„ purge ๋ฅผ **๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๊ฐฑ์‹  ์ „์—** ์‹คํ–‰ํ•˜๋ฏ€๋กœ ๊ฑฐ๋ถ€๋˜๋ฉด ๋“ฑ๋ก์ด ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค. `--yes` ๊ฐ€ ์—†์œผ๋ฉด ๊ฒฝ๊ณ  ํ›„ exit 4, ๋Œ€ํ™”๊ฐ€ ์‚ฌ์šฉ ์ค‘์ด๋ฉด exit 5(๋“ฑ๋ก ์œ ์ง€), ๋””์Šคํฌ์— ๋Œ€ํ™”๊ฐ€ ์—†์œผ๋ฉด ๊ฒฝ๊ณ  ํ›„ ๋“ฑ๋ก๋งŒ ํ•ด์ œ, `--dry-run` ์€ ์˜ˆ์ • ์ž‘์—…๋งŒ ์ถœ๋ ฅ, `--purge-conversation` ์€ `--remove` ์™€ ํ•จ๊ป˜์ผ ๋•Œ๋งŒ ํ—ˆ์šฉ(exit 2). ์„ฑ๊ณต ์‹œ purge ๋œ ๊ฒฝ๋กœ์™€ ์ž”์—ฌ(residual) ๊ฒ€์‚ฌ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. 4. **`orc_onboard.sh` ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ ๊ณผ๊ฑฐํ˜• (Issue #11)**: `add`/`remove` ๋ฅผ `added`/`removed` ๋กœ ๋ช…์‹œ ํ™œ์šฉํ•ด "Successfully removeed" ๋ฅผ ์—†์•ด๋‹ค. 5. **์žก ๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท๊ฐ์‚ฌ ๋กœ๊ทธ์˜ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๊ณ ์ • (Issue #15, `mqtt_common.default_registry_dir`ยท`get_logs_dir`)**: ๊ธฐ๋ณธ ๊ฒฝ๋กœ๊ฐ€ `MAM_JOBS_DIR`/`DELEGATE_JOB_LOGS_DIR` โ†’ `$WORKSPACE_ROOT/.mam/jobs`ยท`$WORKSPACE_ROOT/.mam/delegate_job_logs` โ†’ cwd ๊ธฐ์ค€ `.mam/โ€ฆ` ์ˆœ์œผ๋กœ ์ •ํ•ด์ง„๋‹ค. `registry.py`ยท`publish_event.py`ยท`service.py` ์™€ `create_session.sh`ยท`stop_session.sh` ๊ฐ€ ๊ฐ™์€ ์ˆœ์„œ๋ฅผ ์“ด๋‹ค. ์ €์žฅ์†Œ ๋ฃจํŠธ์—์„œ ๋ˆ pytest ์Šค์œ„ํŠธ(๊ธฐ๋ณธ ์˜จ๋ณด๋”ฉ ์žก์„ ๋“ฑ๋กํ•˜๋Š” `create_session.sh`)๊ฐ€ ํ˜ธ์ถœ์ž cwd ๋ฅผ ๋”ฐ๋ผ ์‹ค์ œ `.mam/jobs` ์— ํ…Œ์ŠคํŠธ ๋ ˆ์ฝ”๋“œ 593๊ฑด์„ ๋‚จ๊ธฐ๋˜ ๋ฌธ์ œ๋ฅผ ํ•ด์†Œํ–ˆ๊ณ , ์ƒŒ๋“œ๋ฐ•์Šค ํ”ฝ์Šค์ฒ˜(`tests/conftest.py`)๋Š” ๋‘ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘ ๊ณ ์ •ํ•œ๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ 26๊ฐœ**: `tests/test_issue10_13_purge.py` 19๊ฐœ(agy ๋ถ€์† ํŒŒ์ผยทannotationยทlock ์‚ญ์ œ, ์‚ฌ์šฉ ์ค‘ lock ๋ณด์กด, ์กด์žฌํ•œ ๊ฒƒ๋งŒ ๋ณด๊ณ , ์ž๊ธฐ ๊ณ„๋ณด๋ฅผ ๋บ€ in-use ํŒ์ •, ๋น„-์—์ด์ „ํŠธ ํ—ฌํผ ์‚ฌ์Šฌ๋งŒ ์ œ์™ธํ•˜๊ณ  ์—์ด์ „ํŠธ CLI ์กฐ์ƒ์€ ๊ฒ€์‚ฌ, purge CLI ์˜ auto ํŒ๋ณ„ยท์‚ฌ์šฉ ์ค‘ ๊ฑฐ๋ถ€ยท์ž๊ธฐ ์—์ด์ „ํŠธ ์„ธ์…˜ ์•ˆ์—์„œ ๊ฑฐ๋ถ€ยท์„ธ์…˜ id ํ™˜๊ฒฝ๋ณ€์ˆ˜ 5์ข…์œผ๋กœ ๊ฑฐ๋ถ€ยท๋ฏธํ•ด๊ฒฐ uuid, `orc_onboard` purge ์˜ `--yes` ์š”๊ตฌยท์‚ญ์ œ ํ›„ ๋“ฑ๋ก ํ•ด์ œยท์‚ฌ์šฉ ์ค‘ ๊ฑฐ๋ถ€ยท์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ์ž๊ธฐ ์„ธ์…˜์—์„œ ๊ฑฐ๋ถ€ยทartifact ์—†์Œ์—๋„ ๋“ฑ๋ก ํ•ด์ œยท`--remove` ํ•„์ˆ˜ยทdry-run ๋ฌด๋ณ€๊ฒฝ, presence lock ๋งŒ ์—ฐ ๋ณด์œ ์ž ๊ฑฐ๋ถ€, DB ๊ฐ€ ์—†๋Š” ๊ณ ์•„ ๋ถ€์† ํŒŒ์ผ๋กœ agy ํ•ด๊ฒฐยทpurge), `tests/test_issue15_registry_isolation.py` 7๊ฐœ(`MAM_JOBS_DIR` ์šฐ์„ , `WORKSPACE_ROOT` ์ถ”์ข…, cwd ํด๋ฐฑ, ๋กœ๊ทธ ๊ฒฝ๋กœ ๋™์ผ ์ˆœ์„œ, CLI ๊ธฐ๋ณธ๊ฐ’์˜ ํ”„๋กœ์„ธ์Šค๋ณ„ ํ•ด์„, ๋ช…์‹œ env ๋กœ ์˜จ๋ณด๋”ฉ ์žก ์ƒŒ๋“œ๋ฐ•์Šค ์œ ์ง€, `WORKSPACE_ROOT` ๋งŒ์œผ๋กœ๋„ ์ƒŒ๋“œ๋ฐ•์Šค ์œ ์ง€). - **๊ธฐ์กด ํ…Œ์ŠคํŠธ ์กฐ์ •**: O-13(`tests/test_orc_onboard.py`)์ด ์ •์ •๋œ ๋ฉ”์‹œ์ง€๋ฅผ ๋‹จ์ •ํ•˜๊ณ , R1 ํ—ฌํผยทB-9 ๋กœ๊ทธ ๊ฒฝ๋กœ ํ…Œ์ŠคํŠธ์™€ `publish_event` ์Šค๋ƒ…์ƒท(`tests/test_issue5_phase1_delegation.py`ยท`tests/test_tier1_unit.py`)์ด ์›Œํฌ์ŠคํŽ˜์ด์Šค ๊ณ ์ • ๊ณ„์•ฝ์— ๋งž๊ฒŒ ์กฐ์ •๋จ. - **์‹ค์ธก (์ปค๋ฐ‹ `c82ea75`ยท์œ„์ž„ ๋ธŒ๋ฆฌํ”„ ๊ธฐ๋ก)**: ์ „์ฒด ์Šค์œ„ํŠธ 839/839 ํ†ต๊ณผ, ์Šค์œ„ํŠธ ์‹คํ–‰ ์ „ํ›„ ์‹ค์ œ `.mam/jobs` ๋ ˆ์ฝ”๋“œ 881๊ฑด ๋ถˆ๋ณ€. ### ๐Ÿš€ `v4.4.2` โ€” Issue #9 herdr ์„œ๋ฒ„ ํ™˜๊ฒฝ ๊ฒฉ๋ฆฌ ๋ฐ Issue #14 ํ”„๋กฌํ”„ํŠธ ์ „๋‹ฌ ํ™•์ธ ๋ณด๊ฐ• (2026-09-12) > **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: ์—์ด์ „ํŠธ ๋Ÿฐํƒ€์ž„(์˜ˆ: Claude Code ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ) ์•ˆ์—์„œ herdr ์„œ๋ฒ„๊ฐ€ ์ฒ˜์Œ ๊ธฐ๋™๋  ๋•Œ ์„ธ์…˜ ์‹๋ณ„ ๋งˆ์ปค๊ฐ€ ์„œ๋ฒ„์™€ ์ดํ›„ ๋ชจ๋“  ์—์ด์ „ํŠธ pane ์— ์ƒ์†๋˜๋˜ ๋ฌธ์ œ(Issue #9)์™€, `working` ์„ ๋ณด๊ณ ํ•˜์ง€ ์•Š๋Š” ์—์ด์ „ํŠธ(herdr 0.9.0 ์˜ codex)์—๊ฒŒ ์ •์ƒ ์ „๋‹ฌ๋œ ํ”„๋กฌํ”„ํŠธ๋ฅผ `send_keys_safe` ๊ฐ€ ์‹คํŒจ๋กœ ์˜คํŒํ•ด ์žก์„ `agent_blocked` ๋กœ ๋‹ซ๊ณ  ์—์ด์ „ํŠธ์˜ `started`/`completed` ๋ฅผ ๊ฑฐ๋ถ€ํ•˜๋˜ ๋ฌธ์ œ(Issue #14)๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค. ์‹ ๊ทœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋Š” ๋ชจ๋‘ ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋Š” ์ถ”๊ฐ€ ์„ค์ •์ด๋ฉฐ ๊ณต๊ฐœ ๊ณ„์•ฝยท์ข…๋ฃŒ ์ฝ”๋“œ ๋ณ€๊ฒฝ์€ ์—†๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **herdr ์„œ๋ฒ„ ๊ธฐ๋™ env ๋งˆ์ปค denylist (Issue #9, `lib.sh` ์‰ผ)**: ์‰ผ์ด `herdr --session server` ๋ฅผ ๋ถ€ํŠธ์ŠคํŠธ๋žฉํ•  ๋•Œ ์„ธ์…˜ ์‹๋ณ„ ๋งˆ์ปค๋งŒ ์ œ๊ฑฐํ•œ ํ™˜๊ฒฝ์„ ๋„˜๊ธด๋‹ค โ€” `CLAUDECODE`, `CLAUDE_CODE_CHILD_SESSION`, `CLAUDE_CODE_SESSION_ID`, `CLAUDE_CODE_SESSION_ATTENDED`, `CLAUDE_CODE_ENTRYPOINT`, `CLAUDE_PID`, `CLAUDE_CODE_MESSAGING_TOKEN`, `CLAUDE_CODE_MESSAGING_SOCKET`, `CLAUDE_CODE_BRIDGE_SESSION_ID`, `ANTIGRAVITY_CONVERSATION_ID`, `HERMES_SESSION_ID`, `GROK_SESSION_ID`, `OPENCODE_SESSION_ID`. `CLAUDE_PROJECT_DIR`ยท`CLAUDE_CONFIG_DIR` ๋“ฑ ์‚ฌ์šฉ์ž ์„ค์ •์€ ๊ทธ๋Œ€๋กœ ์ƒ์†๋œ๋‹ค. ์ƒ์†๋œ `CLAUDE_CODE_CHILD_SESSION` ๋•Œ๋ฌธ์— ์„œ๋ธŒ์—์ด์ „ํŠธ claude ์˜ transcript ์ €์žฅ์ด ๊บผ์ ธ resume ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋˜ ์ฆ์ƒ์„ ํ•ด์†Œ. 2. **์กฐํšŒ ๊ฒฝ๋กœ ์ž๋™ ๊ธฐ๋™ ๊ธˆ์ง€ `MAM_NO_AUTOSTART=1` (Issue #9)**: ์ด ๊ฐ’์ด ์„ค์ •๋˜๋ฉด ์‰ผ์€ ์„œ๋ฒ„๋ฅผ ๋„์šฐ์ง€ ์•Š๊ณ  ํ˜ธ์ถœ๋งŒ ์ „๋‹ฌํ•œ๋‹ค. `status.sh`ยท`reconcile.sh` ๊ฐ€ ์ด๋ฅผ export ํ•˜๋ฏ€๋กœ read-only `multi-agent-mux-status` ์กฐํšŒ๊ฐ€ ๋” ์ด์ƒ ์„œ๋ฒ„ ๋ฐ๋ชฌ์„ ๊ธฐ๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค. 3. **์‰ผ `ls` ์˜ "no server running" ๋ฒˆ์—ญ (Issue #9)**: ์„œ๋ฒ„๊ฐ€ ๋‚ด๋ ค๊ฐ€ ์žˆ์œผ๋ฉด ์‰ผ์˜ `ls` ๊ฐ€ `no server running (herdr session )` ์œผ๋กœ ๋ณด๊ณ ํ•˜๊ณ , `reconcile.sh` ๋Š” ์ด๋ฅผ ๋นˆ ์„œ๋ฒ„๋กœ ํŒ์ •ํ•ด `herdr_confirmed` ๋ฅผ ์œ ์ง€ํ•œ๋‹ค(ํŒŒ์‹ฑ ์‹คํŒจ๋กœ ๋ชจ๋“  ์„œ๋ฒ„์˜ drift ๊ฐ์ง€๊ฐ€ ๋ฉˆ์ถ”๋Š” ํšŒ๊ท€ ๋ฐฉ์ง€). ๋ฒˆ์—ญ์€ `agent list` ๊ฐ€ **์‹คํŒจ**ํ•˜๊ณ  ๊ทธ ์˜ค๋ฅ˜ ๋ด‰ํˆฌ(stderrยทstdout ์–ด๋А ์ชฝ์ด๋“ )์˜ `error.code` ๊ฐ€ ์ •ํ™•ํžˆ `server_not_running` ์ผ ๋•Œ๋งŒ ์ ์šฉํ•œ๋‹ค. ์„ฑ๊ณตํ•œ ๋ชฉ๋ก์— ๊ฐ™์€ ๋ฌธ์ž์—ด์ด ๋“ค์–ด ์žˆ์œผ๋ฉด(์˜ˆ: ์—์ด์ „ํŠธ ์ด๋ฆ„ `server_not_running-codex`) ๋ฒˆ์—ญํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์‚ด์•„ ์žˆ๋Š” ์„œ๋ฒ„๊ฐ€ ๋นˆ ์„œ๋ฒ„๋กœ ์˜คํŒ๋˜์–ด drift A ๋กœ ์ „ ์„ธ์…˜์ด ์ข…๋ฃŒ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ ๋ฐ–์˜ ์‹คํŒจ๋Š” ์ถœ๋ ฅ์ด ๋ชฉ๋ก ํŒŒ์„œ์— ๋‹ฟ๊ธฐ ์ „์— ๋น„-0 ์œผ๋กœ ๋๋‚˜(`โ€ฆ state unknown`, reconcile ์˜ ๋นˆ ์„œ๋ฒ„ ๋ฌธ๊ตฌ์™€ ๊ฒน์น˜์ง€ ์•Š์Œ) unknown(`herdr_confirmed=False`)์œผ๋กœ ๋‚จ๋Š”๋‹ค โ€” stdout ์œผ๋กœ ์˜จ ์˜ค๋ฅ˜ ๋ฌธ์„œ๋„ ๋” ์ด์ƒ ๋นˆ ๋ชฉ๋ก์œผ๋กœ ์ฝํžˆ์ง€ ์•Š๋Š”๋‹ค. 4. **accepted ์ œ์ถœ์˜ ์ „๋‹ฌ ํ™•์ธ `SKS_DELIVERY_GRACE_S` (Issue #14, `send_keys_safe`)**: `agent prompt --wait` ๊ฒฐ๊ณผ๊ฐ€ `timeout`/`agent_prompt_stalled` ์ด๋ฉด herdr ๊ฐ€ ์ž…๋ ฅ์„ ์ด๋ฏธ ์ˆ˜๋ฝํ•œ ๊ฒƒ์œผ๋กœ ๋ณด๊ณ , `SKS_DELIVERY_GRACE_S`(๊ธฐ๋ณธ 15์ดˆ) ๋™์•ˆ `agent_status` working, ์ œ์ถœ ์ „ ํ™”๋ฉด์— ์—†๋˜ busy ํ† ํฐ(`esc to interrupt`), ์ œ์ถœ ์ „ ์Šค๋ƒ…์ƒท ๋Œ€๋น„ pane ๋ณ€ํ™” ์ค‘ ํ•˜๋‚˜๋ฅผ ์ „๋‹ฌ ํ™•์ธ ์‹ ํ˜ธ๋กœ ์ธ์ •ํ•œ๋‹ค. ์‹ ํ˜ธ๊ฐ€ ์—†์œผ๋ฉด ์ข…์ „๋Œ€๋กœ rc 4 ๋กœ fail-closed ํ•˜๊ณ , ๊ทธ ๋ฐ–์˜ ์˜ค๋ฅ˜(`transport_error` ๋“ฑ)๋Š” accepted ๋กœ ์ทจ๊ธ‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‹จ์ผ ์ฃผ์ž… ๊ณ„์•ฝ(์žฌ์ „์†ก ์—†์Œ)์€ ์œ ์ง€๋œ๋‹ค. 5. **๋ž˜ํผ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ack ์œ ์˜ˆ `MAM_INJECT_ACK_GRACE_S` (Issue #14, `multi-agent-mux-delegate-job`)**: `send_keys_safe` ๊ฐ€ ์ „๋‹ฌ์„ ํ™•์ธํ•˜์ง€ ๋ชปํ•ด๋„ ์žก์„ `agent_blocked` ๋กœ ๋‹ซ๊ธฐ ์ „์— `MAM_INJECT_ACK_GRACE_S`(๊ธฐ๋ณธ 10์ดˆ) ๋™์•ˆ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ๊ด€์ธกํ•˜๊ณ , `pending` ์ด ์•„๋‹Œ ์ƒํƒœ(์—์ด์ „ํŠธ์˜ `started` ๋“ฑ)๊ฐ€ ๋ณด์ด๋ฉด ์ „๋‹ฌ๋œ ๊ฒƒ์œผ๋กœ ๋ณด๊ณ  ์ง„ํ–‰ํ•œ๋‹ค(`_inject_acknowledged`). 6. **R1 ์œ ์˜ˆ์˜ pane busy ํ† ํฐยท๋””์Šคํฌ heartbeat ์ธ์ • (Issue #14, `_agent_still_working`, `_recent_disk_heartbeat`)**: ๊ตฌ๋…์ž idle ํƒ€์ž„์•„์›ƒ ํ›„ R1 ์œ ์˜ˆ๊ฐ€ herdr `working` ์™ธ์— pane ์ตœ๊ทผ 8์ค„์˜ `esc to interrupt` ํ† ํฐ, ๊ทธ๋ฆฌ๊ณ  ์žก `events.log` ์˜ ์ตœ๊ทผ ์ด๋ฒคํŠธ(`publish_event` ๊ฐ€ **์™€์ด์–ด ๋ฐœํ–‰ ์ „์—** ๋กœ์ปฌ์— ๊ธฐ๋กํ•˜๋Š” `progress` heartbeat โ€” ๋ธŒ๋กœ์ปค๊ฐ€ ๋А๋ฆฌ๊ฑฐ๋‚˜ ์ฃฝ์–ด ์žˆ์–ด๋„ ๋ฐœํ–‰ยท์žฌ์‹œ๋„๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ๋””์Šคํฌ์— ๋‚จ๋Š”๋‹ค. CLI `publish_event.py` ์™€ `DelegationService.publish_event` ๋ชจ๋‘ `record_local_event` ๋กœ ๋จผ์ € ๊ธฐ๋กํ•˜๊ณ , ๊ฐ์‚ฌ ๋กœ๊ทธ์˜ ๋ฐœํ–‰ ๊ฒฐ๊ณผ์™€ ์ƒํƒœ ๋™๊ธฐํ™”๋งŒ ๋ฐœํ–‰ ๋’ค์— ๋‚จ๊ธด๋‹ค)๋„ ์ž‘์—… ์ค‘ ๊ทผ๊ฑฐ๋กœ ๋ฐ›์•„๋“ค์ธ๋‹ค. ์œ ์˜ˆ์— ๋“ค์–ด๊ฐ€๋ฉด `IDLE_TIMEOUT` ์ฐฝ ํ•˜๋‚˜๊ฐ€ ์—ด๋ฆฌ๊ณ , ๊ทผ๊ฑฐ๊ฐ€ ๊ด€์ธก๋  ๋•Œ๋งˆ๋‹ค ๊ทธ ์‹œ์ ๋ถ€ํ„ฐ ์ฐฝ์ด ๋‹ค์‹œ ์—ด๋ฆฐ๋‹ค. ๋”ฐ๋ผ์„œ **์•„๋ฌด ๊ทผ๊ฑฐ๋„ ์—†๋Š” ๋ฌด์‘๋‹ต ์—์ด์ „ํŠธ๋Š” ๋งˆ์ง€๋ง‰ ์™€์ด์–ด ์ด๋ฒคํŠธ ํ›„ ์•ฝ 2ร—`IDLE_TIMEOUT` ์— ๋‹ซํžˆ๊ณ **(๋””์Šคํฌ heartbeat ๋ฅผ ์ธ์ •ํ•˜๊ธฐ ์ „์—๋Š” `IDLE_TIMEOUT`; Issue #6 ์ˆ˜์šฉ ๊ธฐ์ค€ "โ‰ค `IDLE_TIMEOUT`+5s ๋‚ด `error`"๋Š” "โ‰ค 2ร—`IDLE_TIMEOUT` + ์—ฌ์œ "๋กœ ๋ฐ”๋€๋‹ค), **heartbeat ๊ฐ„๊ฒฉ์€ ์•ฝ 2ร—`IDLE_TIMEOUT` ๊นŒ์ง€ ํ—ˆ์šฉ๋œ๋‹ค**(heartbeat ํ•˜๋‚˜๊ฐ€ `IDLE_TIMEOUT` ๋งŒํผ ์˜ค๋ž˜๋  ๋•Œ๊นŒ์ง€ ๊ทผ๊ฑฐ๋กœ ์ธ์ •๋˜๋ฏ€๋กœ). ๊ทผ๊ฑฐ๋Š” idle ์ฐฝ๋งŒ ๋Š˜๋ฆด ๋ฟ ๋ฒฝ์‹œ๊ณ„๋Š” ๋Š˜๋ฆฌ์ง€ ์•Š๋Š”๋‹ค: ๋ฃจํ”„๋Š” ๋งค ๋ฐ˜๋ณต ์‹œ๊ณ„๋กœ ๋‹ค์‹œ ์ฝ์€ `START_TIME + TIMEOUT` ์—์„œ ๋๋‚˜๊ณ , herdr ๊ด€์ธก ํ˜ธ์ถœ๋„ ๊ทธ ์‹œ๊ฐ์— ๋Š๋Š”๋‹ค(`_within_deadline`). `broker_unavailable` ๋””์Šคํฌ ์žฌ๊ด€์ธก ๋ฃจํ”„๋„ ๊ฐ™์€ ์‹œ๊ณ„ ๊ธฐ์ค€์ด๋‹ค. ์ด๋กœ์จ idle ํƒ€์ž„์•„์›ƒ๋ณด๋‹ค ๊ธด codex ๋ฆฌ๋ทฐ, heartbeat ๊ฐ„๊ฒฉ์ด ํ•œ ๋ฒˆ idle ์ฐฝ์„ ๋„˜๊ธด ๋ฆฌ๋ทฐ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์…ธ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ค herdr ์— idle ๋กœ ๋ณด์ด๋Š” creator ๊ฐ€ ํ„ด ๋„์ค‘ `deadline_timeout` ์œผ๋กœ ๋‹ซํžˆ๊ณ  ์ดํ›„ `completed` ๊ฐ€ ๊ฑฐ๋ถ€๋˜๋˜ ๋ฌธ์ œ๋ฅผ ๋ง‰๋Š”๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ - **T1-7 ํŠธ๋ฆฌ๊ฑฐ ๋ณ€๊ฒฝ** (`tests/test_issue6_fail_closed.py`): ์ฃผ์ž… ์‹คํŒจ ์œ ๋ฐœ๊ฐ’์„ `prompt_wait_result=timeout` โ†’ `transport_error` ๋กœ ๋ณ€๊ฒฝ(Issue #14 ์ดํ›„ `timeout` ์€ accepted ์ œ์ถœ์ด๋ฏ€๋กœ ๋” ์ด์ƒ ํ™•์ • ์‹คํŒจ๊ฐ€ ์•„๋‹˜)ํ•˜๊ณ  `MAM_INJECT_ACK_GRACE_S=1` ๋กœ ๋Œ€๊ธฐ๋ฅผ ๋‹จ์ถ•. - **์‹ ๊ทœ ํ…Œ์ŠคํŠธ 21๊ฐœ**: `tests/test_issue9_server_env.py` 9๊ฐœ(๋งˆ์ปค ์ œ๊ฑฐ, `MAM_NO_AUTOSTART` ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์ƒ๋žตยทํ˜ธ์ถœ ์ „๋‹ฌ, status ์กฐํšŒ ๋ฌด๊ธฐ๋™, ์ •์  opt-out, `ls` ๋ฒˆ์—ญ(์ตœ์†Œยทherdr 0.9 ์‹ค์ œ ์˜ค๋ฅ˜ ๋ด‰ํˆฌยทstdout ๋ด‰ํˆฌ), ์„ฑ๊ณต ๋ชฉ๋ก ์† `server_not_running` ๋ฌธ์ž์—ด ๋น„์˜คํŒ, ๊ทธ ๋ฐ–์˜ ์‹คํŒจ unknown ์œ ์ง€(stderrยทstdout), reconcile drift A ๋น„๋ฐœ์ƒ, ๋‹ค๋ฅธ ์‹คํŒจ ์‹œ reconcile `herdr_confirmed=False`), `tests/test_issue14_delivery_ack.py` 12๊ฐœ(busy ํ† ํฐยทpane ๋ณ€ํ™” ํ™•์ธ, ๋ฌด์‹ ํ˜ธ fail-closed, ์„ ํ–‰ busy ํ† ํฐ ๋ฐฐ์ œ, `transport_error` ๋น„์ˆ˜์šฉ, mock herdr timeout ํ™•์ธ, ๋ž˜ํผ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ack, idle ํƒ€์ž„์•„์›ƒ pane busy ์œ ์˜ˆ, idle ํƒ€์ž„์•„์›ƒ ๋””์Šคํฌ heartbeat ์œ ์˜ˆ(`publish_event` ์‹ค์ œ ๊ฒฝ๋กœ), heartbeat ๊ฐ€ ์ด์–ด์ ธ๋„ ๋ฒฝ์‹œ๊ณ„ `TIMEOUT` ์•ˆ์—์„œ ์ข…๋ฃŒ, ์™€์ด์–ด ๋ฐœํ–‰ ์ „ ๋กœ์ปฌ ๊ธฐ๋ก, ๋ฐœํ–‰ ์‹คํŒจยท์žฌ์‹œ๋„์—๋„ ๋กœ์ปฌ ๊ธฐ๋ก 1ํšŒ). - **ํ…Œ์ŠคํŠธ ๊ธฐ๋Œ€๊ฐ’ ์ •์ •**: `tests/test_a4_adapter_contract.py` ๊ฐ€ `4d444ce` ์˜ agy `exit_key=/exit` ๋ณ€๊ฒฝ์„ ๋ฐ˜์˜ํ•˜๋„๋ก ์ˆ˜์ •(๊ทธ๋™์•ˆ ์ „์ฒด ์Šค์œ„ํŠธ์—์„œ 1๊ฑด ์‹คํŒจํ•˜๋˜ ๋ฌธ์ œ ํ•ด์†Œ). - **์‹ค์ธก (์ปค๋ฐ‹ `497633f` ๊ธฐ๋ก)**: ์ƒˆ๋กœ ๋ถ€ํŠธ์ŠคํŠธ๋žฉํ•œ ์„œ๋ฒ„์— ๋งˆ์ปค๊ฐ€ ์—†๊ณ  claude pane ์˜ transcript ์ €์žฅ์ด ๋ณต๊ตฌ๋จ, codex ์žก `d04ebcb5` ์˜ `started`/`completed` ๊ฐ€ `agent_blocked` ์—†์ด ์ˆ˜๋ฝ๋จ. ### ๐Ÿš€ `v4.4.1` โ€” Issue #6 ๋น„๋™๊ธฐ ์žก ์œ„์ž„ ์•ˆ์ •ํ™” ๋ฐ Fail-Closed / Turn Handshake ์™„๊ฒฐ (2026-09-08) > **ํŒจ์น˜ ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: Issue #6 ๋น„๋™๊ธฐ ์žก ์œ„์ž„ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ๋ฐœ์ƒํ•˜๋˜ ํ„ฐ๋ฏธ๋„ ์ƒํƒœ ๋ฎ์–ด์“ฐ๊ธฐ ๋ ˆ์ด์Šค, Herdr ํ”„๋กฌํ”„ํŠธ ์™„๋ฃŒ ๊ด€์ธก ๋ถ€์žฌ, Subscriber ํƒ€์ž„์•„์›ƒ ์‹œ pending ๋ฐฉ์น˜ ๋ฒ„๊ทธ๋ฅผ ๊ทผ๋ณธ ํ•ด๊ฒฐ. ๊ฒฐ์ •๋ก ์  ์ข…๋ฃŒ ์ฝ”๋“œ ๊ณ„์•ฝ(0, 1, 2, 3, 5) ๋ฐ 4๋‹จ๊ณ„ fail-closed finalizer๋ฅผ ์™„๋น„ํ–ˆ๋‹ค. #### ๐ŸŒŸ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ 1. **Terminal ์ƒํƒœ ๋‹จ๋ฐฉํ–ฅ ๋ถˆ๋ณ€์„ฑ**: ํ„ฐ๋ฏธ๋„ ์ƒํƒœ(`completed`, `error`, `cancelled`)์— ๋„๋‹ฌํ•œ ์žก์— ๋Œ€ํ•ด ์ดํ›„ ๋„์ฐฉํ•˜๋Š” ์ด๋ฒคํŠธ์˜ ์ž„์˜ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์›์ฒœ ์ฐจ๋‹จ. `registry.py close` ์„œ๋ธŒ์ปค๋งจ๋“œ๋กœ ๋น„์ •์ƒ ์ค‘๋‹จ ์‹œ ๋‹จ๋ฐฉํ–ฅ ์ข…๋ฃŒ ์ฒ˜๋ฆฌ ๋ณด์žฅ. 2. **Herdr ํ„ด ์™„๋ฃŒ ํ•ธ๋“œ์…ฐ์ดํฌ (`lib.sh: wait_for_agent_idle`, `watcher.py`)**: `wait_for_agent_idle`์„ ํ†ตํ•ด ์—์ด์ „ํŠธ ํ”„๋กฌํ”„ํŠธ ์ „์†ก ์ „ํ›„ ์œ ํœด ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ณ , ํ„ด์ด ์™„์ „ํžˆ ์™„๋ฃŒ๋˜์–ด ์ž…๋ ฅ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋ณต๊ท€ํ•  ๋•Œ๊นŒ์ง€ ์‹ ๋ขฐ์„ฑ ์žˆ๊ฒŒ ์ถ”์ . 3. **Loop ๋ชจ๋“œ `attempt_id` ์ „์ด ๊ฒฉ๋ฆฌ**: ๋™์ผ iteration ๋‚ด์—์„œ ์›Œ์ปคโ†’๋ฆฌ๋ทฐ์–ด ์ „ํ™˜ ์‹œ `attempt_id`๋ฅผ ๊ฐฑ์‹ ํ•˜์—ฌ CAS ์žฌ์ „์ด ์ถฉ๋Œ(rc=4)์„ ๊ตฌ์กฐ์ ์œผ๋กœ ๋ฐฉ์ง€. 4. **Subscriber ๋น„์ •์ƒ ์ข…๋ฃŒ ์‹œ Fail-Closed ๋ฐ R1 Working ํšŒ๋ณต๋ ฅ ๋ณด์žฅ**: ๊ตฌ๋…๊ธฐ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํƒ€์ž„์•„์›ƒ๋˜๋”๋ผ๋„ ์—์ด์ „ํŠธ๊ฐ€ Herdr์—์„œ ์—ฌ์ „ํžˆ ์ž‘์—… ์ค‘(`working`)์ด๋ฉด ๋ฒฝ์‹œ๊ณ„ `--timeout`๊นŒ์ง€ ์œ ์˜ˆํ•˜๋ฉฐ ๋””์Šคํฌ ์™„๋ฃŒ ์ƒํƒœ๋ฅผ ์žฌ๊ด€์ธก(R1)ํ•˜๊ณ , ๋ฏธ์ข…๋ฃŒ ์žก์„ ์•ˆ์ „ํ•˜๊ฒŒ `error`๋กœ ์™„๊ฒฐ. 5. **๊ฒฐ์ •๋ก ์  ์ข…๋ฃŒ ์ฝ”๋“œ ๊ณ„์•ฝ**: `multi-agent-mux-delegate-job` ์ข…๋ฃŒ ์ฝ”๋“œ๋ฅผ ์ •๊ทœํ™”(`0`=์„ฑ๊ณต, `1`=์‹คํŒจ/์ฃผ์ž… ์˜ค๋ฅ˜, `2`=๊ตฌ๋…๊ธฐ ํƒ€์ž„์•„์›ƒ, `3`=๋ธŒ๋กœ์ปค ์žฅ์• , `5`=์„ธ์…˜ ์„ ๊ฒฐ ์กฐ๊ฑด ๋ฏธ์ถฉ์กฑ/session_busy). ### ๐Ÿš€ `v4.4.0` โ€” Issue #5 Python ์ฝ”์–ด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์™„๊ฒฐ ๋ฐ `main` ๋ณ‘ํ•ฉ (2026-09-07) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค (MINOR Release)**: Issue #5 Phase 1~4 ์ „ ๋‹จ๊ณ„ ์™„๋ฃŒ ๋ฐ `refactor` โ†’ `main` ๋ณ‘ํ•ฉ. ์œ„์ž„ยท๋ฃจํ”„ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์˜ ๊ตฌํ˜„ ์–ธ์–ด๋ฅผ Bash์—์„œ Python์œผ๋กœ ์˜ฎ๊ธฐ๊ณ , CAS ๋‹จ์ผ ์ƒํƒœ ์ „์ดยท์„€๋„์šฐ ์™€์ฒ˜ยท์ž๋™ ์™„๋ฃŒ ๊ฒŒ์ดํŠธยทEager Import ๋ถˆ๋ณ€ ๋Ÿฐํƒ€์ž„์„ ๋„์ž…ํ–ˆ๋‹ค. **๊ณต๊ฐœ ๊ณ„์•ฝ(์Šคํฌ๋ฆฝํŠธ ๊ฒฝ๋กœ, CLI ์ธ์žยท์ข…๋ฃŒ ์ฝ”๋“œ, ์ด๋ฒคํŠธ ์™€์ด์–ด ์Šคํ‚ค๋งˆ `schema_version=1`, YAML ๋ ˆ์ด์•„์›ƒ)์€ ํ•œ ๊ฑด๋„ ์ œ๊ฑฐยท๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์œผ๋ฉฐ**, ๋ชจ๋“  ๊ธฐ๋ณธ ๋™์ž‘ ๋ณ€๊ฒฝ์€ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ•œ ๊ฐœ๋กœ ์ฆ‰์‹œ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค. #### โš–๏ธ SemVer ํŒ์ • ๊ทผ๊ฑฐ (MINOR โ€” `v4.4.0`, MAJOR `v5.0.0` ๋ฏธ์ฑ„ํƒ) SemVer 2.0.0 ยง8์€ MAJOR๋ฅผ **ํ•˜์œ„ ํ˜ธํ™˜์„ ๊นจ๋Š” ๊ณต๊ฐœ API ๋ณ€๊ฒฝ**์— ํ•œ์ •ํ•œ๋‹ค. ๋ณธ ๋ฆด๋ฆฌ์Šค๋Š” ๊ทœ๋ชจ๋Š” ํฌ์ง€๋งŒ ๊ทธ ์กฐ๊ฑด์„ ์ถฉ์กฑํ•˜์ง€ ์•Š๋Š”๋‹ค. | ํŒ์ • ์ถ• | ๊ด€์ธก ์‚ฌ์‹ค | ๊ทผ๊ฑฐ | | :--- | :--- | :---: | | ๊ณต๊ฐœ ๊ณ„์•ฝ ์ œ๊ฑฐ/์ด๋™ | Issue #5 ์ „ ๊ตฌ๊ฐ„์—์„œ **์‚ญ์ œยทrename ํŒŒ์ผ 0๊ฑด** | `git diff --name-status fc0bd96..HEAD` | | ์™€์ด์–ด ์Šคํ‚ค๋งˆ | `SCHEMA_VERSION = 1` ๋ถˆ๋ณ€ | `mqtt_common.py:135` | | ์Šคํฌ๋ฆฝํŠธ ๋ฆฌํ„ฐ๋Ÿด ๊ฒฝ๋กœ | `scripts/*.py` 4์ข… ์ „๋ถ€ ์กด์† (ํ˜ธํ™˜ shim) | Phase 1 D2 | | CLI ์ธ์žยท์ข…๋ฃŒ ์ฝ”๋“œ | `run_loop.sh`ยท`publish_event`ยท`job_subscriber` ๊ณ„์•ฝ ํšŒ๊ท€ ํ†ต๊ณผ | G-1~G-9 | | ๊ธฐ๋ณธ ๋™์ž‘ ๋ณ€๊ฒฝ์˜ ๊ฐ€์—ญ์„ฑ | `MAM_DELEGATION_PY=0` / `MAM_ENGINE_CAS=0` / `MAM_LOOP_PY=0` / `MAM_LOOP_FREEZE=1` ๋กœ ์ด์ „ ๋™์ž‘ ์™„์ „ ๋ณต์› (๋ ˆ๊ฑฐ์‹œ ๋ณธ์ฒด ์กด์น˜, 968์ค„ ์…ธ ๋ฃจํ”„ ํฌํ•จ) | `test_issue5_phase3_loop.py`, `test_issue5_phase4_runtime.py` | | ์ตœ๊ณ ์œ„ํ—˜ ์‹ ๊ทœ ๊ธฐ๋Šฅ | ์ž๋™ ์™„๋ฃŒ๋Š” **๊ธฐ๋ณธ ๋น„ํ™œ์„ฑ** ์˜ตํŠธ์ธ | `MAM_AUTO_COMPLETE` ๊ธฐ๋ณธ๊ฐ’ `0` | > **์šด์˜์ž ์กฐ์น˜ ํ•„์š” ์‚ฌํ•ญ ์—†์Œ**: ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ ˆ์ฐจ, ์„ค์ • ๋ณ€ํ™˜, ์žฌ๋ฐฐํฌ๊ฐ€ ์š”๊ตฌ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด๊ฒƒ์ด MAJOR๋ฅผ ์ฑ„ํƒํ•˜์ง€ ์•Š์€ ๊ฒฐ์ •์  ์ด์œ ๋‹ค. ๋‹ค๋งŒ `MAM_VERSION` ๋ถˆ์ผ์น˜๋Š” ์ƒˆ๋กœ fail-closed ๊ฑฐ๋ถ€๋˜๋ฏ€๋กœ, **ํ”„๋ ˆ์ž„์›Œํฌ ํŠธ๋ฆฌ๋Š” ๋ถ€๋ถ„ ๊ฐฑ์‹ ํ•˜์ง€ ๋ง๊ณ  ํ†ต์งธ๋กœ ๋ฐฐํฌํ•ด์•ผ ํ•œ๋‹ค**(ยง ์•„๋ž˜ ์ฃผ์˜). #### ๐ŸŒŸ ์‹ ๊ทœ ๊ธฐ๋Šฅ ๋ฐ ์•„ํ‚คํ…์ฒ˜ ํ™•์žฅ * **Phase 1 โ€” `lib_py.delegation` ํŒจํ‚ค์ง€ ์ถ”์ถœ**: `v4.3.0`์—์„œ ์„ ํ–‰ ์™„๋ฃŒ(ํ•˜๋‹จ ํ•ญ๋ชฉ ์ฐธ์กฐ). * **Phase 2a โ€” CAS ๋‹จ์ผ ์ƒํƒœ ์ „์ด ์—”์ง„ ๋ฐ ์„€๋„์šฐ ์™€์ฒ˜**: ์žก ๋ ˆ์ฝ”๋“œ ์ „์ด๋ฅผ `claim`/`redispatch` ์‹œ์ ์— ๋ฐœ๊ธ‰๋˜๋Š” ๋ถˆํˆฌ๋ช… `dispatch_token`์„ ์„ ๊ฒฐ ์กฐ๊ฑด์œผ๋กœ ํ•˜๋Š” compare-and-set์œผ๋กœ ๊ฒฐ์†. ๊ด€์ธก ์ฆ๊ฑฐ ์ˆ˜์ง‘(`attribution.py`)ยทํŒ์ • ์ •์ฑ…(`policy.py`)ยท์ฆ๊ฑฐ ์ €์žฅ์†Œ(`store.py`)๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ , `shadow.py`๊ฐ€ ์‹ค์ œ ์ „์ด ์—†์ด "Would-Complete" ํŒ์ •๋งŒ ๊ธฐ๋กํ•œ๋‹ค. * **Phase 2b โ€” ์ž๋™ ์™„๋ฃŒ ๊ฒŒ์ดํŠธ (`autocomplete.py`)**: ์„€๋„์šฐ ์ฆ๊ฑฐ๊ฐ€ ์ถฉ๋ถ„ํ•  ๋•Œ์—๋งŒ ์žก์„ ์ž๋™ ์ข…๊ฒฐํ•˜๋Š” ํ‰๊ฐ€๊ธฐ. `MAM_AUTO_COMPLETE`(๊ธฐ๋ณธ `0`), `MAM_AUTO_COMPLETE_BACKENDS`๋กœ ๋ฐฑ์—”๋“œ ๋‹จ์œ„ ์˜ตํŠธ์ธ. CAS๊ฐ€ ๋กค๋ฐฑ๋œ ์ƒํƒœ(`MAM_ENGINE_CAS=0`)์—์„œ๋Š” ์Šค์Šค๋กœ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค. * **Phase 2b โ€” ๋‚ด๊ตฌ์„ฑ ์žˆ๋Š” ๋ณด๋ฅ˜ ์•Œ๋ฆผ (`notifier.py`)**: ์•Œ๋ฆผ ์˜๋„๋ฅผ ์žก ๋ ˆ์ฝ”๋“œ์— ์ €์žฅํ•˜๊ณ  CAS ๋ฝ ๋ฐ”๊นฅ์—์„œ ๋ฐœํ–‰ํ•œ ๋’ค `notification_settled` ๋ถ€๊ธฐ ์š”์ฒญ์œผ๋กœ ์กฐ๊ฑด๋ถ€ ์ •์‚ฐ. ์žฌ์‹œ๋„๋Š” ์ƒˆ ์™€์ด์–ด ์‹œํ€€์Šค๋ฅผ ๋ฐ›์œผ๋ฏ€๋กœ ๋‹จ์กฐ ์‹œํ€€์Šค ๊ฐ€๋“œ๋กœ๋Š” ์ค‘๋ณต์„ ์‹๋ณ„ํ•  ์ˆ˜ ์—†๊ณ , ์•ˆ์ •์ ์ธ `(seq, event, timestamp)` ์˜๋„ ์‹ ์›์œผ๋กœ ์ค‘๋ณต์„ ํŒ๋ณ„ํ•œ๋‹ค. * **Phase 3 โ€” `run_loop.sh` ์–‡์€ ๋ž˜ํผ ์ „ํ™˜ ๋ฐ `lib_py.loop` ์Šน๊ฒฉ**: 936์ค„ Bash ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์„ ๋‹จ์ผ ์ œ์–ด ๊ตฌ์กฐ์˜ Python ๋ชจ๋“ˆ๋กœ ์ด์‹. `run_loop.sh`๋Š” CLI ๊ณ„์•ฝ๊ณผ ๊ธฐ์กด ์ •์  ๊ฒ€์‚ฌ ์•ต์ปค๋ฅผ ๋ณด์กดํ•˜๋Š” ๋ž˜ํผ๊ฐ€ ๋˜์—ˆ๊ณ , `MAM_LOOP_PY=0`์œผ๋กœ ๋ ˆ๊ฑฐ์‹œ ์…ธ ๋ฃจํ”„๋ฅผ ๊ทธ๋Œ€๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. * **Phase 3 โ€” Strict ํŒ์ • ํŒŒ์„œ (`parser.py`)**: ๋งˆํฌ๋‹ค์šด ์ฝ”๋“œ ํŽœ์Šค(` ``` `, `~~~`) ๋‚ด๋ถ€๋ฅผ ์ œ๊ฑฐํ•œ ๋’ค **๋งˆ์ง€๋ง‰ ๋น„๊ณต๋ฐฑ ๋ผ์ธ**๋งŒ์„ ํŒ์ •์œผ๋กœ ์ธ์ •ํ•œ๋‹ค. ๋ฆฌํฌํŠธ ๋ณธ๋ฌธ์˜ ์˜ˆ์‹œ ์ฝ”๋“œ์— ์ ํžŒ `[VERDICT: PASS]`๊ฐ€ ์‹ค์ œ ํŒ์ •์œผ๋กœ ์˜ค์ธ๋˜๋˜ ๊ฒฐํ•จ(`baseline.md` Gap)์„ ํ•ด์†Œํ–ˆ๋‹ค. ํŒ์ • ๋ถ€์žฌยท`PASS`/`NOT PASS` ๋ชจํ˜ธ ์ถฉ๋Œยท๋ฏธ์ง€ ํƒœ๊ทธ๋Š” ๋ชจ๋‘ fail-closed `NOT PASS`. * **Phase 3 โ€” ์›์ž์  ์ฒดํฌํฌ์ธํŠธ ๋ฐ ์šฐ์•„ํ•œ ์ •์ง€ (`checkpoint.py`)**: `.mam/loop-checkpoint.json`์— ํšŒ์ฐจยทํ™œ์„ฑ ์žกยท๊ณ„ํšยท๋ฆฌ๋ทฐ ์ƒํƒœยท๊ธฐ์ค€ ์ปค๋ฐ‹์„ ์ž„์‹œ ํŒŒ์ผ + `replace` ์›์ž ์ปค๋ฐ‹์œผ๋กœ ๊ธฐ๋ก. SIGINT/SIGTERM ์ˆ˜์‹  ์‹œ ์„œ๋ธŒํ”„๋กœ์„ธ์Šค๋ฅผ ์ •๋ฆฌํ•˜๊ณ  ์ฒดํฌํฌ์ธํŠธ๋ฅผ ๋‚จ๊ธด ๋’ค ๊ฐ€๋“œ๋ฅผ ํ•ด์ œํ•˜๊ณ  ์ข…๋ฃŒ(130/143). * **Phase 4 โ€” Eager Import ๋ถˆ๋ณ€ ๋Ÿฐํƒ€์ž„ (`eager.py`)**: ๊ธฐ๋™ ์ตœ์šฐ์„  ๋‹จ๊ณ„์—์„œ ํ‘œ์ค€ยทํ”„๋ ˆ์ž„์›Œํฌ ์ฝ”์–ด ๋ชจ๋“ˆ ์ „๋Ÿ‰์„ `sys.modules`์— ์ƒ์ฃผ์‹œ์ผœ, ๋ฃจํ”„ ์‹คํ–‰ ์ค‘ ๋””์Šคํฌ์˜ `.py`๊ฐ€ ํŽธ์ง‘๋˜์–ด๋„ ๋Ÿฐํƒ€์ž„์ด ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค. `PYTHONDONTWRITEBYTECODE=1`๋กœ `.pyc` ํŒŒํŽธ์— ์˜ํ•œ ์ž‘์—… ํŠธ๋ฆฌ ์˜ค์—ผ์„ ์ฐจ๋‹จํ•œ๋‹ค. * **Phase 4 โ€” B-13 Freeze ์Šค๋ƒ…์ƒท ์ œ๊ฑฐ**: Bash๊ฐ€ ์‹คํ–‰ ์ค‘ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋””์Šคํฌ์—์„œ ์žฌ๋…ํ•ดํ•˜๋ฉฐ ๋ฐœ์ƒํ•˜๋˜ ํฌ๋ž˜์‹œ(B-13)์˜ ์›์ธ์ด Python ์ƒ์ฃผ ํ”„๋กœ์„ธ์Šค ์ „ํ™˜์œผ๋กœ ๊ทผ๋ณธ ํ•ด์†Œ๋˜์—ˆ์œผ๋ฏ€๋กœ, ์ž„์‹œ ๋น„๊ณ„์˜€๋˜ `/tmp/mam-loop-freeze.XXXXXX` ํŠธ๋ฆฌ ๋ณต์‚ฌ๋ฅผ ๊ธฐ๋ณธ ๊ฒฝ๋กœ์—์„œ ์ฒ ๊ฑฐํ–ˆ๋‹ค. ํ„ด๋‹น ์ˆ˜๋ฐฑ ๊ฐœ ํŒŒ์ผ ๋ณต์‚ฌ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค. #### ๐Ÿ”’ ๋กค๋ฐฑ ๋ฐ ์•ˆ์ „์žฅ์น˜ * **๋‹จ์ผ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กค๋ฐฑ 4์ข…**: `MAM_DELEGATION_PY=0`(์œ„์ž„ ์…ธ ํ—ฌํผ ๋ ˆ๊ฑฐ์‹œ ๋ณธ์ฒด), `MAM_ENGINE_CAS=0`(๋ฌด์กฐ๊ฑด ๋ณ‘ํ•ฉ ๋ณต์›), `MAM_LOOP_PY=0`(๋ ˆ๊ฑฐ์‹œ ์…ธ ๋ฃจํ”„), `MAM_LOOP_FREEZE=1`(Freeze ์Šค๋ƒ…์ƒท ์žฌํ™œ์„ฑํ™”). ์žฌ๋ฐฐํฌยทrevert ๋ถˆํ•„์š”. * **โš ๏ธ ๋ถ€๋ถ„ ๋ฐฐํฌ ๊ธˆ์ง€ (์‹ ๊ทœ fail-closed ๊ฒŒ์ดํŠธ)**: `lib.sh`์˜ `MAM_VERSION`๊ณผ `lib_py/loop/eager.py`์˜ `FRAMEWORK_VERSION`์ด ๋‹ค๋ฅด๋ฉด `verify_framework_version()`์ด `RuntimeError`๋กœ ๋ฃจํ”„ ๊ธฐ๋™์„ ๊ฑฐ๋ถ€ํ•œ๋‹ค. ๋‘ ํŒŒ์ผ ์ค‘ ํ•˜๋‚˜๋งŒ ๊ฐฑ์‹ ๋œ ํ˜ผ์žฌ ํŠธ๋ฆฌ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ํ•ญ์ƒ ํŠธ๋ฆฌ ๋‹จ์œ„๋กœ ๋ฐฐํฌํ•œ๋‹ค. * **4-Way ๋ฝ์Šคํ… ์ž๋™ ๊ฒ€์ฆ**: `test_version_consistency.py`๊ฐ€ `lib.sh` โ†” `eager.py` โ†” `VERSIONS.md`(ํ˜„์žฌ ๋ฒ„์ „ + ๋งคํŠธ๋ฆญ์Šค 8ํ–‰) โ†” 8๊ฐœ `SKILL.md` frontmatter์˜ ์™„์ „ ์ผ์น˜๋ฅผ ๊ฐ•์ œํ•œ๋‹ค. `eager.py` ์ปค๋ฒ„๋ฆฌ์ง€๋Š” ๋ณธ ๋ฆด๋ฆฌ์Šค์—์„œ ์‹ ๊ทœ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. #### โœ… ํ’ˆ์งˆ ๊ฒ€์ฆ * ์ „์ฒด ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ **750๊ฐœ ํ†ต๊ณผ, 0 ์‹คํŒจ, 0 skip** (Issue #5 ์ฐฉ์ˆ˜ ์‹œ์  510๊ฐœ ๋Œ€๋น„ +240). * Issue #5 ์ „์šฉ ํšŒ๊ท€: `test_issue5_phase{0,1,2a,2b,3,4}_*.py`. * **ํ…Œ์ŠคํŠธ ๊ฒฉ๋ฆฌ ๊ฒฐํ•จ 1๊ฑด ์ˆ˜์ • (`tests/conftest.py`)**: `lib.sh`๊ฐ€ exportํ•œ `MAM_VERSION`์ด pytest๋ฅผ ๊ธฐ๋™ํ•œ ์…ธ์—์„œ ์ƒ์†๋˜์–ด, ๋ฃจํ”„ ํ…Œ์ŠคํŠธ๊ฐ€ *๊ฒ€์‚ฌ ๋Œ€์ƒ ํŠธ๋ฆฌ*๊ฐ€ ์•„๋‹ˆ๋ผ *๋Ÿฌ๋„ˆ๋ฅผ ๋„์šด ์…ธ์ด ์–ธ์ œ `lib.sh`๋ฅผ sourceํ–ˆ๋Š”์ง€*์— ๋”ฐ๋ผ ํ†ต๊ณผยท์‹คํŒจํ•˜๊ณ  ์žˆ์—ˆ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€๋Š” ์ƒ์†๊ฐ’๊ณผ ์ƒ์ˆ˜๊ฐ€ ์šฐ์—ฐํžˆ ๊ฐ™์•„ ์ž ๋ณตํ•ด ์žˆ์—ˆ๊ณ , ๋ณธ ๋ฆด๋ฆฌ์Šค์˜ ๋ฒ„์ „ ์ƒ์Šน์ด ์ด๋ฅผ ๋“œ๋Ÿฌ๋ƒˆ๋‹ค(4๊ฑด ์‹คํŒจ). ์„ธ์…˜ ์Šค์ฝ”ํ”„ fixture๊ฐ€ ์ƒ์†๋œ ๊ฐ’์„ ์ œ๊ฑฐํ•˜์—ฌ ์Šค์œ„ํŠธ๊ฐ€ ํŠธ๋ฆฌ๋ฅผ ์ธก์ •ํ•˜๋„๋ก ๋ฐ”๋กœ์žก์•˜๋‹ค. - ํ”„๋กœ๋•์…˜ ๊ฒฝ๋กœ๋Š” ์˜ํ–ฅ ์—†์Œ: `run_loop.sh:17`์ด ์‹คํ–‰ ์ตœ์ดˆ์— `lib.sh`๋ฅผ sourceํ•˜๊ณ  `lib.sh:32-33`์ด `MAM_VERSION`์„ ๋ฌด์กฐ๊ฑด ์žฌexportํ•˜๋ฏ€๋กœ, ๋‚ก์€ ์ƒ์†๊ฐ’์€ ํŒŒ์ด์ฌ ๊ธฐ๋™ ์ „์— ํ•ญ์ƒ ๋ฎ์–ด์จ์ง„๋‹ค(`MAM_VERSION=4.3.0 bash -c 'source lib.sh; echo $MAM_VERSION'` โ†’ `4.4.0`). ### ๐Ÿš€ `v4.3.0` โ€” `lib_py.delegation` ํŒจํ‚ค์ง€ ์ถ”์ถœ ๋ฐ ์žฅ์ˆ˜ MQTT ์—ฐ๊ฒฐ ํ”„๋กœํ† ํƒ€์ž… (2026-09-06) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค (MINOR Release)**: Issue #5 Phase 1. `lib.sh`์˜ ์œ„์ž„ ํ—ฌํผ 3์ข…๊ณผ `multi-agent-mux-delegate-job/scripts/*.py` 4์ข…์„ `lib_py/delegation/` ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€๋กœ ์ถ”์ถœ. ์Šคํฌ๋ฆฝํŠธ ๊ฒฝ๋กœ๋Š” ์™€์ด์–ด ๊ณ„์•ฝ์ด๋ฏ€๋กœ ํ˜ธํ™˜ shim์œผ๋กœ ๋ณด์กดํ•˜๋ฉฐ, ํ”„๋กœ๋•์…˜ ๋™์ž‘ยท์ด๋ฒคํŠธ ์Šคํ‚ค๋งˆยท์ž๋™์™„๋ฃŒ ์ •์ฑ…์€ ํ•œ ์ค„๋„ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค. #### ๐ŸŒŸ ์‹ ๊ทœ ๊ธฐ๋Šฅ ๋ฐ ์•„ํ‚คํ…์ฒ˜ ํ™•์žฅ * **ํŒจํ‚ค์ง€ ๋ฐ”์ธ๋”ฉ shim (D2)**: `scripts/{mqtt_common,registry,publish_event,job_subscriber}.py`๋Š” `lib_py.delegation` **ํŒจํ‚ค์ง€**๋ฅผ ์ž๊ธฐ ์˜† ํŠธ๋ฆฌ์— ํ”„๋กœ์„ธ์Šค๋‹น 1ํšŒ ๋ฐ”์ธ๋”ฉํ•˜๊ณ  `sys.path`๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. `import mqtt_common`์€ `lib_py.delegation.mqtt_common`๊ณผ ๋™์ผํ•œ ๋ชจ๋“ˆ ๊ฐ์ฒด๊ฐ€ ๋œ๋‹ค. - `sys.path.insert(0, ...)` ๋ฐฉ์‹์€ pytest์—์„œ ์ตœ์ดˆ๋กœ ์ž„ํฌํŠธ๋œ ์ƒŒ๋“œ๋ฐ•์Šค ์‚ฌ๋ณธ์ด `lib_py.agents`/`layout`/`state`๊นŒ์ง€ ์„ธ์…˜ ์ „์ฒด์— ๊ฑธ์ณ ๊ฐ€๋กœ์ฑ„๋ฏ€๋กœ ์ฑ„ํƒํ•˜์ง€ ์•Š์•˜๋‹ค. - ๋ชจ๋“ˆ ๋‹จ์œ„ ํŒŒ์ผ ์ ์žฌ ๋ฐฉ์‹์€ ํ˜•์ œ๋ฅผ ์ž„ํฌํŠธํ•˜๋Š” ๋ชจ๋“ˆ์ด ๋‹ค๋ฅธ ํŠธ๋ฆฌ์˜ ์˜์กด์„ฑ์„ ๋Œ์–ด์™€ freeze(B-13) ๋ณดํ˜ธ๋ฅผ ๋ถ€๋ถ„ ์ƒ์‹คํ•˜๋ฏ€๋กœ ์ฑ„ํƒํ•˜์ง€ ์•Š์•˜๋‹ค. * **`DelegationService` / `cli.py` / `daemon.py`**: ์…ธ ํ—ฌํผ 3์ข…์˜ ๋ณธ์ฒด๋ฅผ `python -m lib_py.delegation.cli`๋กœ ๊ต์ฒด. `start_watchdog`์€ `ensure_subscriber_daemon`์˜ ๋ณ„์นญ์œผ๋กœ ์œ ์ง€. * **`PersistentPublisher` ํ”„๋กœํ† ํƒ€์ž… (`publisher.py`) ๋ฐ ๋ฒค์น˜๋งˆํฌ ํ•˜๋‹ˆ์Šค (`bench.py`)**: ์žฅ์ˆ˜ ์—ฐ๊ฒฐ์€ Phase 2a ์—”์ง„์šฉ์ด๋ฉฐ ํ”„๋กœ๋•์…˜ ๊ฒฝ๋กœ(CLIยท์…ธ)๋Š” ๊ณ„์† 1ํšŒ์„ฑ ์—ฐ๊ฒฐ์„ ์‚ฌ์šฉํ•œ๋‹ค. * **`ensure_env_loaded()` (D6)**: `.mam.env` ๋กœ๋”ฉ์„ ์ž„ํฌํŠธ ์‹œ์ ์—์„œ ํ”„๋กœ์„ธ์Šค ์ง„์ž…์ ์œผ๋กœ ์ด๋™. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž„ํฌํŠธ๊ฐ€ `os.environ`์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค. #### ๐Ÿ”’ ๋กค๋ฐฑ ๋ฐ ์•ˆ์ „์žฅ์น˜ * **`MAM_DELEGATION_PY=0`**: ์…ธ ํ—ฌํผ๊ฐ€ ๋ณด์กด๋œ ๋ ˆ๊ฑฐ์‹œ ์ธ๋ผ์ธ ๋ณธ์ฒด(`_*_legacy`)๋กœ ์ฆ‰์‹œ ๋ถ„๊ธฐ. ์žฌ๋ฐฐํฌยทrevert ๋ถˆํ•„์š”. ๋ ˆ๊ฑฐ์‹œ ๋ณธ์ฒด ์ œ๊ฑฐ๋Š” Phase 4. * **ํŠน์„ฑํ™” ํ…Œ์ŠคํŠธ ์„ ํ–‰ (P1-00)**: ์ด์ „๊นŒ์ง€ ์œ„์ž„ ์…ธ ํ—ฌํผ 3์ข…์„ ์ง์ ‘ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ 0๊ฑด์ด์—ˆ์œผ๋ฏ€๋กœ, ์ด๋™ **์ „์—** ๊ณจ๋“  ํ…Œ์ŠคํŠธ๋ฅผ ๋จผ์ € ์ถ”๊ฐ€ํ•˜๊ณ  ๋‘ ๊ฒฝ๋กœ(`MAM_DELEGATION_PY` 0/1) ๋ชจ๋‘์—์„œ ํ†ต๊ณผ์‹œ์ผฐ๋‹ค. ### ๐Ÿš€ `v4.2.0` โ€” OpenAI Codex First-Class Agent Backend Integration (2026-09-05) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค (MINOR Release)**: OpenAI Codex CLI(`codex`)๋ฅผ 6๋ฒˆ์งธ ์ •์‹ 1๊ธ‰ ์—์ด์ „ํŠธ ๋ฐฑ์—”๋“œ๋กœ ํ†ตํ•ฉ. 46๊ฐœ ์ „์ฒด ์•„ํ‚คํ…์ฒ˜ ๊ฒฐํ•ฉ ์ง€์ (Shell ๋ผ์ดํ”„์‚ฌ์ดํด, Python ์–ด๋Œ‘ํ„ฐ, ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ, ์ƒํƒœ ๋จธ์‹ , ๋™๊ธฐํ™” ๋ฃจํ”„, ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ, ๋ฌธ์„œ) ์ „๋ฉด ์—ฐ๋™ ์™„๋ฃŒ. #### ๐ŸŒŸ ์‹ ๊ทœ ๊ธฐ๋Šฅ ๋ฐ ์•„ํ‚คํ…์ฒ˜ ํ™•์žฅ (New Features & Architecture) * **A-1: Codex Agent Adapter (`CodexAgentAdapter`) ์‹ ๊ทœ ๊ตฌํ˜„ (`codex.py`, `registry.py`)**: - `BaseAgentAdapter` ์™„์ „ ๊ตฌํ˜„: `name='codex'`, `own_key='codex_session_id_own'`, `ready_tokens='Codex|Ask Codex to do anything'`, `exit_key='Ctrl+C'`, `delegate_agent_key='codex-cli'`. - Class A ์—์ด์ „ํŠธ ๋ผ์ดํ”„์‚ฌ์ดํด ์ฑ„ํƒ: ์„ธ์…˜ ์ƒ์„ฑ ์‹œ UUID๋ฅผ ์‚ฌ์ „ ํ• ๋‹นํ•˜์ง€ ์•Š๊ณ  ๋Ÿฐํƒ€์ž„์— ๋™์ ์œผ๋กœ ๋ฐœ๊ฒฌ. - Rev.2 ๋™์  SQLite DB ํƒ์ƒ‰: ๊ณ ์ • ๋ฒ„์ „ ํŒŒ์ผ ๋Œ€์‹  `state_*.sqlite` ํŒจํ„ด์„ ์ตœ์‹  mtime ์ˆœ์œผ๋กœ ํƒ์ƒ‰ํ•˜์—ฌ `threads` ํ…Œ์ด๋ธ”์—์„œ `(rollout_path, cwd)`๋ฅผ O(1)๋กœ ์ƒ‰์ธ. - Fallback ํƒ์ƒ‰: SQLite ๋ฏธ์กด์žฌ ์‹œ `~/.codex/sessions/*/*/*/rollout-*-.jsonl` date-nested ๊ฒฝ๋กœ ๋ฐ ์žฌ๊ท€ ํŒจํ„ด ์ง€์›. - Safe Purge: `codex delete --force` CLI ํ˜ธ์ถœ ๋ฐ ์˜จ๋””์Šคํฌ jsonl/DB ๋ ˆ์ฝ”๋“œ ํด๋ฐฑ ์‚ญ์ œ. * **A-2: Shell ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฐ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ 46๊ฐœ ์ง€์  ์™„์ „ ์—ฐ๋™**: - `lib.sh`: `resolve_agent_type_from_registry`, `_sanitize_herdr_agent_name`, `send_keys_safe`์— codex ๋“ฑ๋ก. - `create_session.sh`: `--agent codex` ํ—ˆ์šฉ, `~/.codex/auth.json` / `OPENAI_API_KEY` ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ ๊ฒ€์ฆ, `--dangerously-bypass-approvals-and-sandbox` ํ”Œ๋ž˜๊ทธ ์Šคํฐ, `codex_session_id_own: null` ์ดˆ๊ธฐํ™”. - `resume_session.sh`: `codex` ๋ณต์› ์ง€์›, 0-turn ์„ธ์…˜์— ๋Œ€ํ•œ `FRESH_SPAWN=1` Class A ์•ˆ์ „ ํด๋ฐฑ. - `update_yaml_resumed.sh`: `codex` ์„ธ์…˜ ๋ณต์› ์‹œ YAML ํ•„๋“œ ์›์ž์  ๊ฐฑ์‹ . - `stop_session.sh`: `codex` ์ง€์›, ์ข…๋ฃŒ ์‹œ ์ตœ์ข… session id ์บก์ฒ˜ ๋ฐ ์ €์žฅ. - `orc_onboard.sh`: flag-tolerant regex ๊ธฐ๋ฐ˜์œผ๋กœ `codex resume [options] ` ๋ช…๋ นํ–‰์—์„œ UUID ์ •ํ™• ์ถ”์ถœ. - `reconcile.sh`: drift C (codex) ์ „์šฉ ๊ฐ์ง€ ๋ธ”๋ก ์ถ”๊ฐ€ (state DB ์šฐ์„  + glob ํด๋ฐฑ), discovery sweep ์—ฐ๋™. - `status.sh`: text/JSON ํฌ๋งทํ„ฐ ์–‘์ธก์— codex ์˜จ๋””์Šคํฌ ๋ณต์› ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ๊ฒ€์‚ฌ ๋กœ์ง ์—ฐ๋™. * **A-3: ๋ฌธ์„œ ๋ฐ ํ‘œ์ค€ ํ—Œ์žฅ ๋™๊ธฐํ™”**: - `README.md`, `README.ko.md`, `BOOTSTRAP.md`, `BOOTSTRAP.ko.md`, `deploy/INSTALL.md`, `.agents/MULTI_AGENT_RULES.md`, `.mam.env.example`, `docs/NEW_AGENT_INTEGRATION_GUIDE.md` ์ „๋ฉด ์—…๋ฐ์ดํŠธ. --- ### ๐Ÿ› ๏ธ `v4.1.3` โ€” Herdr Daemon Isolation & Class A 0-Turn Resume Reliability Fixes (2026-08-31) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: Issue #3 ๊ฒฐํ•จ ํ•ด์†Œ โ€” Herdr ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ฐ๋ชฌ์˜ `start_new_session=True` ๊ธฐ๋ฐ˜ ํ”„๋กœ์„ธ์Šค ๊ทธ๋ฃน(setsid) ์™„์ „ ๊ฒฉ๋ฆฌ, Class A(`agy`, `hermes`, `opencode`) ์—์ด์ „ํŠธ 0-turn ์ค‘์ง€ ์„ธ์…˜์˜ `FRESH_SPAWN` fallback ๋ฐ discovery epoch ์›์ž์  ์žฌ์„ค์ •, Class B(`claude`, `grok`) ๊ณ„์•ฝ ๋ถˆ๋ณ€ ๋ณด์กด, 455๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ 100% PASS ๋‹ฌ์„ฑ. #### ๐Ÿฉน ๋ฒ„๊ทธ ์ˆ˜์ • ๋ฐ ์•ˆ์ •์„ฑ ๊ฐœ์„  (Bug Fixes & Resilience) * **C-1: Herdr ๋ฐ๋ชฌ ํ”„๋กœ์„ธ์Šค ๊ทธ๋ฃน(PGID/SID) ์™„์ „ ๋ถ„๋ฆฌ (`lib.sh`)**: - `.mam/shim/herdr` ํ…œํ”Œ๋ฆฟ์˜ `nohup ... & disown` ์Šคํฐ ๋ฐฉ์‹์„ ํŒŒ์ด์ฌ `subprocess.Popen(..., start_new_session=True)` ๊ธฐ๋ฐ˜ ์ „์šฉ ์Šคํฌ๋„ˆ๋กœ ๋Œ€์ฒดํ•˜์—ฌ ์ž์‹ ํ”„๋กœ์„ธ์Šค์—์„œ `os.setsid()` ํ˜ธ์ถœ ๋ณด์žฅ. - ๋ถ€๋ชจ ์…ธ์ด๋‚˜ ํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆ์˜ ์‹œ๊ทธ๋„ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ(`SIGTERM`/`SIGINT`)๋กœ ์ธํ•ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ Herdr ๋ฐ๋ชฌ์ด ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ์ข…๋ฃŒ๋˜๋Š” ๊ฒฐํ•จ์„ ์›์ฒœ ์ฐจ๋‹จ (`test_h26`). * **C-2: Class A ์—์ด์ „ํŠธ 0-turn resume ์ž์œจ Fallback ๋ฐ Discovery Watermark ๊ฐฑ์‹  (`resume_session.sh`, `update_yaml_resumed.sh`)**: - ๋Œ€ํ™” ํ„ด์ด ๋ฐœ์ƒํ•˜๊ธฐ ์ „์— ์ค‘์ง€๋œ Class A(`agy`, `hermes`, `opencode`) ์„ธ์…˜ resume ์‹œ, UUID ๋ฏธ์กด์žฌ๋กœ ์ธํ•œ ๋น„์ •์ƒ hard-fail(RC=1) ๋Œ€์‹  `FRESH_SPAWN=1`๋กœ ์ž๋™ ์ „ํ™˜ํ•˜์—ฌ `spawn-spec` ๊ธฐ๋ฐ˜์˜ ์ •์ƒ ์žฌ์Šคํฐ ์ˆ˜ํ–‰ (`test_t14`). - fresh-spawn resume ์‹œ `update_yaml_resumed.sh`์—์„œ `herdr_session_epoch`๋ฅผ resume ์‹œ์ ์˜ ํƒ€์ž„์Šคํƒฌํ”„๋กœ ์ฆ‰์‹œ ๊ฐฑ์‹ ํ•˜๊ณ  `session_id_source: pending-discovery`๋กœ ์žฌ์„ค์ •ํ•˜์—ฌ, ์„ธ์…˜ ์ƒ์„ฑ๊ณผ ๋ณต์› ์‚ฌ์ด์— ์ƒ์„ฑ๋œ ๋ฌด๊ด€ํ•œ ์ด์ „ ๋Œ€ํ™” ํŒŒ์ผ์ด `reconcile.sh`์˜ discovery sweep์— ์˜ํ•ด ์˜ค์ธ ์บก์ฒ˜๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ (`test_t16`). - Class B(`claude`, `grok`) ์—์ด์ „ํŠธ์˜ `verify_session.py:99-101` assigned escape hatch ๋ฐ `test_t8` ๊ณ„์•ฝ์€ ์ˆ˜์ • ์—†์ด ์™„๋ฒฝํžˆ ๋ณด์กด (`test_t15`). --- ### ๐Ÿ› ๏ธ `v4.1.2` โ€” Pane Resolution & Reuse Reliability Fixes (2026-08-30) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: `bug-report/mam_upstream_bug_fix_report.md`์— ๋ณด๊ณ ๋œ 4๊ฑด์„ ์‹ค ์ฝ”๋“œ ๋Œ€๋น„ ๋…๋ฆฝ ๊ฒ€์ฆ โ€” 2๊ฑด์€ ์‹ค์žฌํ•˜๋Š” ๊ฒฐํ•จ์œผ๋กœ ํ™•์ธ๋˜์–ด ์ˆ˜์ •(SemVer 2.0.0 ยง6 PATCH), 1๊ฑด(`ready_tokens`)์€ v4.1.1์—์„œ ์ด๋ฏธ ํ•ด๊ฒฐ๋จ์„ ํ™•์ธ, 1๊ฑด(`resume_spec` `materialized` ๊ฒŒ์ดํŠธ)์€ 5๊ฐœ ์–ด๋Œ‘ํ„ฐ ๊ณตํ†ต์˜ ์˜๋„๋œ ์•ˆ์ „์žฅ์น˜๋กœ ํŒ๋‹จ๋˜์–ด ์ œ์•ˆ๋œ ์ˆ˜์ •์„ ์ฑ„ํƒํ•˜์ง€ ์•Š์Œ(๊ทผ๊ฑฐ๋Š” ์•„๋ž˜ ์ฐธ์กฐ). #### ๐Ÿฉน ๋ฒ„๊ทธ ์ˆ˜์ • ๋ฐ ์•ˆ์ •์„ฑ ๊ฐœ์„  (Bug Fixes & Resilience) * **C-1: `_resolve_herdr_pane_id` Generic `agent_kind` ์˜ค์ธ ๋งค์นญ ๋ฐฉ์ง€ (`lib.sh`)**: - ์„ธ์…˜๋ช… ๋งค์นญ ์‹คํŒจ ์‹œ ์ˆ˜ํ–‰๋˜๋˜ `agent_kind` ๊ด‘์—ญ ํด๋ฐฑ์„ ํ˜ธ์ถœ ๋Œ€์ƒ ์ž์ฒด๊ฐ€ kind ๋ฆฌํ„ฐ๋Ÿด(์˜ˆ: `"agy"`)์ธ ๊ฒฝ์šฐ๋กœ๋งŒ ํ•œ์ •ํ•˜๊ณ , ์ƒ์œ„ ๋ฃจํ”„์˜ exact match ๋Œ€์ƒ์—์„œ `'agent'`๋ฅผ ์ œ์™ธํ•˜์—ฌ ๋™์ข… ํŽ˜์ธ ๋‹ค์ˆ˜ ์กด์žฌ ์‹œ R-1 fail-closed ๊ณ„์•ฝ์ด ์˜จ์ „ํžˆ ์ ์šฉ๋˜๋„๋ก ๋ณด์žฅ. ์ด์ „์—๋Š” ๋Œ€์ƒ ์„ธ์…˜์˜ yaml ํ•ญ๋ชฉ์—์„œ ์กฐํšŒํ•œ kind๋กœ "๋™์ข… ํŽ˜์ธ ์•„๋ฌด๊ฑฐ๋‚˜" ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ด, ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋‚ด ๋ฌด๊ด€ํ•œ ๋™์ข… ์—์ด์ „ํŠธ(์˜ˆ: ๋ณ„๋„ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ `agy` ์„ธ์…˜)๊ฐ€ ๋‹จ 1๊ฐœ๋ผ๋„ ์‚ด์•„์žˆ์œผ๋ฉด ๊ทธ ํŽ˜์ธ์„ ๋Œ€์ƒ์œผ๋กœ ์˜ค์ธ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฐํ•จ์ด ์žˆ์—ˆ์Œ. * **C-2: `new-session` ์œ ํœด ํŽ˜์ธ(Idle Pane) ์šฐ์„  ์žฌ์‚ฌ์šฉ ๋ฐ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž… ๋ณด์ „ (`lib.sh`)**: - ๊ธฐ์กด ์›Œํฌ์ŠคํŽ˜์ด์Šค๊ฐ€ ๊ฐ์ง€๋˜๋ฉด ๊ณง๋ฐ”๋กœ `pane split`์„ ํ˜ธ์ถœํ•˜๋˜ ๊ธฐ์กด ๋™์ž‘์„ ๋ณ€๊ฒฝ, ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋‚ด ์—์ด์ „ํŠธ ๋ฏธํ• ๋‹น(์œ ํœด) ํŽ˜์ธ์ด ์กด์žฌํ•˜๋ฉด ์ด๋ฅผ `target_pane`์œผ๋กœ ์ฆ‰์‹œ ์žฌ์‚ฌ์šฉํ•˜๊ณ  split์„ ์ƒ๋žต. ์žฌ์‚ฌ์šฉ ์‹œ ํ˜ธ์ถœ์ž๊ฐ€ ์ „๋‹ฌํ•œ `$env_flags` ๋ฐ `HERDR_WORKSPACE_ID`๊ฐ€ ์œ ์‹ค๋˜์ง€ ์•Š๋„๋ก `env` ์ ‘๋‘์–ด๋กœ `$final_cmd`์— ์ฃผ์ž…ํ•˜์—ฌ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ „๋‹ฌ ์ผ๊ด€์„ฑ ๋ณด์žฅ. ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ์‹œ ์‹ ๊ทœ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์ƒ์„ฑ ์ •์ฑ…(W2b)์€ ์œ ํœด ํŽ˜์ธ์ด ์—†๋Š” ๊ฒฝ์šฐ์—๋งŒ ๊ทธ๋Œ€๋กœ ์ ์šฉ๋จ. * **๊ฒ€ํ†  ํ›„ ๋ฏธ์ฑ„ํƒ โ€” `opencode.py::resume_spec` `materialized` ๊ฒŒ์ดํŠธ**: - ๋ณด๊ณ ์„œ๋Š” `if materialized and session_uuid:` ์กฐ๊ฑด ์ œ๊ฑฐ๋ฅผ ์ œ์•ˆํ–ˆ์œผ๋‚˜, ์ด๋Š” `claude`/`grok`/`agy`/`hermes`/`opencode` 5๊ฐœ ์–ด๋Œ‘ํ„ฐ ์ „์›์— ๋™์ผํ•˜๊ฒŒ ์กด์žฌํ•˜๋Š” ์˜๋„๋œ ๊ณ„์•ฝ์ด๋ฉฐ, `materialized`๋Š” `verify_artifact()`(opencode์˜ ๊ฒฝ์šฐ ์‹ค์ œ SQLite ์„ธ์…˜ DB ์กฐํšŒ)๋กœ ์‚ฐ์ถœ๋จ โ€” ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์„ธ์…˜ ID๋กœ `--session`์„ ๋„˜๊ฒจ CLI๊ฐ€ ์‹คํŒจ/ํ–‰(hang)ํ•˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•œ ์•ˆ์ „์žฅ์น˜. ์ œ์•ˆ๋Œ€๋กœ ์ˆ˜์ • ์‹œ 5-์–ด๋Œ‘ํ„ฐ ์ผ๊ด€์„ฑ์ด ๊นจ์ง€๊ณ  ์•ˆ์ „์žฅ์น˜๊ฐ€ ๋ฌด๋ ฅํ™”๋˜๋ฏ€๋กœ ์ฑ„ํƒํ•˜์ง€ ์•Š์Œ. `resume_session.sh`๋Š” `WORKSPACE`๋ฅผ ํ•„์ˆ˜ ์ธ์ž๋กœ ๊ฒ€์ฆํ•˜๋ฏ€๋กœ `ctx`๊ฐ€ ๋น„์–ด `materialized`๊ฐ€ ๊ฑฐ์ง“์ด ๋˜๋Š” ๊ฒฝ๋กœ๋„ ํ™•์ธ๋˜์ง€ ์•Š์Œ. * **์žฌํ™•์ธ(์กฐ์น˜ ๋ถˆํ•„์š”) โ€” `opencode.py::ready_tokens`**: - ๋ณด๊ณ ์„œ๊ฐ€ ์ง€์ ํ•œ ASCII ๋ฐฐ๋„ˆ ๋ฏธ์ธ์‹ ๋ฌธ์ œ๋Š” v4.1.1(B-1, job `c38ddfcf`)์—์„œ ์ด๋ฏธ ํ•ด๊ฒฐ๋˜์–ด ํ˜„์žฌ `ready_tokens`๊ฐ€ ๋ณด๊ณ ์„œ์˜ "์ˆ˜์ • ํ›„" ๊ฐ’๊ณผ ์™„์ „ํžˆ ์ผ์น˜ํ•จ์„ ํ™•์ธ. ๋ณด๊ณ ์„œ ์ž‘์„ฑ ์‹œ์ ์ด v4.1.1 ์ด์ „ ์ฒดํฌ์•„์›ƒ์ด์—ˆ๋˜ ๊ฒƒ์œผ๋กœ ์ถ”์ •. --- ### ๐Ÿ› ๏ธ `v4.1.1` โ€” Agent Creation & Runtime Reliability Fixes (2026-08-30) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค (PATCH Release)**: ์—์ด์ „ํŠธ ์ƒ์„ฑ ๋ฐ ๋Ÿฐํƒ€์ž„ ์‹ ๋ขฐ์„ฑ ํ–ฅ์ƒ์„ ์œ„ํ•œ 4๋Œ€ ๋ณดํŽธ ๋ฒ„๊ทธ ์ˆ˜์ •(SemVer 2.0.0 ยง6 PATCH), Herdr ๊ฒฉ๋ฆฌ ํ™˜๊ฒฝ `PYTHONPATH` ์ž๋™ ์ฃผ์ž…, OpenCode TUI ๋ ˆ๋”” ํ† ํฐ ๋ณด๊ฐ•, ์„ธ์…˜ ํŽ˜์ธ ์‹๋ณ„์ž Fast-Path ๋ฐ ์‹œ์ž‘ ํƒ€์ž„์•„์›ƒ ๋ณต์›๋ ฅ ๊ฐ•ํ™”. #### ๐Ÿฉน ๋ฒ„๊ทธ ์ˆ˜์ • ๋ฐ ์•ˆ์ •์„ฑ ๊ฐœ์„  (Bug Fixes & Resilience) * **B-1: OpenCode TUI ์ค€๋น„ ๊ฐ์ง€ ํ† ํฐ ๋ณด๊ฐ• (`lib_py/agents/adapters/opencode.py`)**: - `ready_tokens`๋ฅผ `'OpenCode|Chat|Ask anything|tab agents|ctrl\+p|Build auto|commands'`๋กœ ํ™•์žฅํ•˜์—ฌ ASCII ๋ฐฐ๋„ˆ ๋ฐ `Build auto` ์ดˆ๊ธฐ ํ™”๋ฉด์—์„œ 30์ดˆ ๋Œ€๊ธฐ ์—†์ด ์ฆ‰๊ฐ ์ค€๋น„ ์™„๋ฃŒ ํŒ์ •. * **B-2: Herdr Shim ํ…œํ”Œ๋ฆฟ ๋‚ด ๋…๋ฆฝ `PYTHONPATH` ์ฃผ์ž… (`lib.sh`)**: - `.mam/shim/herdr` ์Šคํฌ๋ฆฝํŠธ ์ƒ๋‹จ์—์„œ ์ž์‹ ์˜ ๊ฒฝ๋กœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ `_SKILL_DIR`์„ ์‚ฐ์ถœํ•˜๊ณ  `PYTHONPATH`์— ์ฃผ์ž…ํ•˜์—ฌ ๋ถ€๋ชจ ์…ธ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋ฏธ์ƒ์† ์„œ๋ธŒ์…ธ์—์„œ๋„ `lib_py` ๋ชจ๋“ˆ ์ •์ƒ ๋กœ๋“œ ๋ณด์žฅ. * **B-3: `herdr agent start` ํƒ€์ž„์•„์›ƒ ๋ณต์›๋ ฅ ๋ฐ send-text ํด๋ฐฑ (`lib.sh`)**: - ์‹œ์ž‘ ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ ์‹œ ๋ฌด์กฐ๊ฑด ์‹คํŒจ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ , ์‹ค์ œ ์—์ด์ „ํŠธ ํ”„๋กœ์„ธ์Šค ์ƒ์กด์„ ํ™•์ธํ•˜๊ฑฐ๋‚˜ `pane send-text` + Enter ํด๋ฐฑ์œผ๋กœ ๋ช…๋ น์„ ์ง์ ‘ ์žฌ์ฃผ์ž…ํ•˜์—ฌ ์„ธ์…˜ ์ดˆ๊ธฐํ™” ์„ฑ๊ณต๋ฅ  ์ œ๊ณ . * **B-4: `_resolve_herdr_pane_id` ํŽ˜์ธ ID Fast-Path ๋ฐ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ ˆ์ด๋ธ” ๋งค์นญ (`lib.sh`)**: - ์ด๋ฏธ ํ•ด์„๋œ ํŽ˜์ธ ID(`wN:pM`) ์ž…๋ ฅ ์‹œ ์ฆ‰๊ฐ ๋ฐ˜ํ™˜ํ•˜๋Š” Fast-Path ์ถ”๊ฐ€. - `herdr workspace list`๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ ˆ์ด๋ธ”(์˜ˆ: `mam-agents`)์„ ์‹ค์ œ `workspace_id`๋กœ ์ž๋™ ๋ณ€ํ™˜ ๋งค์นญ. --- ### ๐Ÿš€ `v4.1.0` โ€” OpenCode AI Agent Integration (2026-08-29) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค (MINOR Release)**: OpenCode AI ์—์ด์ „ํŠธ(`anomalyco/opencode`) ๋ฐฑ์—”๋“œ ์™„์ „ ํ†ตํ•ฉ(SemVer 2.0.0 ยง7 MINOR), 5๋Œ€ ํ•ต์‹ฌ ์—์ด์ „ํŠธ(`claude`, `agy`, `hermes`, `grok`, `opencode`) ํ‘œ์ค€ํ™”, 29๊ฐœ CLI ์Šคํฌ๋ฆฝํŠธ/์Šคํ‚ฌ ์—ฐ๋™ ๋ฐ 447๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS ๋‹ฌ์„ฑ. #### โœจ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ (New Features & Enhancements) * **F-1: OpenCode ์—์ด์ „ํŠธ ์–ด๋Œ‘ํ„ฐ(`OpenCodeAgentAdapter`) ๊ตฌํ˜„**: - `lib_py/agents/adapters/opencode.py` ์‹ ๊ทœ ๊ตฌํ˜„ ๋ฐ `lib_py/agents/registry.py` ๋“ฑ๋ก. - SQLite ์Šคํ† ๋ฆฌ์ง€(`opencode.db`) ๋‚ด `directory` ๋ฐ `time_created`(๋ฐ€๋ฆฌ์ดˆ) ์‹ค ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜์˜ ๋ฐฉ์–ด์  ์„ธ์…˜ ๊ฒ€์ฆ(`verify_artifact`) ๋ฐ ๋ฉ€ํ‹ฐ ํ›„๋ณด์ž ๋ฐœ๊ฒฌ(`discover`) ์ง€์›. - `_resolve_db()`๋ฅผ ํ†ตํ•œ `opencode db path` CLI ๋™์  ํ•ด์„ ๋ฐ ์ƒŒ๋“œ๋ฐ•์Šค ํ™˜๊ฒฝ(`HOME`/`XDG_DATA_HOME`) ๊ฒฉ๋ฆฌ ์ง€์›. * **F-2: CLI ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์ „๋ฉด ์—ฐ๋™ (29๊ฐœ ํ„ฐ์น˜ํฌ์ธํŠธ)**: - `create_session.sh`, `resume_session.sh`, `stop_session.sh`, `status.sh`, `reconcile.sh`, `run_loop.sh`, `orc_onboard.sh` ๋‚ด `--agent opencode` ์™„์ „ ์ง€์›. - ์„ธ์…˜ ์ƒ์„ฑ ๋ฐ ์žฌ๊ฐœ ์‹œ `OPENCODE_PERMISSION='{"*":"allow"}'` ์ž๋™ ๋‚ด๋ณด๋‚ด๊ธฐ ๋ฐ ์‚ฌ์šฉ์ž ์‚ฌ์ „ ์„ค์ • ๋ณด์กด(Empty-Guard). - ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์กฐ์ •์ž(`reconcile.sh`) drift-C ๋ธ”๋ก์—์„œ ์–ด๋Œ‘ํ„ฐ ๊ณต์œ  ํ—ฌํผ๋ฅผ ํ†ตํ•œ ์„ธ์…˜ ID ์‚ฌํ›„ ์ž์œจ ๊ฐ์ง€ ๋ฐ YAML ๋“ฑ๋ก. * **F-3: ํฌ๊ด„์  ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ํ™•์žฅ**: - `tests/test_a4_adapter_contract.py`: ์–ด๋Œ‘ํ„ฐ ๊ณ„์•ฝ, ์‹ค ์Šคํ‚ค๋งˆ ๋ฐ€๋ฆฌ์ดˆ ๋น„๊ต, CLI DB ๊ฒฝ๋กœ ๋™์  ํ•ด์„ ๊ฒ€์ฆ. - `tests/test_tier1_unit.py`, `tests/test_tier2_component.py`, `tests/test_c1_tui_readiness.py`: TUI ๋ฐ ๋ผ์ดํ”„์‚ฌ์ดํด ํ†ตํ•ฉ ๊ฒ€์ฆ (์ด 447๊ฐœ ํ…Œ์ŠคํŠธ 100% ํ†ต๊ณผ). --- ### ๐Ÿš€ `v4.0.0` โ€” Complete Cline Agent Deprecation & Hermes Modernization (2026-08-28) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค (MAJOR Release)**: `cline` ์—์ด์ „ํŠธ ๋ฐฑ์—”๋“œ์˜ ์™„์ „ํ•œ ์ œ๊ฑฐ ๋ฐ ํ์ง€(Breaking Change per SemVer 2.0.0 ยง8), Hermes ์—์ด์ „ํŠธ ํ’€ ๋ชจ๋”๋‹ˆ์ œ์ด์…˜ ๋ฐ Ollama ๋กœ์ปฌ ์—ฐ๋™ ์ •์‹ ์ง€์›, 4๋Œ€ ํ•ต์‹ฌ ์—์ด์ „ํŠธ(`claude`, `agy`, `hermes`, `grok`) ํ‘œ์ค€ํ™” ๋ฐ 439๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS ๋‹ฌ์„ฑ. #### โš ๏ธ ๋™์ž‘ ๋ณ€๊ฒฝ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์•ˆ๋‚ด (Breaking Changes) * **B-1: `--agent cline` ์ง€์› ์™„์ „ ์ œ๊ฑฐ ๋ฐ ํŒŒ์„œ ๊ฑฐ๋ถ€**: - `adapters/cline.py` ์‚ญ์ œ ๋ฐ `registry.py` ๋“ฑ๋ก ํ•ด์ œ์— ๋”ฐ๋ผ ๋ชจ๋“  ์Šคํ‚ฌ ์—”ํŠธ๋ฆฌํฌ์ธํŠธ(`create_session.sh`, `resume_session.sh`, `stop_session.sh`, `reconcile.sh`, `orc_onboard.sh`, `multi-agent-mux-delegate-job` ๋“ฑ)์—์„œ `--agent cline` ์ž…๋ ฅ ์‹œ `exit 2`๋กœ ์ฆ‰์‹œ ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค. - ์ง€์›๋˜๋Š” ์—์ด์ „ํŠธ ํŒจ๋ฐ€๋ฆฌ๋Š” `claude`, `agy`, `hermes`, `grok` 4์ข…์œผ๋กœ ์—„๊ฒฉํžˆ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. * **B-2: ์˜ํ–ฅ๋„ ํ™•์ธ (Detection)**: - ์ €์žฅ๋œ ์Šคํฌ๋ฆฝํŠธ๋‚˜ ์ปค์Šคํ…€ ์„ธ์…˜์—์„œ Cline ๊ด€๋ จ ์„ค์ •์ด ๋‚จ์•„์žˆ๋Š”์ง€ ํ™•์ธ: ```bash grep -rn "agent: cline\|cline_conversation_id_own\|--agent cline" .mam/ *.sh 2>/dev/null ``` * **B-3: ์—…๊ทธ๋ ˆ์ด๋“œ ์ „ ์„ธ์…˜ ์ •๋ฆฌ (Pre-upgrade Cleanup โ€” ๊ถŒ์žฅ)**: - v3.1.0 ์ƒํƒœ์—์„œ `deploy/update.sh` ์‹คํ–‰ ์ „, ๊ธฐ์กด์˜ ํ™œ์„ฑ/๋ณด๊ด€ ์ค‘์ธ Cline ์„ธ์…˜์„ ์›์ž์ ์œผ๋กœ ์ •๋ฆฌ: ```bash multi-agent-mux-stop --session --agent cline --purge-conversation --yes ``` * **B-4: ์—…๊ทธ๋ ˆ์ด๋“œ ํ›„ ๊ณ ์•„ ์„ธ์…˜ ๋ ˆ์ฝ”๋“œ ์ •๋ฆฌ (Post-upgrade YAML Pruning)**: - `deploy/update.sh`๋Š” `.mam/agent-sessions.yaml`์„ ๋ฎ์–ด์“ฐ์ง€ ์•Š๊ณ  ๋ณด์กดํ•˜๋ฏ€๋กœ, ์‚ฌ์ „์— ์ •๋ฆฌ๋˜์ง€ ์•Š์€ v3.x `cline` ์„ธ์…˜ ํ–‰์€ ์•„๋ž˜ ์›์ž์  ๊ฐฑ์‹  ๋ช…๋ น์„ ํ†ตํ•ด ์•ˆ์ „ํ•˜๊ฒŒ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: ```bash source .agents/skills/lib.sh atomic_dump_yaml .mam/agent-sessions.yaml <<'MUT' d['herdr_sessions'] = [ s for s in d.get('herdr_sessions', []) if s.get('agent') != 'cline' and not str(s.get('name', '')).endswith('-cline') ] MUT ``` * **B-5: Hermes ์—์ด์ „ํŠธ ๋ชจ๋”๋‹ˆ์ œ์ด์…˜**: - ํ—ค๋“œ๋ฆฌ์Šค ํ”Œ๋ž˜๊ทธ(`--yolo --accept-hooks`), TUI ์ž…๋ ฅ ์˜์—ญ ๊ตฌ๋ถ„์ž(`โ”€{10,}` ๋ฐ `โฏ`), ์ค€๋น„ ํ† ํฐ ๊ฐ•ํ™”, `reconcile.sh` ํƒ€์ž„์Šคํƒฌํ”„ ๊ธฐ๋ฐ˜ ๋‹ค์ค‘ ํ›„๋ณด์ž ๋™๋“ฑ์„ฑ(`discover()`) ์ง€์›์ด ์ •์‹ ํ†ตํ•ฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. --- ### ๐Ÿš€ `v3.1.0` โ€” 2-Tier TUI Readiness Model, Adapter Modal Contract & Fail-Closed Pane Resolution (2026-08-28) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: ๋‹ค์ด์–ผ๋กœ๊ทธ ๊ธฐ์•„(starvation) ๊ตฌ์กฐ์  ํ•ด์†Œ๋ฅผ ์œ„ํ•œ 2๊ณ„์ธต TUI ์ค€๋น„ ํŒ์ • ๋ชจ๋ธ(`S โˆจ (W โˆง C)`), ๋ชจ๋‹ฌ/ํžŒํŠธ 2๊ณ„์ธต ํ† ํฐ ๋ถ„๋ฆฌ, ์–ด๋Œ‘ํ„ฐ๋ณ„ `modal_tokens` ๊ณ„์•ฝ(`T-2d`) ์ •๋ฆฝ, ๋™์ข… ํŽ˜์ธ ๋‹ค์ค‘ ๋งค์น˜ fail-closed ๋ฐฉ์–ด, ์ค€๋น„ ์‹คํŒจ ์„ธ์…˜ ์ง„๋‹จ ๋ณด์กด(`create_session.sh`), `lib.sh` ๋Ÿฐํƒ€์ž„ ๋ฒ„์ „ ์ƒ์ˆ˜(`MAM_VERSION="3.1.0"`) ์‹ ์„ค ๋ฐ 436๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS ๋‹ฌ์„ฑ. #### โš ๏ธ ๋™์ž‘ ๋ณ€๊ฒฝ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์•ˆ๋‚ด * **B-1: TUI ๋ฏธ์ค€๋น„ ์„ธ์…˜ ๋ณด์กด (`create_session.sh`)**: - TUI ์ค€๋น„ ๊ฐ์ง€ ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ ์‹œ, PID๊ฐ€ ์ƒ์กดํ•ด ์žˆ๋‹ค๋ฉด ์„ธ์…˜์„ ๊ฐ•์ œ ์ข…๋ฃŒ(`kill-session`)ํ•˜์ง€ ์•Š๊ณ  `status: running`, `last_visible_status: tui-ready-timeout`์œผ๋กœ ๋“ฑ๋กํ•œ ๋’ค `exit 0`(์„ฑ๊ณต)์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. - ๊ตฌ `v3.0.1`์˜ ๋ฌด์กฐ๊ฑด ๋กค๋ฐฑ ๋ฐ ์ข…๋ฃŒ ๋™์ž‘์„ ์›ํ•  ๊ฒฝ์šฐ `MAM_KILL_ON_READY_TIMEOUT=1`์„ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค. * **B-2: ํ”„๋กœ์„ธ์Šค ์‚ฌ๋ง ์‹œ ์ข…๋ฃŒ ์ฝ”๋“œ ๋ณ€๊ฒฝ**: - `create_session.sh` ์‹คํ–‰ ์ค‘ PID๊ฐ€ ์‚ฌ๋งํ•œ ๊ฒฝ์šฐ ๋ช…์‹œ์  ๊ตฌ๋ถ„ ์ฝ”๋“œ `42`(`MAM_EXIT_READY_TIMEOUT`)๋กœ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. * **B-3: ๋™์ข… ํŽ˜์ธ ๋‹ค์ค‘ ๋งค์น˜ ์‹œ Fail-Closed ๊ฑฐ๋ถ€ (`_resolve_herdr_pane_id`)**: - ๋™์ผํ•œ ์—์ด์ „ํŠธ ์ข…๋ฅ˜์˜ ํŽ˜์ธ์ด ์—ฌ๋Ÿฌ ๊ฐœ ๋ฐœ๊ฒฌ๋˜๊ณ  ์ •ํ™•ํ•œ ์„ธ์…˜๋ช… ์ผ์น˜๊ฐ€ ์‹คํŒจํ•  ๊ฒฝ์šฐ, ์ž„์˜์˜ ์ฒซ ๋ฒˆ์งธ ํŽ˜์ธ์„ ์„ ํƒํ•˜์ง€ ์•Š๊ณ  ์ฆ‰์‹œ `exit 1`๋กœ ์‹คํŒจํ•˜์—ฌ ๊ต์ฐจ ์„ธ์…˜ ์˜ค์—ผ์„ ์›์ฒœ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. * **B-4 & B-5: ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ „ํŒŒ (`WORKSPACE_ROOT`, `MAM_VERSION`)**: - `WORKSPACE_ROOT` ๋ฐ `MAM_VERSION`์ด ์ž์‹ ํ”„๋กœ์„ธ์Šค๋กœ ์ž๋™ `export`๋ฉ๋‹ˆ๋‹ค. `MAM_VERSION`์€ ๋Ÿฐํƒ€์ž„ ์œ„์กฐ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์˜ค๋ฒ„๋ผ์ด๋“œ๊ฐ€ ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค. #### ๐Ÿ”ง ๊ธฐ์กด ์›Œํฌ์ŠคํŽ˜์ด์Šค ์‚ฌ์šฉ์ž ์•ˆ๋‚ด ๊ธฐ์กด ๋ฐฐํฌ๋œ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์‚ฌ์šฉ์ž๋Š” `.mam.env`์— ์•„๋ž˜ 3์ข…์˜ ์‹ ๊ทœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋™์ž‘์„ ํŠœ๋‹ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: | ๋ณ€์ˆ˜๋ช… | ๊ธฐ๋ณธ๊ฐ’ | ์„ค๋ช… | | :--- | :---: | :--- | | `MAM_KILL_ON_READY_TIMEOUT` | `0` | `1`๋กœ ์„ค์ • ์‹œ TUI ์ค€๋น„ ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ ์‹œ ์„ธ์…˜์„ ๋ณด์กดํ•˜์ง€ ์•Š๊ณ  ๊ฐ•์ œ ์ข…๋ฃŒ(v3.0.1 ๋™์ž‘) | | `MAM_DIALOG_TAIL_LINES` | `8` | ๋ธ”๋กœํ‚น ๋ชจ๋‹ฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฒ€์‚ฌํ•  ํŽ˜์ธ ํ•˜๋‹จ ๋น„๊ณต๋ฐฑ ๋ผ์ธ ์ˆ˜ | | `MAM_READY_EMPTY_GIVEUP` | `5` | `wait_for_tui_ready`๊ฐ€ ํŽ˜์ธ์„ ๊ด€์ธก ๋ถˆ๊ฐ€(Headless)๋กœ ํŒ์ •ํ•˜๊ธฐ๊นŒ์ง€์˜ ์—ฐ์† ๋นˆ ์บก์ฒ˜ ํšŸ์ˆ˜ | #### 1. 2๊ณ„์ธต TUI ์ค€๋น„ ํŒ์ • ๋ชจ๋ธ (`S โˆจ (W โˆง C)`) * **๋‹จ๋… ์ถฉ๋ถ„ ๊ฐ•ํ•œ ์‹ ํ˜ธ (`S`)**: `strong_ready_tokens` ๋˜๋Š” 8์ž ์ด์ƒ์˜ ์œ ์˜๋ฏธํ•œ `input_placeholder`(`Ask anything...`) ์ผ์น˜ ์‹œ ์ค€๋น„ ์™„๋ฃŒ ์„ ์–ธ. * **๋ณด๊ฐ• ๊ฒฐํ•ฉ ์•ฝํ•œ ์‹ ํ˜ธ (`W โˆง C`)**: `weak_ready_tokens`(`history`, `Chat` ๋“ฑ)๋Š” `input_prompt`(`โฏ`, `>`) ๋˜๋Š” `input_rule_pattern`(`โ”€{10,}`)๊ณผ ๋™์‹œ ์ผ์น˜ํ•  ๋•Œ๋งŒ ์ค€๋น„ ์™„๋ฃŒ๋กœ ํŒ์ •ํ•˜์—ฌ ์˜คํƒ ์ฐจ๋‹จ. * **ํ•˜๊ฐ• ํ˜ธํ™˜์„ฑ (Fallback)**: `input_*` ์†์„ฑ์ด ๋ฏธ์ •์˜๋œ ์—์ด์ „ํŠธ(Hermes ๋“ฑ)๋Š” ๊ฐ•ํ•œ ํ† ํฐ ๋‹จ๋… ๊ฒ€์‚ฌ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ํ•˜๊ฐ•. #### 2. ๋ชจ๋‹ฌ/ํžŒํŠธ ํ† ํฐ ๋ถ„๋ฆฌ ๋ฐ ์–ด๋Œ‘ํ„ฐ `modal_tokens` ๊ณ„์•ฝ (`T-2a` / `T-2d`) * **ํ† ํฐ ์ง‘ํ•ฉ ๋ถ„๋ฆฌ**: `_MAM_MODAL_TOKENS`(๊ณ ์‹ ๋ขฐ ๋ธ”๋กœํ‚น ๋ชจ๋‹ฌ)์™€ `_MAM_HINT_TOKENS`(์ž์—ฐ์–ด TUI ์•ˆ๋‚ด ํŒ)๋ฅผ ๋ถ„๋ฆฌ. `_pane_dialog_open`์ด ํžŒํŠธ ํ…์ŠคํŠธ๋กœ ์ธํ•ด ์˜๊ตฌ ์ฐจ๋‹จ(Starvation)๋˜๋Š” ๊ฒฐํ•จ ํ•ด์†Œ. `_MAM_DIALOG_TOKENS`๋Š” ํ•˜์œ„ํ˜ธํ™˜์šฉ ํ•ฉ์ง‘ํ•ฉ์œผ๋กœ ์กด์น˜. * **`BaseAgentAdapter.modal_tokens`**: ์—์ด์ „ํŠธ๋ณ„ ํŠนํ™” ๋ชจ๋‹ฌ(Claude์˜ `Try the new fullscreen renderer?`, Cline์˜ `Select API Provider` ๋“ฑ)์„ ํŒฉํŠธ ๋ธŒ๋ฆฌ์ง€(`MAM_MODAL_TOKENS`)๋กœ ํ†ตํ•ฉ. #### 3. Fail-Closed ํŽ˜์ธ ํ•ด์„ ๋ฐ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์Šค์ฝ”ํ”„ ๊ฐ•ํ™” (`R-1` / `R-2`) * `_resolve_herdr_pane_id`์—์„œ ๋‹ค์ค‘ ๋งค์น˜ ์‹œ `sys.exit(1)` ์ฒ˜๋ฆฌ ๋ฐ `export WORKSPACE_ROOT` ๋ณด๊ฐ•์œผ๋กœ ๊ฒฉ๋ฆฌ ์•ˆ์ •์„ฑ ํ™•๋ณด. #### 4. ์ค€๋น„ ์‹คํŒจ ์„ธ์…˜ ์ง„๋‹จ ๋ณด์กด ๋ฐ `MAM_VERSION` ์Šคํƒฌํ”„ * `create_session.sh` ์‹คํŒจ/ํƒ€์ž„์•„์›ƒ ์‹œ `.mam/diagnostics/-.txt`๋กœ ํŽ˜์ธ ๋คํ”„ ๋ฐ `MAM Version` ์Šคํƒฌํ”„ ์ž๋™ ๊ธฐ๋ก. #### 5. ๋Ÿฐํƒ€์ž„ ๋ฒ„์ „ ์ƒ์ˆ˜ `MAM_VERSION` ์‹ ์„ค ๋ฐ 3์ž ๋ฝ์Šคํ… ๊ฒ€์ฆ * `lib.sh` ๋‚ด `MAM_VERSION="3.1.0"` ์„ ์–ธ ๋ฐ `test_version_consistency.py`๋ฅผ ํ†ตํ•œ `lib.sh` โ†” `VERSIONS.md` โ†” 8๊ฐœ `SKILL.md` 3์ž ์ผ์น˜ ์ž๋™ ๊ฒ€์ฆ. #### 6. ์‹ ๊ทœ ํ™˜๊ฒฝ๋ณ€์ˆ˜ 3์ข… ๋ฌธ์„œํ™” * `.mam.env.example`์— `MAM_KILL_ON_READY_TIMEOUT`, `MAM_DIALOG_TAIL_LINES`, `MAM_READY_EMPTY_GIVEUP` 3์ข… ๋“ฑ๋ก. #### 7. ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ํ™•์žฅ: 412 โ†’ 436 (+24, 100% PASS) * `test_c1_tui_readiness.py` (21 tests), `test_adapter_modal_tokens_contract`, `test_version_consistency.py` ์‹ ์„ค ๋“ฑ ์ด 436๊ฐœ ํ…Œ์ŠคํŠธ ๋ฌด๊ฒฐ์  ํ†ต๊ณผ. #### 8. ๊ต์ฐจ ํ”ผ์–ด ๋ฆฌ๋ทฐ ์ด๋ ฅ * F-1~F-8 ๋ฐ R-1, R-2 ์ด 10๊ฑด์˜ ํ”ผ์–ด ๋ฆฌ๋ทฐ ์ด์Šˆ ๋„์ถœ ๋ฐ ํ•ด์†Œ: - F-1 (`Yes, try it` ํŒ ์˜คํƒ ์ œ๊ฑฐ), F-2 (์—…์…€ ๋ชจ๋‹ฌ ์„ ํ–‰ ๊ฒ€์‚ฌ), F-3 (๋ณ€์ˆ˜ ์Šค์ฝ”ํ”„ ๊ฒฉ๋ฆฌ), F-4 (๋ฆฌ๋ทฐ์–ด ์‹ค์ธก ๊ฒ€์ฆ ํ›„ ์ž์ง„ ์ฒ ํšŒ), F-5/F-6/F-7 (์–ด๋Œ‘ํ„ฐ ํŒฉํŠธ ์—ฐ๋™ ๋ฐ ์„ธ์…˜ ํŒจํ„ด ๋ณด๊ฐ•), F-8 (๋น„-ํด๋กœ๋“œ ์–ด๋Œ‘ํ„ฐ modal_tokens ๊ณ„์•ฝ ์ •๋ฆฝ), R-1 (Fail-closed ํŽ˜์ธ ๋ฆฌ์กธ๋ฒ„), R-2 (`WORKSPACE_ROOT` export ์ „ํŒŒ). - ๊ณ„ํš ๋‹จ๊ณ„ ์ฑŒ๋ฆฐ์ง€(`b9c29c3b`)๋ฅผ ํ†ตํ•œ C-1 (`MAM_VERSION` 3์ž ๋ฝ์Šคํ…) ๋ฐ C-2 (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์•ˆ๋‚ด) ์ˆ˜์šฉ. ### ๐Ÿš€ `v3.0.1` โ€” Herdr Shim Routing Contract Refactor, Multi-Workspace Isolation & 412-Test Milestone (2026-08-27) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: Herdr ์‹ฌ ๋ผ์šฐํŒ… 5๋Œ€ ๊ฒฐํ•จ(ISSUE-1~5) ๋ฐ F-1~F-4 ๋ณด์™„ ์™„๊ฒฐ, `HERDR_WORKSPACE_ID` ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์Šค์ฝ”ํ•‘ ๋ฐ ํŒŒ์ผ ๊ธฐ๋ฐ˜ ๊ฒฉ๋ฆฌ ์˜์†ํ™” ์—”์ง„ ํƒ‘์žฌ, `pane send-text` ๊ธฐ๋ฐ˜ ๋‹จ์ผ ์•ˆ์ „ ์‚ฝ์ž… ๊ณ„์•ฝ ์ •๋ฆฝ, ๋ถ€๋ถ„ ๋ฌธ์ž์—ด ๋งค์นญ(`in tn`) ์™„์ „ ์ œ๊ฑฐ, 412๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS ๋‹ฌ์„ฑ. #### 1. Herdr ์‹ฌ ๋ผ์šฐํŒ… ๋ฐ ๋‹ค์ค‘ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๊ฒฉ๋ฆฌ ๊ฐ•ํ™” (`.agents/skills/lib.sh`) * **`HERDR_WORKSPACE_ID` ๋ช…์‹œ์  ์Šค์ฝ”ํ”„ & ์˜์†ํ™” (`ISSUE-3`, `F-2b`)**: - `_herdr_ws_id_file()`, `_herdr_persist_ws_id()`, `_herdr_ws_scope()` ํ—ฌํผ ๋„์ž…. - `new-session` ์‹œ ์ƒ์„ฑ๋œ ์›Œํฌ์ŠคํŽ˜์ด์Šค ID๋ฅผ `$WORKSPACE_ROOT/.mam/herdr_workspace_id`์— ์˜์†ํ™”. - `_herdr_agent_get_scoped`๋Š” ํ˜ธ์ถœ์ž์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜(`HERDR_WORKSPACE_ID`)๋งŒ์„ ์šฐ์„  ํ‰๊ฐ€ํ•˜์—ฌ ๋‹จ์ถ• ๊ฒฝ๋กœ์—์„œ์˜ ๊ต์ฐจ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ผ์šฐํŒ… ์˜ค์—ผ ์ฐจ๋‹จ. * **์—„๊ฒฉ ์ผ์น˜(Exact-Match) ๋‹จ์ผ ํŽ˜์ธ ๋ฆฌ์กธ๋ฒ„ ์ •๋ฆฝ (`ISSUE-2`, `ISSUE-5`, `F-3`)**: - ๊ธฐ์กด์˜ ์—์ด์ „ํŠธ CLI ๋ช…์นญ ๋ถ€๋ถ„ ๋ฌธ์ž์—ด ๋งค์นญ(`in tn`, `agent in tn`) ์™„์ „ ์ œ๊ฑฐ. - ๋‹จ์ผ ์ค‘์•™ ํ—ฌํผ `_resolve_herdr_pane_id`๋กœ `has-session`, `kill-session`, `capture-pane`, `send-keys`, `paste-buffer`์˜ ํŽ˜์ธ ํ•ด์„ ํ†ตํ•ฉ. - ์˜์ˆซ์ž ์›Œํฌ์ŠคํŽ˜์ด์Šค ์‹๋ณ„์ž ํŒจํ„ด(`^w[A-Za-z0-9]+:p[A-Za-z0-9]+$`) ์ง€์› (`H-20`). #### 2. ์›์ž์  ํ…์ŠคํŠธ ์‚ฝ์ž… ๋ฐ ์•ˆ์ „ ์ „์†ก ๊ณ„์•ฝ ๊ต์ • (`ISSUE-1`, `F-4`) * **`paste-buffer`์˜ `pane send-text` ์ „์šฉํ™”**: - Herdr CLI์— ๋ฏธ์กด์žฌํ•˜๋˜ `agent send` ์ œ๊ฑฐ ๋ฐ `pane send-text`๋กœ ๊ต์ฒด. - ์‚ฝ์ž…๊ณผ ์ œ์ถœ(Enter) ์ฑ…์ž„์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์˜๋„์น˜ ์•Š์€ ์ž๋™ ์ œ์ถœ ๋ฐ ์ด์ค‘ ์ œ์ถœ(Double Submit) ๋ฐฉ์ง€. - ํŽ˜์ธ ๋ฏธํ•ด์„ ๋˜๋Š” ์‚ฝ์ž… ์‹คํŒจ ์‹œ `send_keys_safe`๋กœ ์—๋Ÿฌ ์ฝ”๋“œ(`rc 3`) ๋ช…์‹œ์  ์ „ํŒŒ. * **`capture-pane` ํด๋ฐฑ ์ฒด์ธ ๋ณต์› (`F-4`)**: - `pane read` ์‹คํŒจ ์‹œ `agent read`๋กœ์˜ ํด๋ฐฑ ์ฒด์ธ์„ ์•ˆ์ •์ ์œผ๋กœ ๋ณต์›ํ•˜์—ฌ TUI ์บก์ฒ˜ ์‹ ๋ขฐ์„ฑ ๋ณด์žฅ. #### 3. TUI ์ค€๋น„ ์ƒํƒœ ๊ฐ์ง€ ๋ฐ ๋‹ค์ด์–ผ๋กœ๊ทธ ํ† ํฐ ์ •๋ฐ€ํ™” (`N-1`, `F-1`) * **`_MAM_DIALOG_TOKENS` ์ผ๋ฐ˜ ํŒ ํ† ํฐ ๋ถ„๋ฆฌ**: - ์ผ๋ฐ˜ ๋Œ€ํ™”ํ˜•/TUI ํŒ์— ๋“ฑ์žฅํ•˜๋Š” `Yes, try it`์„ ๋ธ”๋กœํ‚น ๋‹ค์ด์–ผ๋กœ๊ทธ ํ† ํฐ ๋ชฉ๋ก์—์„œ ์ œ์™ธํ•˜์—ฌ ์„ธ์…˜ ์‹œ์ž‘ ์‹œ์˜ ์˜คํƒ ๋ฐฉ์ง€. - ํ’€์Šคํฌ๋ฆฐ ์—…์…€ ๋ชจ๋‹ฌ์— ๋Œ€ํ•ด์„œ๋Š” ์ „์šฉ Escape ๊ฑฐ์ ˆ ๋ธŒ๋žœ์น˜๋กœ ๊ฒฉ๋ฆฌ ์ฒ˜๋ฆฌ. #### 4. ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ๋ฐ Mock Herdr ๊ณ„์•ฝ ์ •๊ธฐ ๋™๊ธฐํ™” (`tests/`) * **Mock Herdr (`tests/conftest.py`)**: ์‹ค์ œ Herdr 0.8.2 CLI ๊ณ„์•ฝ๊ณผ ์™„๋ฒฝ ๋™๊ธฐํ™” (`pane send-text`, `pane read`, `pane rename` ๋ฐ˜์˜, ๋ฏธ์ง€์› `agent send` ์‹คํŒจ ์ฒ˜๋ฆฌ). * **์‹ ๊ทœ ๊ณ„์•ฝ & ํšŒ๊ท€ ํ…Œ์ŠคํŠธ 19๊ฑด ์ถ”๊ฐ€**: - `test_herdr_shim_contract.py`: H-15 ~ H-23 (๋‹จ์ผ ๋ฆฌ์กธ๋ฒ„ ๋ถˆ๋ณ€์‹, ์›Œํฌ์ŠคํŽ˜์ด์Šค ๊ฒฉ๋ฆฌ, ๋‹จ์ถ•๊ฒฝ๋กœ ์Šค์ฝ”ํ•‘). - `test_b19_headless_reconcile_fixes.py`: D-4 ~ D-7 (set -e ์•ˆ์ „์„ฑ, send_keys_safe ์‹คํŒจ ์ „ํŒŒ, ๋‹ค์ด์–ผ๋กœ๊ทธ ํ† ํฐ ์ œ์™ธ ํšŒ๊ท€ ๋ฐฉ์ง€). * **์ „์ฒด ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ**: 412 passed in 681.20s (100% PASS). --- ### ๐Ÿš€ `v3.0.0` โ€” 5th Agent (Grok) Ecosystem Expansion, Mux Loop CLI Redesign & Deterministic 2xK Layout Engine 2.0 (2026-08-26) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: 5๋ฒˆ์งธ ๊ณต์‹ ์—์ด์ „ํŠธ `grok`(Grok Build) ์ „๋ฉด ํ†ตํ•ฉ, `/multi-agent-mux-loop`์˜ `--target-agent` ํ์ง€ ๋ฐ `--creator`/`--planner` ์—ญํ•  ๋ถ„๋ฆฌ(Breaking Change), 2ร—2 ๋Œ€์นญ ๊ทธ๋ฆฌ๋“œ๋ฅผ ๋ณด์žฅํ•˜๋Š” ๊ฒฐ์ •๋ก ์  ๋ ˆ์ด์•„์›ƒ ์—”์ง„ 2.0 ํƒ‘์žฌ, 393๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS ๋‹ฌ์„ฑ. #### โš ๏ธ Breaking Changes & Migration Guide * **`multi-agent-mux-loop` CLI ํ”Œ๋ž˜๊ทธ ๊ฐœํŽธ**: - ๊ธฐ์กด์˜ ๋‹จ์ผ ๋Œ€์ƒ ์ง€์ • ํ”Œ๋ž˜๊ทธ `--target-agent `๊ฐ€ **๊ณต์‹ ์ œ๊ฑฐ(Drop)** ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. - ๊ธฐ์กด ๋ช…๋ น์€ ์ด์ œ `ERROR: --target-agent was removed. Use --creator instead.`์™€ ํ•จ๊ป˜ ์ข…๋ฃŒ(`exit 1`)๋ฉ๋‹ˆ๋‹ค. - **๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐฉ๋ฒ•**: - ๊ธฐ์กด: `bash run_loop.sh --target-agent --task "..."` - ๋ณ€๊ฒฝ: `bash run_loop.sh --creator --task "..."` - ํ”Œ๋ž˜๋„ˆ ์ง€์ • ์‹œ: `bash run_loop.sh --plan --planner --creator --task "..."` #### 1. 5๋ฒˆ์งธ ๊ณต์‹ AI ์—์ด์ „ํŠธ Grok Build (`grok`) ์ „๋ฉด ํ†ตํ•ฉ * **์–ด๋Œ‘ํ„ฐ ๋ฐ ์ˆ˜๋ช… ์ฃผ๊ธฐ ๊ด€๋ฆฌ (`lib_py/agents/`, `lib.sh`)**: - `GrokAgentAdapter` ๊ตฌํ˜„ ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๊ณต์‹ ๋“ฑ๋ก (`claude, agy, hermes, cline, grok`). - `create_session.sh`, `resume_session.sh`, `stop_session.sh`, `resolve_session_id.sh`์˜ ์—์ด์ „ํŠธ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐ ๋””์ŠคํŒจ์น˜ ์ง€์› ์™„๋น„. - ์˜จ๋””์Šคํฌ ์„ธ์…˜ UUID ํ•ด์„ ๋ฐ TUI ๋ Œ๋”๋ง ์ค€๋น„ ๊ฐ์ง€ ํ† ํฐ ๋ฐ˜์˜. #### 2. ๊ฒฐ์ •๋ก ์  2ร—K ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ ์—”์ง„ 2.0 (`lib_py/layout.py`, `lib.sh`) * **$N=1\to 2$ `right` ๋ถ„ํ•  ์šฐ์„  ์ •์ฑ… (R-1 ๊ฒฐํ•จ ํ•ด์†Œ)**: - 1๊ฐœ ํŽ˜์ธ์—์„œ 2๋ฒˆ์งธ ์—์ด์ „ํŠธ ์ถ”๊ฐ€ ์‹œ `down` ๋Œ€์‹  `right`๋กœ ๋ถ„ํ• (`single_pane_split_right`)ํ•˜์—ฌ ์ขŒ/์šฐ 2๊ฐœ์˜ ์ „๊ณ (Full-height) ์—ด ํ™•๋ณด. - ์ดํ›„ $N=3$(์ขŒ์ธก down), $N=4$(์šฐ์ธก down)๋กœ ์ด์–ด์ ธ ์™„๋ฒฝํ•œ 2ร—2 ๊ท ๋“ฑ ๋Œ€์นญ ๊ฒฉ์ž ๊ธฐํ•˜ํ•™(`widths=[138,139], heights=[39]`) ์™„์„ฑ. * **GUI โ†” ํ—ค๋“œ๋ฆฌ์Šค ๋‹จ์ผ ๊ฒฐ์ •ํ‘œ ํ†ตํ•ฉ (`_decide_from_cols`)**: - ๊ธฐ์กด์˜ ๋ถˆ์™„์ „ํ•œ `n % 2` ํ™€์ง ํŒจ๋ฆฌํ‹ฐ ๋กœ์ง์„ ํ๊ธฐํ•˜๊ณ , ์—ด ์ ์œ  ์ƒํƒœ(`cols=(a, b)`) ๊ธฐ๋ฐ˜์˜ ํ†ตํ•ฉ 3๋‹จ๊ณ„ ๊ฒฐ์ • ๋งคํŠธ๋ฆญ์Šค๋กœ ์ผ์›ํ™”. * **์•ˆ์ „ ๊ฐ€๋“œ ๋ฐ 4์ค‘ ์šฉ๋Ÿ‰ ๋ฐฐ์„ **: - `_full_height_pane` ๊ฐ€๋“œ๋กœ ๋ฐ˜์ชฝ ์—ด ๋ถ„ํ•  ์›์ฒœ ์ฐจ๋‹จ. - `layout.py` ๊ธฐ๋ณธ๊ฐ’(`default=2`), `lib.sh:435` CLI ์ธ์ž(`--max-cols`, `--max-rows`), `.mam.env.example` ๋™๊ธฐํ™” ์™„๋ฃŒ. #### 3. ์‚ฌ์ „ ๊ฒ€์ฆ ๋ฐ ์ธํ”„๋ผ ๋™๊ธฐํ™” * `create_session.sh`์˜ `--workspace` ํ•„์ˆ˜ ์ธ์ž ์‚ฌ์ „ ๊ฒ€์ฆ(preflight) ๊ฐ•ํ™”. * ์‚ฌ์„ค NATS ๋ธŒ๋กœ์ปค(`nats-docker`) 2.14-alpine ์ตœ์‹  ํƒœ๊ทธ ๋™๊ธฐํ™”. --- ### ๐Ÿš€ `v2.2.1` โ€” Single-Workspace 2xK Multi-Pane Tiling Optimization & Premature Overflow Fix (2026-08-24) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: `MAM_MIN_PANE_COLS` ๊ธฐ๋ณธ๊ฐ’ 60โ†’40 ์™„ํ™”, ํ‘œ์ค€ 80~100์ปฌ๋Ÿผ ํ„ฐ๋ฏธ๋„ ๋ทฐํฌํŠธ์—์„œ ์กฐ๊ธฐ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์˜ค๋ฒ„ํ”Œ๋กœ(๊ฐ€์ƒ ๋ฐ์Šคํฌํ†ฑ ๋ถ„๋ฆฌ) ๋ฐฉ์ง€ ๋ฐ ๋‹จ์ผ ์›Œํฌ์ŠคํŽ˜์ด์Šค 2x2 ํ†ตํ•ฉ ํƒ€์ผ๋ง ์™„์„ฑ, ์‹ ๊ทœ 80/79 ๊ฒฝ๊ณ„ ๋ฐ 90/100 col ํƒ€์ผ๋ง ํ…Œ์ŠคํŠธ 6์ข… ์ถ”๊ฐ€, ๋‹ค์ค‘ ์—์ด์ „ํŠธ ํ”ผ์–ด ๋ฆฌ๋ทฐ 100% PASS ๋‹ฌ์„ฑ. #### 1. 2xK ๋ ˆ์ด์•„์›ƒ ์—”์ง„ ์ตœ์†Œ ํญ ์™„ํ™” (`lib_py/layout.py`, `lib.sh`) - `compute_2xk_layout` ๊ธฐ๋ณธ `min_cols` ๋ฐ CLI `--min-cols`, `lib.sh:432`์˜ `${MAM_MIN_PANE_COLS:-40}`, `.mam.env.example` ๋ฌธ์„œ๋ฅผ `40`์œผ๋กœ 4์ค‘ ์ผ์น˜ํ™”. - 90~100์ปฌ๋Ÿผ ๋„ˆ๋น„ ํ„ฐ๋ฏธ๋„์—์„œ 3๋ฒˆ์งธ, 4๋ฒˆ์งธ ์—์ด์ „ํŠธ ์ƒ์„ฑ ์‹œ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๊ฐ€์ƒ ๋ฐ์Šคํฌํ†ฑ(Workspace)์ด ๋ถ„๋ฆฌ๋˜๋˜ ํ˜„์ƒ ์™„์ „ ํ•ด์†Œ. #### 2. ๊ฒฝ๊ณ„๊ฐ’ ๋ฐ ํƒ€์ผ๋ง ์ž๋™ํ™” ํ…Œ์ŠคํŠธ ํ™•์ถฉ (`tests/test_layout.py`, `tests/test_tier1_unit.py`) - `80` ์ปฌ๋Ÿผ(๋ถ„ํ•  ์„ฑ๊ณต) vs `79` ์ปฌ๋Ÿผ(์˜ค๋ฒ„ํ”Œ๋กœ) ํ•˜ํ•œ ๊ฒฝ๊ณ„๊ฐ’ ๊ฒ€์ฆ. - `90` ๋ฐ `100` ์ปฌ๋Ÿผ ๋‹จ์ผ ์›Œํฌ์ŠคํŽ˜์ด์Šค 1โ†’2โ†’3โ†’4 ๋‹จ๊ณ„ 2x2 ํƒ€์ผ๋ง ๋ฐ 5๋ฒˆ์งธ ์—์ด์ „ํŠธ ์˜ค๋ฒ„ํ”Œ๋กœ ์ „ ๊ณผ์ • ์ˆ˜๋ช… ์ฃผ๊ธฐ ๊ฒ€์ฆ. --- ### ๐Ÿš€ `v2.2.0` โ€” Herdr Workspace Label Standardization, Runtime Sync & Legacy Fallback Decoupling (2026-08-24) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: `--herdr-workspace` ์˜ต์…˜ ์ „ ์Šคํ‚ฌ ๋„์ž… ๋ฐ YAML ๋…๋ฆฝ ์ง๋ ฌํ™”, Herdr ๋Ÿฐํƒ€์ž„ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ ˆ์ด๋ธ” ์‹ค์‹œ๊ฐ„ ๋™๊ธฐํ™”, ๋ ˆ๊ฑฐ์‹œ ์†Œ์ผ“ ํด๋ฐฑ ์ฒด์ธ ๋ถ„๋ฆฌ(Breaking Change ๋ฐฉ์–ด), ์ตœ์‹  Claude Code TUI ๊ฐ์ง€ ํ† ํฐ ๋ฐ˜์˜, 27๊ฐœ ์‹ ๊ทœ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ ๋ฐ ๋งŒ์žฅ์ผ์น˜ PASS ๋‹ฌ์„ฑ. #### 1. `--herdr-workspace` ์˜ต์…˜ ๋„์ž… ๋ฐ Herdr ๋Ÿฐํƒ€์ž„ ๋ ˆ์ด๋ธ” ๋™๊ธฐํ™” - **CLI ์˜ต์…˜ ๋ฐ YAML ์ง๋ ฌํ™” ํ‘œ์ค€ํ™”**: - `create_session.sh`, `resume_session.sh`, `update_yaml_resumed.sh`, `stop_session.sh`์— `--herdr-workspace ` ํŒŒ์„œ ๋ฐ ํ™˜๊ฒฝ๋ณ€์ˆ˜(`HERDR_WORKSPACE`) ์ง€์› ์ถ”๊ฐ€. - `agent-sessions.yaml`์— `herdr_session`(์†Œ์ผ“๋ช…)๊ณผ `herdr_workspace`(์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ผ๋ฒจ)๋ฅผ ๊ฐ๊ฐ ๋…๋ฆฝ ํ•„๋“œ๋กœ ์˜๊ตฌ ์ง๋ ฌํ™”. - **Herdr ๋Ÿฐํƒ€์ž„ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ ˆ์ด๋ธ” ์‹ค์‹œ๊ฐ„ ์—ฐ๋™ (`lib.sh`, `resume_session.sh`)**: - `herdr workspace create` ํ˜ธ์ถœ ์‹œ `--label "$MAM_WS_LABEL"` ์ „๋‹ฌ ๋ฐ ๊ธฐ์กด ์›Œํฌ์ŠคํŽ˜์ด์Šค ์‚ฌ์šฉ ์‹œ `herdr workspace rename` ์ž๋™ ํ˜ธ์ถœ. - `resume_session.sh` ์‹คํ–‰ ์‹œ ์ €์žฅ๋œ `herdr_workspace`๋ฅผ ์ฝ์–ด Herdr ๋Ÿฐํƒ€์ž„ ๋ ˆ์ด๋ธ” ๋ณต์› ๋ณด์žฅ. #### 2. ๋ ˆ๊ฑฐ์‹œ ์†Œ์ผ“ ํด๋ฐฑ ์ฒด์ธ ๋ถ„๋ฆฌ ๋ฐ Breaking Change ์›์ฒœ ์ฐจ๋‹จ - **์†Œ์ผ“ vs ์›Œํฌ์ŠคํŽ˜์ด์Šค ํ•จ์ˆ˜ ์™„์ „ ๋ถ„๋ฆฌ (`lib.sh`)**: - `resolve_herdr_session()`: ๋ฐ๋ชฌ/์†Œ์ผ“ ์„ธ์…˜๋ช…๋งŒ ๋ฐ˜ํ™˜ (row `herdr_session` -> row `herdr_server` -> env -> slug). - `resolve_herdr_workspace()`: ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋ผ๋ฒจ๋งŒ ๋ฐ˜ํ™˜ (row `herdr_workspace` -> `pane.cwd` slug -> caller `ws` arg). - ๊ธฐ์กด ์ฝ”๋“œ๋ฒ ์ด์Šค 6๊ฐœ ์ง€์ (`lib.sh:1027`, `reconcile.sh:135, 399, 495`, `status.sh:145, 270`)์—์„œ ์†Œ์ผ“ ๊ฒ€์ƒ‰ ์‹œ `herdr_workspace`๋ฅผ ์˜ค์ธ ์ฐธ์กฐํ•˜๋˜ ๊ตฌ๋ฌธ์„ ์™„์ „ํžˆ ์ œ๊ฑฐ. - **์™ธ๋ถ€ ์„ธ์…˜ ์ž…์–‘(Drift-B) ๋ณด๊ฐ• (`reconcile.sh`)**: - ์™ธ๋ถ€ ์„ธ์…˜ ์ž…์–‘ ์‹œ `herdr_workspace`์™€ `herdr_server`๋ฅผ ์ž๋™ ์ฑ„๋ฒˆ ๋ฐ ์ง๋ ฌํ™”. #### 3. ์ตœ์‹  ์—์ด์ „ํŠธ TUI ์ค€๋น„ ๊ฐ์ง€ ๋ณด๊ฐ• (`claude.py`, `lib.sh`) - ์ตœ์‹  Claude Code(`v2.1.241`)์˜ ์‹œ์ž‘ ๋ฐฐ๋„ˆ(`Claude Code`, `Opus 5 with high effort` ๋“ฑ)๋ฅผ `ready_tokens`์— ์ถ”๊ฐ€ํ•˜์—ฌ ์„ธ์…˜ ์ƒ์„ฑ ํƒ€์ž„์•„์›ƒ ๋ฐฉ์ง€. --- ### ๐Ÿš€ `v2.1.0` โ€” 2xK Grid Layout Engine, Explicit Agent Standardization & Herdr Session Hardening (2026-08-24) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: 2xK ์šฐ์ธก ์„ฑ์žฅ ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ ์—”์ง„(`lib_py.layout`) ๊ตฌ์ถ•(B-20), ์ „ ์Šคํฌ๋ฆฝํŠธ `--agent` / `--herdr-session` ํ‘œ์ค€ํ™” ๋ฐ ์ „ํŒŒ ๊ฐ€๋“œ, ์ „์ฒด 346๊ฐœ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ 100% PASS ๋‹ฌ์„ฑ. #### 1. 2xK ์šฐ์ธก ์„ฑ์žฅ ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ ์—”์ง„ ๊ตฌ์ถ• (B-20 / I-2, I-3, C-1, J-1) - **์ˆœ์ˆ˜ ํŒŒ์ด์ฌ ๋ ˆ์ด์•„์›ƒ ์—”์ง„ ์‹ ์„ค (`lib_py/layout.py`)**: - `tput` ๊ธฐ๋ฐ˜ ํ„ฐ๋ฏธ๋„ ํฌ๊ธฐ ๋™์  ๊ฐ์ง€ ๋ฐ 2xK(2ํ–‰ ๊ณ ์ •, ์šฐ์ธก ์—ด ์ถ”๊ฐ€) ๊ทธ๋ฆฌ๋“œ ๊ธฐํ•˜ํ•™ ๊ณ„์‚ฐ ์—”์ง„ ๊ตฌํ˜„. - ํŒจ๋„ ๋ฒˆํ˜ธ ์ˆœ์„œ(0:์ขŒ์ƒ, 1:์ขŒํ•˜, 2:์ค‘์ƒ, 3:์ค‘ํ•˜...)์— ๋”ฐ๋ฅธ ์šฐ์ธก ํ™•์žฅ ํƒ€์ผ๋ง ๋ถ„ํ•  ๋ช…๋ น(`split-pane -h/-v`, `select-pane`) ๊ณ„์‚ฐ. - ํ—ค๋“œ๋ฆฌ์Šค/CI ์ตœ์†Œ ์ฐจ์›(์ตœ์†Œ ๋„ˆ๋น„ 60, ์ตœ์†Œ ๋†’์ด 20) ๊ฐ€๋“œ ๋ฐ `default=60` falsy-zero trap ํ•ด๊ฒฐ (`_env_int`). - **33๊ฐœ ์‹ ๊ทœ ๋ ˆ์ด์•„์›ƒ ๋‹จ์œ„/ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ๊ตฌ์ถ• (`tests/test_layout.py`)**: - 1~8๊ฐœ ํŒจ๋„ ์ˆ˜์‹ ๊ฒ€์ฆ, ๋น„์ •์ƒ ์ธ์ž/ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋ฐฉ์–ด, ๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€ ๊ฐ€๋“œ ๊ฒ€์ฆ. #### 2. ์—์ด์ „ํŠธ ์ธ์ž ํ‘œ์ค€ํ™” ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ž๋™ ์ถ”๋ก  - `stop_session.sh`, `create_session.sh`, `resume_session.sh`, `update_yaml_resumed.sh`, `resolve_session_id.sh` ์ „๋ฐ˜์— ๊ฑธ์ณ `--agent ` ๋ช…์‹œ์  ํ‘œ์ค€ํ™”. - ๋ฏธ์ง€์ • ์‹œ YAML ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ(`agent-sessions.yaml`) ๊ธฐ๋ฐ˜ ์—์ด์ „ํŠธ ํƒ€์ž… ์ž๋™ ์ถ”๋ก (`resolve_agent_type_from_registry`) ์—ฐ๋™. #### 3. `--herdr-session` ๊ฒฉ๋ฆฌ ์„ธ์…˜ ์˜ต์…˜ ํ‘œ์ค€ํ™” ๋ฐ ์ „ํŒŒ ๊ฐ€๋“œ - `create_session.sh`, `resume_session.sh`, `stop_session.sh`, `update_yaml_resumed.sh` ์ „๋ฐ˜์— `--herdr-session ` ํ‘œ์ค€ ์˜ต์…˜ํ™” (๋ ˆ๊ฑฐ์‹œ `--herdr-server` ์™„์ „ ํ˜ธํ™˜). - `create_session.sh`์—์„œ ๋ช…์‹œ์  ์„ธ์…˜๋ช…์ด ์›Œํฌ์ŠคํŽ˜์ด์Šค ์Šฌ๋Ÿฌ๊ทธ์— ์˜ํ•ด ๋ฎ์–ด์”Œ์›Œ์ง€์ง€ ์•Š๋„๋ก ๊ฐ€๋“œ ๋ณด๊ฐ•. - `resume_session.sh`์˜ post-spawn ์žฌ๊ฐœ ์‹œ ์‹ ๊ทœ Herdr ์„ธ์…˜๋ช…์ด YAML ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ์ •ํ™•ํžˆ ์ „ํŒŒ๋˜๋„๋ก ๊ฐฑ์‹  ๋กœ์ง ๋ฐ ์‹ ๊ทœ Tier 2 ํ…Œ์ŠคํŠธ 5๊ฑด ์ถ”๊ฐ€. #### 4. ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ํ™•์žฅ ๋ฐ ํ”ผ์–ด ๋ฆฌ๋ทฐ ์ „์› ๋งŒ์žฅ์ผ์น˜ PASS - ์ „์ฒด ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ์ˆ˜ **276๊ฑด โ†’ 346๊ฑด (100% PASS)** ํ™•์žฅ. - Multi-Agent Loop๋ฅผ ํ†ตํ•œ Reviewer(`claude`, `cline`) ์ „์› `[VERDICT: PASS]` ๊ฒ€์ฆ ์™„๋ฃŒ. --- ### ๐Ÿš€ `v2.0.0` โ€” Unified Agent Adapter Architecture & Herdr Standardization (2026-08-17) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: ์—์ด์ „ํŠธ ์ง€์‹ ๊ณ„์ธต ๋‹จ์ผ ์†Œ์Šคํ™”(A-4), ๋ ˆ๊ฑฐ์‹œ ๊ฒฉ๋ฆฌ ์™„์ „ ํ๊ธฐ(Option B), ์…ธ ๋ธŒ๋ฆฌ์ง€ ํ•˜๋“œ๋‹ ๋ฐ ์Šคํ‚ฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ทœ๊ฒฉํ™” ์™„๋ฃŒ. #### 1. ์—์ด์ „ํŠธ ์ง€์‹ ๊ณ„์ธต ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ (A-4 Phase 2 / P3-1) - **`BaseAgentAdapter` ์ถ”์ƒ ํด๋ž˜์Šค ๋ฐ 4๋Œ€ ์–ด๋Œ‘ํ„ฐ ๊ตฌ์ถ•**: - [`.agents/skills/lib_py/agents/base.py`](.agents/skills/lib_py/agents/base.py): `DiscoveryContext` ๋ฐ ์ถ”์ƒ ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜ (`ready_tokens`, `exit_key`, `delegate_agent_key`, `identity_cache_fields`, `artifact_path`, `verify_artifact`, `purge_artifacts`, `spawn_spec`, `resume_spec`, `auth_ok`, `discover`). - [`.agents/skills/lib_py/agents/adapters/`](.agents/skills/lib_py/agents/adapters/): `ClaudeAgentAdapter`, `AgyAgentAdapter`, `HermesAgentAdapter`, `ClineAgentAdapter` 4๊ฐœ ๊ตฌ์ฒด ํด๋ž˜์Šค ๊ตฌํ˜„. - **`facts` ๋ธŒ๋ฆฌ์ง€ ์…ธ ์ธํ„ฐํŽ˜์ด์Šค ํ•˜๋“œ๋‹**: - `lib_py.agents` CLI ๋ชจ๋“ˆ์„ ํ†ตํ•ด 8๊ฐœ `MAM_*` ๋ณ€์ˆ˜๋ฅผ `shlex.quote` ์•ˆ์ „ ์ธ์šฉ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ฐฉ์ถœ. - `wait_for_tui_ready` ๋นˆ ํ† ํฐ ์‹œ ์ „๋Ÿ‰ ๋งค์นญ ์˜คํƒ ๋ฐฉ์ง€ ๋ฐ ๋ฏธ์ง€ ์—์ด์ „ํŠธ fail-closed ๊ฐ€๋“œ ๋‚ด์žฅ. - **์…ธ ์Šคํฌ๋ฆฝํŠธ ์ „๋ฐ˜ ์–ด๋Œ‘ํ„ฐ ์ด๊ด€**: - `create_session.sh`, `resume_session.sh`, `stop_session.sh`, `reconcile.sh`์— ์‚ฐ์žฌ๋˜์–ด ์žˆ๋˜ 40์—ฌ ๊ฐœ ํ•˜๋“œ์ฝ”๋”ฉ ๋ถ„๊ธฐ๋ฅผ ์–ด๋Œ‘ํ„ฐ ํ˜ธ์ถœ๋กœ ์ผ์›ํ™”. #### 2. ๋ ˆ๊ฑฐ์‹œ `isolation.root` ๋ฐ C-3b ์†Œ๋น„์ž ์™„์ „ ํ๊ธฐ (Option B) - Universal Global Config ์ „ํ™˜ ์ดํ›„ ๋‚จ์•„์žˆ๋˜ 4๊ฐœ ๋ ˆ๊ฑฐ์‹œ ๊ฒฉ๋ฆฌ ์†Œ๋น„์ž ์ฝ”๋“œ(`lib.sh::mam_session_iso_root`, `workspace_uuid.py::iso_root_of`, `verify_session.py`, `stop_session.sh`) ๋ฐ `atomic_yaml.py`์˜ ๋ ˆ๊ฑฐ์‹œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ ˆ 100% ์‚ญ์ œ. - ์ €์žฅ์†Œ ๋‚ด ๊ฒฉ๋ฆฌ ์ž”์žฌ ์ฐธ์กฐ 0๊ฑด ๋‹ฌ์„ฑ. #### 3. ์…ธ ๋ธŒ๋ฆฌ์ง€ ๋ณด์•ˆ ๋ฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ฐ•ํ™” (R1, R2, N1 ๊ต์ •) - **R1 (์œ„์ž„ ์—์ด์ „ํŠธ ํ‚ค ํด๋ฐฑ ๋ณด๊ฐ•)**: ๋ธŒ๋ฆฌ์ง€ ๋ฏธ์ž‘๋™ ์‹œ `delegate_agent`๊ฐ€ `antigravity-cli`๋กœ ์ผ๊ด„ ํ‡ดํ™”ํ•˜์ง€ ์•Š๊ณ  `claude-code`, `hermes-agent`, `cline-agent`๋กœ ๋ช…์‹œ์  `case` ํด๋ฐฑํ•˜๋„๋ก ๊ฐœ์„ . - **R2 (`argv` ์„œ๋ธŒ์ปค๋งจ๋“œ ์ „ํ™˜)**: `python -c` ์…ธ ๋ณ€์ˆ˜ ๋ฌธ์ž์—ด ๋ณด๊ฐ„์„ `spawn-spec`, `resume-spec`, `exit-key` ์„œ๋ธŒ์ปค๋งจ๋“œ๋กœ ์ „๋ฉด ์ „ํ™˜ํ•˜์—ฌ ๊ณต๋ฐฑ/์ž‘์€๋”ฐ์˜ดํ‘œ ๊ฒฝ๋กœ ์—๋Ÿฌ ๋ฐ ์ฝ”๋“œ ์ฃผ์ž… ์œ„ํ˜‘ ์›์ฒœ ์ฐจ๋‹จ. - **N1 (ํด๋ฆฐ ํ™˜๊ฒฝ ๊ฒฉ๋ฆฌ ๊ฐ€๋“œ)**: `test_a4_adapter_contract.py` ๋‚ด CLI ํ…Œ์ŠคํŠธ๊ฐ€ ์•ฐ๋น„์–ธํŠธ `PYTHONPATH` ์—†์ด๋„ ๋…๋ฆฝ ํ†ต๊ณผํ•˜๋„๋ก ํ™˜๊ฒฝ ๊ฒฉ๋ฆฌ ๋ณด๊ฐ•. #### 4. ์Šคํ‚ฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ทœ๊ฒฉํ™” ๋ฐ ํ”ผ์–ด ๋ฆฌ๋ทฐ 100% PASS - 8๊ฐœ `SKILL.md` frontmatter `version: 2.0.0` ํ†ต์ผ ๋ฐ ๋ฐฐํฌ ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ. - Reviewer `cline` (Job `e7b9812b`) ๋ฐ Planner/Senior Reviewer `claude` (Job `31730364`) ์ „์› `[VERDICT: PASS]` ํš๋“. #### 5. ๋ ˆ๊ฑฐ์‹œ ์ฃผ์„ ๋ฐ ์‚ฌ์šฉ๋ฒ• ์ •ํ•ฉ์„ฑ ์ตœ์‹ ํ™” (C-6) - `stop_session.sh` ์ƒ๋‹จ ์ฃผ์„ ๋ฐ `usage()` ๋‚ด ํ๊ธฐ๋œ ํ”Œ๋ž˜๊ทธ(`--mode soft|hard`, `--capture-id`, `--graceful`) ์•ˆ๋‚ด ๋ฌธ๊ตฌ๋ฅผ ์™„์ „ ์ œ๊ฑฐํ•˜๊ณ  ํ˜„ํ–‰ 4๋Œ€ ์—์ด์ „ํŠธ(`claude`, `agy`, `hermes`, `cline`) ๋ฐ ํ”Œ๋ž˜๊ทธ ์ฒด๊ณ„๋กœ ๋™๊ธฐํ™”. - ํšŒ๊ท€ ๋ฐฉ์ง€ ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ(`test_comp_stop_usage_matches_parser`) ์‹ ์„ค. - ํšŒ๊ท€ ๋ฐ ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ: **263/263 PASS (100%)** ๋‹ฌ์„ฑ. #### 6. macOS NFS ๊ฐ์ง€ `df -P` ํด๋ฐฑ ๊ฒ€์ฆ ๋ฐ ์ข…๊ฒฐ (B-5) - `_check_is_nfs`(`lib.sh`)์˜ macOS/BSD ํ™˜๊ฒฝ ๋‚ด GNU ์ „์šฉ `df --output` ๊ตฌ๋ฌธ ์˜ค๋ฅ˜ ์‹œ POSIX `df -P` ํด๋ฐฑ ๋™์ž‘์„ ์‹ค์ธก ๋ฐ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ(`test_stop_check_is_nfs_local`)๋กœ ๊ฒ€์ฆ ์™„๋ฃŒํ•˜์—ฌ B-5 ์ด์Šˆ๋ฅผ ์ •์‹ ์ข…๊ฒฐ. #### 7. `agent_identities` tier-3 ์‹ ์› ์บ์‹œ ์™„์ „ ์ œ๊ฑฐ ๋ฐ UUID ํ•ด๊ฒฐ ๊ฒฝ๋กœ PyYAML ํƒˆ์˜์กด (B-10 / Option A) - ์“ฐ๊ธฐ ๊ฒฝ๋กœ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์•„ ๊ตฌ์กฐ์ ์œผ๋กœ ํžˆํŠธ ๋ถˆ๊ฐ€์˜€๋˜ tier-3 ํด๋ฐฑ๊ณผ ๋ถ€์† ์†Œ๋น„์ž(`workspace_uuid.py`, `reconcile.sh` drift D, `stop_session.sh` ์บ์‹œ ์†Œ๊ฑฐ)๋ฅผ ์ „๋ฉด ์‚ญ์ œ. - UUID ํ•ด๊ฒฐ ๊ฒฝ๋กœ๋ฅผ **tier-1(per-row own id) โ†’ tier-2(์–ด๋Œ‘ํ„ฐ `discover()`)** 2๋‹จ๊ณ„๋กœ ๋‹จ์ˆœํ™”. - `verify_session.py::mam_orchestrator_uuids` ์˜ ์ฆ‰์‹œ `yaml` import ๋ฅผ YAML ํด๋ฐฑ ๋ถ„๊ธฐ๋กœ ์ด๋™, UUID ํ•ด๊ฒฐ ๊ฒฝ๋กœ๊ฐ€ PyYAML ์—†์ด ์™„์ฃผํ•จ์„ ์‹คํ–‰ ๊ฐ€๋“œ๋กœ ๊ณ ์ •(`atomic_yaml.py` ์˜ ์‹œ์Šคํ…œ PyYAML ์š”๊ตฌ๋Š” ์„ค๊ณ„์ƒ ์œ ์ง€). - ํšŒ๊ท€ ๊ฐ€๋“œ 3์ข… ์‹ ์„ค โ€” ์ฝ๊ธฐ ๊ฒฝ๋กœ ๋ถ€ํ™œ ์ฐจ๋‹จ, `import yaml` AST ๊ฒ€์‚ฌ(์ง€์—ฐ import ํฌํ•จ), ์‹คํ–‰ ๊ฒฝ๋กœ ๊ฒ€์ฆ. - ํšŒ๊ท€ ๋ฐ ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ: **266/266 PASS (100%)** ๋‹ฌ์„ฑ. #### 8. ์…€ํ”„ ํ˜ธ์ŠคํŒ… ๋ฃจํ”„ ๋Ÿฐํƒ€์ž„ ํ”„๋ฆฌ์ฆˆ ์Šค๋ƒ…์ƒท (B-13 / Stage 2) - ๋ฃจํ”„ ๊ธฐ๋™ ์‹œ `.agents/skills/` ๋ฅผ `$TMPDIR` ์— 1ํšŒ ๋™๊ฒฐํ•˜๊ณ  ์Šค๋ƒ…์ƒท์—์„œ ์žฌ์‹คํ–‰ํ•˜์—ฌ, ํ„ด ๋„์ค‘ ํ”„๋ ˆ์ž„์›Œํฌ ์Šคํ‚ฌ ํŽธ์ง‘์ด ์ง„ํ–‰ ์ค‘์ธ ๋ฃจํ”„๋ฅผ ๊นจ๋œจ๋ฆฌ์ง€ ๋ชปํ•˜๋„๋ก ์ฐจ๋‹จ. - ์ฝ”๋“œ ๋ฃจํŠธ(์Šค๋ƒ…์ƒท)์™€ ์ƒํƒœ ๋ฃจํŠธ(`MAM_REAL_ROOT`)๋ฅผ ๋ถ„๋ฆฌํ•ด ๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท๋ฃจํ”„ ๋ฝยทdiff ์ˆ˜์ง‘์€ ์‹ค์ œ ์ €์žฅ์†Œ๋ฅผ ๊ณ„์† ์‚ฌ์šฉ. - ์žฌ์‹คํ–‰ ์‹œ ์›๋ณธ argv ๋ฅผ ๋ฐฐ์—ด๋กœ ๋ณด์กดํ•ด ์ธ์ž ์œ ์‹ค์„ ๋ฐฉ์ง€(ํŒŒ์„œ๊ฐ€ `$@` ๋ฅผ ์†Œ๋น„ํ•˜๋ฏ€๋กœ ํ•„์ˆ˜). - ์Šค๋ƒ…์ƒท ์ƒ์„ฑ ์‹คํŒจ ์‹œ `log_*` ์ •์˜ ์ด์ „ ๊ตฌ๊ฐ„์ž„์„ ๊ณ ๋ คํ•ด `echo` ๋กœ ๊ฒฝ๊ณ ํ•˜๊ณ  ๋ฏธ๋™๊ฒฐ ์ง„ํ–‰. - `MAM_LOOP_NO_FREEZE=1` ๋กœ ๋น„ํ™œ์„ฑํ™” ๊ฐ€๋Šฅ. ์Šค๋ƒ…์ƒท ์ƒ์„ฑ ์‹คํŒจ๋Š” ๊ฒฝ๊ณ  ํ›„ ๊ธฐ์กด ๋™์ž‘์œผ๋กœ ํด๋ฐฑ. - ํšŒ๊ท€ ๊ฐ€๋“œ 5์ข… ์‹ ์„ค โ€” argv ๋ณด์กด, ํŒŒ์† ๋ž˜ํผ ๋ฉด์—ญ, ์Šคํ‚ฌ ํŠธ๋ฆฌ ๋ฌด์˜ค์—ผ(B-6 ๊ฒฝ๊ณ„), ๋ฝ ํ•ด์ œ ๋ฐ ์Šค๋ƒ…์ƒท ์ •๋ฆฌ(B-12 ๊ฒฝ๊ณ„), ๋น„ํ™œ์„ฑํ™” ์Šค์œ„์น˜. - ํšŒ๊ท€ ๋ฐ ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ: **271/271 PASS (100%)** ๋‹ฌ์„ฑ. #### 9. ๊ฐ์‚ฌ ๋กœ๊ทธ ๋ฃจํŠธ ์ง€์—ฐ ํ‰๊ฐ€ (B-9 / P4-1) - `mqtt_common.LOGS_DIR` ์˜ import ์‹œ์  cwd ๊ณ ์ •์„ ์ œ๊ฑฐํ•˜๊ณ  ํ˜ธ์ถœ ์‹œ์ ์— ํ•ด์„ํ•˜๋Š” `get_logs_dir()` ๋ฅผ ๋„์ž…. `chdir` ์ดํ›„์—๋„ ๊ฐ์‚ฌ ๋กœ๊ทธ๊ฐ€ ํ˜„์žฌ ์›Œํฌ์ŠคํŽ˜์ด์Šค์— ์ •ํ™•ํžˆ ๊ธฐ๋ก๋จ. - PEP 562 ๋ชจ๋“ˆ `__getattr__` ๋กœ `LOGS_DIR` ์†์„ฑ ์ ‘๊ทผ ํ•˜์œ„ ํ˜ธํ™˜ ์œ ์ง€(๋™์  ํ‰๊ฐ€). - PEP 562 `__dir__` ๋ณ‘ํ–‰ ์ •์˜๋กœ `dir()`ยทํƒญ ์™„์„ฑ ๊ฐ€์‹œ์„ฑ ์œ ์ง€. - `DELEGATE_JOB_LOGS_DIR` ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์‹คํ–‰ ์ค‘ ๋ณ€๊ฒฝ๊นŒ์ง€ ๋ฐ˜์˜. - ํšŒ๊ท€ ๊ฐ€๋“œ 5์ข… ์‹ ์„ค โ€” cwd ์ถ”์ข…, ์‹ค์ œ ํŒŒ์ผ ์ƒ์„ฑ, ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋™์  ๋ฐ˜์˜, ์ „์—ญ ์žฌ๋„์ž… ์ฐจ๋‹จ, dir() ํƒ์ƒ‰์„ฑ. - ํšŒ๊ท€ ๋ฐ ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ: **276/276 PASS (100%)** ๋‹ฌ์„ฑ. --- ### ๐Ÿ› ๏ธ `v1.4.0` โ€” Stability, Cleanup & Safe Job Delegation (2026-08-16) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: ๊ฒฉ๋ฆฌ ์ž”์žฌ ์ •๋ฆฌ, ์„œ๋ธŒ์…ธ ๋ฃจํ”„ ๋ฝ ์กฐ๊ธฐ ํ•ด์ œ ๋ฒ„๊ทธ ํ”ฝ์Šค, ์‹ ๊ทœ ํŒŒ์ผ ์บก์ฒ˜ ๋ฐ ๊ฒฝ๋Ÿ‰ํ™”. - **P2-2 (C-3a / C-4 ๋ ˆ๊ฑฐ์‹œ ๊ฒฉ๋ฆฌ ์Šคํ… ๋ฐ ๋ฏธ์‚ฌ์šฉ ์‹ฌ๋ณผ ์ œ๊ฑฐ)**: - `lib.sh` ๋‚ด ๋นˆ ์Šคํ… 4์ข…(`provision_isolation`, `isolation_lever`, `isolation_env_prefix`, `isolation_cmd_args`) ๋ฐ `_REAL_HERDR_PATH` ์™„์ „ ์‚ญ์ œ. - `registry.py::TERMINAL_STATUSES` ๋ฐ `create_session.sh::ISOLATE` ์ œ๊ฑฐ. - **P2-1 (B-6 / B-12 `delegate_job_safe` ์•ˆ์ •ํ™”)**: - `.agents/skills/...` ๋‚ด ๋ถˆํ•„์š”ํ•œ `.tmp` ๋ณต์‚ฌ๋ณธ ์ƒ์„ฑ ์ œ๊ฑฐ ๋ฐ ์ธํ”Œ๋ ˆ์ด์Šค ์ง์ ‘ ์‹คํ–‰(`bash "$orig_script"`) ์ „ํ™˜. - ์„œ๋ธŒ์…ธ ๋‚ด `trap`์œผ๋กœ ์ธํ•œ ๋ฃจํ”„ ๋ฝ ๋งˆ์ปค(`.mam/loop-guard-active`) ์กฐ๊ธฐ ์‚ญ์ œ ๊ฒฐํ•จ(D1) ์›์ฒœ ์ฐจ๋‹จ. - **P0-2 (O-2 ์ค‘๋ณต ๋ฃจํ”„ ๊ธฐ๋™ ๋ฐฉ์ง€ ์›์ž์  ๋ฝ)**: - `loop_lock.sh` ์‹ ์„ค: `set -C` ๊ธฐ๋ฐ˜ ์›์ž์  ๋ฝ ํš๋“ ๋ฐ PID + lstart ์†Œ์œ ๊ถŒ ๊ฒ€์ฆ์œผ๋กœ ๋™์‹œ ์‹คํ–‰ ๋ฐฉ์ง€. - **P0-1 (B-7 ์™ธ๊ณฝ diff ์ˆ˜์ง‘ ๋ฐ ๋ฏธ์ถ”์  ํŒŒ์ผ ์บก์ฒ˜)**: - `diff_collect.sh` ๋„์ž…: CWD ๋…๋ฆฝ `$REPO_ROOT` ๊ธฐ์ค€ diff ์ˆ˜์ง‘ ๋ฐ `git ls-files -o` ๋ฏธ์ถ”์  ํŒŒ์ผ ๋ณ‘ํ•ฉ. - **B-4 (์‹œํ”„ํŠธ `ls` ๋™์  ํฌ์‹œ์Šค ํƒ€์ž„์Šคํƒฌํ”„ ๋ณต์›)**: - `lib.sh` 554ํ–‰์˜ `created=999999` ํ•˜๋“œ์ฝ”๋”ฉ์„ ์‹ค์‹œ๊ฐ„ ํƒ€์ž„์Šคํƒฌํ”„(`int(time.time())`)๋กœ ๋ณต์›. - **ํ…Œ์ŠคํŠธ ์ŠˆํŠธ ๊ฒฝ๋Ÿ‰ํ™”**: - ๋…ธํ›„ํ™”๋œ ์ค‘๋ณต ๋ ˆ๊ฑฐ์‹œ ํ…Œ์ŠคํŠธ 7๊ฐœ ํŒŒ์ผ(1,559์ค„) ์‚ญ์ œ (`cf51b2c`). --- ### ๐Ÿ›ก๏ธ `v1.3.0` โ€” Orchestrator Onboarding & Scoped Guarding (2026-08-15) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ์‹ ์› ๊ฒฉ๋ฆฌ ๋ฐ ๋‹ค์ค‘ ์—์ด์ „ํŠธ ํ˜‘์—… ๊ฐ€๋“œ๋ ˆ์ผ ํ™•๋ฆฝ. - **`multi-agent-mux-orc-onboard` ์Šคํ‚ฌ ์‹ ์„ค**: - ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ(`agy`)์˜ ๋Œ€ํ™” UUID๋ฅผ `.mam/agent-sessions.yaml` ๋‚ด `orchestrator_uuids` ๋ฆฌ์ŠคํŠธ๋กœ ๋“ฑ๋ก. - `find_workspace_uuid` ๋ฐ `verify_session_uuid`์—์„œ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ UUID๋ฅผ ์Šคํ‚ตํ•˜์—ฌ ์„œ๋ธŒ์—์ด์ „ํŠธ ์„ธ์…˜ ์˜ค์—ผ ๋ฐฉ์ง€. - **O-3 (์กฐ๊ฑด๋ถ€ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ์œ„์ž„ ๊ฐ€๋“œ โ€” Scoped Guard)**: - ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๊ฐ€ `/multi-agent-mux-loop` ํ™œ์„ฑํ™” ์ƒํƒœ์—์„œ ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ์Šคํฌ๋ฆฝํŠธ๋กœ ์œ„์ž„ํ•˜๋„๋ก ํ†ต์ œ. - `AGENTS.md` ยง5 ๋ฐ `MULTI_AGENT_RULES.md` ๋‚ด ๊ฐ€๋“œ๋ ˆ์ผ ๋ช…์‹œ. - **๋ฐฐํฌ ๋ฐ ํŒจํ‚ค์ง• ํŒŒ์ดํ”„๋ผ์ธ ํ˜„๋Œ€ํ™”**: - `deploy/lib_ownership.sh` ์‹ ์„ค ๋ฐ `hooks.json` 3-way ๋ณ‘ํ•ฉ(`MERGE_REGISTRY`) ์ง€์›. - Gitea CI/CD ํŒŒ์ดํ”„๋ผ์ธ (`deploy/gitea-ci.yml`) ์—ฐ๋™. --- ### ๐Ÿ”Œ `v1.2.0` โ€” Universal Herdr Server Isolation & Cline Integration (2026-08-14) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: Herdr ๋‹จ์ผ ์„œ๋ฒ„ ๊ฒฉ๋ฆฌ ๋ฐ ๋‹ค์ค‘ AI ์—์ด์ „ํŠธ ํ™•์žฅ. - **Universal Herdr Session Isolation**: - `HERDR_SESSION_NAME` ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฒฉ๋ฆฌ ์„œ๋ฒ„๋ฅผ ํ†ต์ผํ•˜์—ฌ ํ”„๋กœ์„ธ์Šค ์ถฉ๋Œ ๋ฐฉ์ง€. - **Cline ์—์ด์ „ํŠธ ํ†ตํ•ฉ**: - `cline` CLI ๊ธฐ๋ฐ˜ ๋Œ€ํ™” ์„ธ์…˜ ์ƒ์„ฑ, ์ •์ง€, ๋ณต์› ๋ฐ TUI ๋ ˆ๋”” ํ† ํฐ ํ•ธ๋“ค๋ง ์ง€์›. - **SQLite WAL ํŠธ๋žœ์žญ์…˜ ๋™์‹œ์„ฑ**: - ์„ธ์…˜ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋™์‹œ ์“ฐ๊ธฐ ์‹œ ๋ฐœ์ƒํ•˜๋Š” ๋ฝ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด SQLite WAL ๋ชจ๋“œ ์ „๋ฉด ์ ์šฉ. --- ### ๐Ÿงฑ `v1.0.0` ~ `v1.1.0` โ€” Initial Multi-Agent Mux Framework (2026-08-10 ~ 2026-08-13) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: ํ„ฐ๋ฏธ๋„ ๋‹ค์ค‘ ์—์ด์ „ํŠธ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ๊ธฐ์ดˆ ์„ค๊ณ„ ๋ฐ ๋น„๋™๊ธฐ ๋ฃจํ”„ ์™„์„ฑ. - **ํ•ต์‹ฌ ์ˆ˜๋ช…์ฃผ๊ธฐ ์Šคํ‚ฌ๊ตฐ ๊ตฌ์ถ•**: `create`, `stop`, `resume`, `status`, `monitor` ์Šคํ‚ฌ ๊ธฐ๋ณธ ๊ตฌํ˜„. - **MQTT ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ์žก ์œ„์ž„**: `multi-agent-mux-delegate-job`์„ ํ†ตํ•œ ์—์ด์ „ํŠธ ๊ฐ„ ์ด๋ฒคํŠธ ํ†ต์‹  ๋ฐ ๊ฒฐ๊ณผ ๊ตฌ๋…. - **์ž์œจ ํ˜‘์—… ๋ฃจํ”„**: `multi-agent-mux-loop` ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ†ตํ•œ Planner-Creator-Reviewer ์—ญํ•  ๋ถ„๋‹ด ์ฒด๊ณ„ ์ •๋ฆฝ. --- ## ๐Ÿงช ํ’ˆ์งˆ ๋ณด์ฆ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (Verification Standards) ๋ชจ๋“  ๋ฆด๋ฆฌ์Šค๋Š” ๋‹ค์Œ 4๋‹จ๊ณ„ ์—„๊ฒฉํ•œ ๊ฒ€์ฆ์„ ํ†ต๊ณผํ•ด์•ผ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค: 1. **์ •์  ๋ฌธ๋ฒ• ๊ฒ€์‚ฌ**: `bash -n` (๋ชจ๋“  ์…ธ ์Šคํฌ๋ฆฝํŠธ) ๋ฐ AST ๋ฏธ์‚ฌ์šฉ ์ฝ”๋“œ ๋ถ„์„. 2. **๋‹จ์œ„ ๋ฐ ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ (Tier 1~2)**: ์ธํ”Œ๋ ˆ์ด์Šค ๋ฐ ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ์ƒํ˜ธ์ž‘์šฉ ๊ฒ€์ฆ. 3. **ํ†ตํ•ฉ ๋ฐ ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ (Tier 3~4 / Contract)**: clean environment (`env -u PYTHONPATH`) ํ•˜์—์„œ์˜ ์–ด๋Œ‘ํ„ฐ ๊ณ„์•ฝ ๋ฐ CLI ๋ธŒ๋ฆฌ์ง€ ๊ฒ€์ฆ. 4. **๋ฉ€ํ‹ฐ์—์ด์ „ํŠธ ๊ต์ฐจ ํ”ผ์–ด ๋ฆฌ๋ทฐ**: Planner(`claude`) ๋ฐ Reviewer(`cline`) ๊ฐ„ ๊ต์ฐจ ๊ฒ€์ฆ ๋ฐ `[VERDICT: PASS]` 100% ํ•ฉ์˜.