# ๐Ÿ” Review Report โ€” PR #37 ๋ธํƒ€ ๋ฆฌ๋ทฐ 2ํšŒ์ฐจ (job `1a9fbbc4`) - **Reviewer**: `planner-reviewer-claude-01` (role: planner,reviewer) ยท 2026-09-24 - **๋Œ€์ƒ ๋ธํƒ€**: `59be1c8..708262a` (1์ปค๋ฐ‹, 3ํŒŒ์ผ `+63/โˆ’7`, `git diff --check` ํด๋ฆฐ), base `main 1debc7d` (v4.7.2). - **๊ฒ€์ฆ ๊ฒฝ๋กœ**: `/var/folders/.../T/mam-review/loop-20260924T085626Z-b8987e/1`. **identity ํ™•์ธ**: `HEAD = 708262a5โ€ฆ1e30`, `HEAD^{tree} = d601c2d0โ€ฆ5b96` โ€” ๋ธŒ๋ฆฌํ”„์™€ ์ผ์น˜. - **1ํšŒ์ฐจ ๋‚ด ํŒ์ •**: PASS + H1~H5 (job `4ab12ae2`). ์ด๋ฒˆ์—๋Š” H1(๋Ÿฌ๋„ˆ ์“ฐ๊ธฐ ์‹คํŒจ)ยทH3(๋นˆ ํŒŒ์ผ)ยทS9 ์ด์‹์„ฑ์˜ ์ •์ •์„ ๋ณธ๋‹ค. - **์•ˆ์ „ ๊ทœ์น™ ์ค€์ˆ˜**: ๊ฒ€์ฆ ๊ฒฝ๋กœยท์†Œ์Šค ์›ŒํฌํŠธ๋ฆฌยท๋ณธ ์ฒดํฌ์•„์›ƒ(`main 1debc7d`) ๋ชจ๋‘ ์ˆ˜์ •ยท์ปค๋ฐ‹ยทcheckout ์—†์Œ. ๋Ÿฌ๋„ˆ ์‹คํ—˜์€ in-process ์Šคํ…, ๋ž˜ํผ๋Š” `--dry-run`+`--agent human`+๋„๋‹ฌ ๋ถˆ๊ฐ€ ๋ธŒ๋กœ์ปค, ๋˜๋Œ๋ฆฐ ์‚ฌ๋ณธ์€ scratchpad. ์‹ค์ œ herdrยท์„ธ์…˜ยท`.mam`ยท๋ฐ๋ชฌ ์กฐ์ž‘ ์—†์Œ, pushยทmergeยทPR ์ƒํƒœ ๋ณ€๊ฒฝ ์—†์Œ. ## ๊ฒฐ๋ก  ์š”์•ฝ **์„ธ ์ •์ • ๋ชจ๋‘ ์ง€์ ์„ ๋‹ซ๊ณ , 1ํšŒ์ฐจ ํŒ์ •์„ ๋’ค์ง‘์„ ์š”์†Œ๋Š” ์—†๋‹ค.** ๋‚ด๊ฐ€ 1ํšŒ์ฐจ์—์„œ H1 ์„ ์žฌํ˜„ํ–ˆ๋˜ ๋ฐ”๋กœ ๊ทธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ •์ •๋œ ๋Ÿฌ๋„ˆ์— ๋‹ค์‹œ ๋Œ๋ ค "์‹คํŒจ ์ œ์ถœยท๋ถ€๋ถ„ ํŒŒ์ผ ์—†์Œยท๋ž˜ํผ ๋ฏธ์‹คํ–‰"์„ ํ™•์ธํ–ˆ๊ณ , ์ •์ƒยทexec ์‹คํŒจยท์“ฐ๊ธฐ ์‹คํŒจ ์„ธ ๊ฒฝ๋กœ์—์„œ ํŒŒ์ผ์ด **์ •ํ™•ํžˆ ํ•œ ๋ฒˆ**๋งŒ ์ง€์›Œ์ง€๋Š” ๊ฒƒ๋„ ํ™•์ธํ–ˆ๋‹ค. ๋น„์ฐจ๋‹จ ๊ด€์ฐฐ 1๊ฑด(I1). ## 1. R1/H1 โ€” ๋Ÿฌ๋„ˆ์˜ ์“ฐ๊ธฐ ์‹คํŒจ (ํ•ญ๋ชฉ 1) `runner.py:472-491`: `mkdir`ยท`NamedTemporaryFile`ยท`write` ๊ฐ€ ํ•˜๋‚˜์˜ `try` ๋กœ ๋“ค์–ด๊ฐ€๊ณ , `prompt_file` ์€ `""` ๋กœ ์ดˆ๊ธฐํ™”๋œ ๋’ค ํŒŒ์ผ์ด ์ƒ๊ธฐ๋Š” ์ฆ‰์‹œ(`write` ์ด์ „) ์ด๋ฆ„์„ ๋ฐ›๋Š”๋‹ค. `except OSError` ๋Š” ์ด๋ฆ„์ด ์žˆ์œผ๋ฉด ์ง€์šฐ๊ณ  `CompletedProcess(โ€ฆ, 126, "", "cannot write the prompt file under : (prompt N bytes)")` ๋ฅผ ๋Œ๋ ค์ค€๋‹ค. exec ์šฉ `try/finally`(`:504-516`)๋Š” ๊ทธ ๋’ค์— ๋”ฐ๋กœ ์žˆ์–ด ๋‘ ๋ธ”๋ก์ด ๊ฒน์น˜์ง€ ์•Š๋Š”๋‹ค. ### ์‹ค์ธก (๊ฒ€์ฆ ๊ฒฝ๋กœ ๋Ÿฌ๋„ˆ in-process) | ๊ฒฝ๋กœ | ๊ฒฐ๊ณผ | |---|---| | **์“ฐ๊ธฐ ์‹คํŒจ**(1ํšŒ์ฐจ H1 ์žฌํ˜„ ์Šคํฌ๋ฆฝํŠธ ๊ทธ๋Œ€๋กœ: `write` ๊ฐ€ `OSError(28)`) | `delegate_job` ์ด **None** ์„ ๋ฐ˜ํ™˜(์˜ˆ์™ธ ์—†์Œ), ๋กœ๊ทธ `cannot write the prompt file under โ€ฆ: [Errno 28] No space left on device (prompt 5 bytes)`, `.mam` ์ž”์กด **0**, ๋ž˜ํผ ํ˜ธ์ถœ **0** | | ์ •์ƒ | ๋ž˜ํผ ํ˜ธ์ถœ ์‹œ์ ์— ํŒŒ์ผ ์กด์žฌยท๋‚ด์šฉ `'hello'`ยท๊ถŒํ•œ **0600**, `os.unlink` ํ˜ธ์ถœ **1ํšŒ**, ์ž”์กด 0 | | exec ์‹คํŒจ(`subprocess.run` ์ด `OSError(2)`) | rc 126 ๊ฒฝ๋กœ, `os.unlink` ํ˜ธ์ถœ **1ํšŒ**, ์ž”์กด 0, ๋ฐ˜ํ™˜ None | - ๋ฐ˜ํ™˜ํ•˜๋Š” `CompletedProcess` ๋Š” exec ์‹คํŒจ์™€ ๊ฐ™์€ rc 126 ์ด๋ผ ๊ธฐ์กด ์ฒ˜๋ฆฌ(`submit failed (exit 126)` ๋กœ๊ทธ, busy ์žฌ์‹œ๋„ ์—†์Œ, id ์—†์Œ โ†’ `failed_submit_jobs` ๋ฏธ๋“ฑ๋กยทNone ๋ฐ˜ํ™˜ โ†’ ํ˜ธ์ถœ์ž `return 1`)์— ๊ทธ๋Œ€๋กœ ํ•ฉ๋ฅ˜ํ•œ๋‹ค. S4c ๊ฐ€ `submit failed (exit 126)`ยท`cannot write the prompt file`ยท`Traceback` ๋ถ€์žฌยท๋ž˜ํผ ๋ฏธ์‹คํ–‰ยท์ž”์กด ์—†์Œ์„ ํ•œ๊บผ๋ฒˆ์— ๋‹จ์–ธํ•œ๋‹ค. - `NamedTemporaryFile` ์ƒ์„ฑ ์ž์ฒด๊ฐ€ ์‹คํŒจํ•˜๋ฉด `prompt_file` ์ด ๋นˆ ์ฑ„๋กœ `except` ์— ๋“ค์–ด๊ฐ€ `unlink` ๋ฅผ ๊ฑด๋„ˆ๋›ด๋‹ค โ€” ์ง€์šธ ๊ฒƒ์ด ์—†์œผ๋‹ˆ ๋งž๋‹ค. `mkdir` ์‹คํŒจ๋„ ๊ฐ™์€ ๋ธ”๋ก์ด๋‹ค. ## 2. H3 โ€” ๋นˆ `--prompt-file` ๊ฑฐ๋ถ€ (ํ•ญ๋ชฉ 2) `multi-agent-mux-delegate-job:193` ์˜ `[[ -s "$PROMPT_FILE" ]]` ๋Š” **๋ž˜ํผ `submit` ์˜ ์ž…๋ ฅ**์—๋งŒ ๊ฑธ๋ฆฐ๋‹ค. ์ •๋‹นํ•œ ๋นˆ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ์ฒ˜๋ฅผ ๋ง‰๋Š”์ง€ ์‹ค์ธก: | ๊ฒฝ๋กœ | ๊ฒฐ๊ณผ | |---|---| | ๋ž˜ํผ `submit --prompt-file empty.md` | `submit: --prompt-file <์ ˆ๋Œ€๊ฒฝ๋กœ> is empty`, rc 1 (S1e) | | ๋ž˜ํผ `submit --prompt-file newline.md`(๊ฐœํ–‰ 1๋ฐ”์ดํŠธ) | ๋“ฑ๋ก๋จ โ€” `-s` ๋Š” ๋ฐ”์ดํŠธ ํฌ๊ธฐ ๊ธฐ์ค€์ด๋ผ ๊ณต๋ฐฑ๋งŒ ์žˆ๋Š” ํŒŒ์ผ์€ ํ†ต๊ณผํ•œ๋‹ค(I1) | | ๋ž˜ํผ `submit --prompt ""` | ์ข…์ „๋Œ€๋กœ `submit requires --prompt or --prompt-file` ๊ฑฐ๋ถ€ โ€” ํ…์ŠคํŠธ ํ˜•์‹๊ณผ ๋Œ€์นญ | | `registry.py register --prompt-file empty.md`(์ง์ ‘) | ๋“ฑ๋ก๋จ(rc 0) โ€” ๋“ฑ๋ก๋ถ€๋Š” ๊ทธ๋Œ€๋กœ | | loop ํ˜• ํšŒ์ฐจ์˜ `update --prompt-file "$job_dir/prompt.md"` | `cmd_submit` ์„ ๊ฑฐ์น˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฒ€์‚ฌ ๋ฐ–(๋ณ€๊ฒฝ ์—†์Œ) | ์ฆ‰ ์ƒˆ ๊ฒ€์‚ฌ๋Š” "๋นˆ ํ…์ŠคํŠธ๋ฅผ ๊ฑฐ๋ถ€ํ•˜๋˜" ๊ธฐ์กด ๊ทœ์น™์„ ํŒŒ์ผ ํ˜•์‹์œผ๋กœ ํ™•์žฅํ•œ ๊ฒƒ์ด๊ณ , ๋“ฑ๋ก๋ถ€ยทloop ํšŒ์ฐจ ๊ฒฝ๋กœ๋Š” ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. ๋ง‰ํžˆ๋Š” ์ •๋‹นํ•œ ์‚ฌ์šฉ์ฒ˜๋Š” ์—†๋‹ค. ## 3. R2/N1 โ€” S9 ์˜ macOS ์ด์‹์„ฑ `sys.platform == "linux"` ์ผ ๋•Œ๋งŒ `File too large` ๋ฅผ ๋‹จ์–ธํ•˜๊ณ , rcโ‰ 0ยท`prompt-shloop-*` ์ž”์กด ์—†์Œยท`delegate_job_safe: cannot write the prompt file` ์€ ๋ชจ๋“  ํ”Œ๋žซํผ์—์„œ ๋‹จ์–ธํ•œ๋‹ค. 1ํšŒ์ฐจ์—์„œ ๋‚ด๊ฐ€ ํ•˜๋„ค์Šค๋ฅผ ์ง์ ‘ ๋Œ๋ ค ํ™•์ธํ•œ macOS ๋™์ž‘(rc 1ยท์ž”์กด ์—†์Œยท์›์ธ ๋ฌธ๊ตฌ ์ถœ๋ ฅ)๊ณผ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ๊ณ„์•ฝ์ด๋‹ค. ## 4. ํ…Œ์ŠคํŠธ (ํ•ญ๋ชฉ 1ยท3) ### 4-1. ์ˆ˜์ • ์—†์ด๋Š” ์‹คํŒจํ•˜๋Š”๊ฐ€ โ€” ํ™•์ธํ•จ ๊ฒ€์ฆ ๊ฒฝ๋กœ ์‚ฌ๋ณธ์—์„œ `runner.py` ์™€ ๋ž˜ํผ๋งŒ `59be1c8` ํŒ์œผ๋กœ ๋˜๋Œ๋ฆฌ๊ณ  `-k "s4c or s1e or s9 or s4 or s1"` ์„ ๋Œ๋ ธ๋‹ค: ``` 2 failed, 6 passed, 1 skipped in 3.11s FAILED test_s4c_a_prompt_file_that_cannot_be_written_is_a_failed_submit โ† H1 FAILED test_s1e_an_empty_prompt_file_is_refused โ† H3 ``` S9 ๋Š” ๋˜๋Œ๋ฆฐ ์‚ฌ๋ณธ์—์„œ๋„ ํ†ต๊ณผํ•œ๋‹ค โ€” `run_loop.sh` ์ชฝ ์ˆ˜์ •์€ ์ด๋ฏธ `59be1c8` ์— ์žˆ์—ˆ๊ณ  ์ด๋ฒˆ ๋ธํƒ€๋Š” ํ…Œ์ŠคํŠธ์˜ ๋‹จ์–ธ๋งŒ ์ด์‹ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ฐ”๊ฟจ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค(macOS ์—์„œ ํ†ต๊ณผ = R2 ์ •์ • ํ™•์ธ). S1/S1c/S1d/S4/S4b ๋Š” ์–‘์ชฝ ๋ชจ๋‘ ํ†ต๊ณผ(๋ถˆ๋ณ€ ์กฐ๊ฑด). ### 4-2. ๊ฒ€์ฆ ๊ฒฝ๋กœ์—์„œ์˜ ์‹คํ–‰ ``` tests/test_issue36_prompt_transport.py tests/test_issue5_phase1_delegation.py tests/test_loop_cli.py tests/test_issue14_delivery_ack.py tests/test_issue25_plan_reuse.py โ†’ 127 passed, 1 skipped in 215.94s failed 0 (skip = linux_only S1b) ``` 1ํšŒ์ฐจ์˜ ์œ ์ผํ•œ ์‹คํŒจ์˜€๋˜ S9 ๊ฐ€ ์ด ํ˜ธ์ŠคํŠธ์—์„œ ํ†ต๊ณผํ•˜๊ณ , ์ƒˆ ํŒŒ์ผ 24๊ฑด(23 ํ†ต๊ณผ + Linux ์ „์šฉ skip 1)๊ณผ ์ธ์ ‘ ๊ณ„์•ฝ ์Šค์œ„ํŠธ๊ฐ€ ๋ชจ๋‘ ํ†ต๊ณผํ•œ๋‹ค. ### 4-3. ๊ฒฉ๋ฆฌ | ํ•ญ๋ชฉ | ๊ฒฐ๊ณผ | |---|---| | ์‹ค์ œ `.mam/jobs` ์ „ํ›„ | **์ฐจ์ด 0** | | `prompt-*` ์ž”์กด(์‹ค์ œ `.mam` / ๊ฒ€์ฆ ๊ฒฝ๋กœ `.mam`) | **0 / 0** | | ๊ฒ€์ฆ ๊ฒฝ๋กœ tracked ํŒŒ์ผ | ๋ณ€ํ™” ์—†์Œ(HEAD `708262a`), ๋ณธ ์ฒดํฌ์•„์›ƒ `main 1debc7d` | ## 5. ๋ณ‘ํ•ฉ ์ฐจ๋‹จ ์—ฌ๋ถ€ (ํ•ญ๋ชฉ 3) 1ํšŒ์ฐจ PASS ๋ฅผ ๋’ค์ง‘์„ ์š”์†Œ๋Š” ์—†๋‹ค. ๋ธํƒ€๋Š” ๋Ÿฌ๋„ˆ์˜ `try` ๋ฒ”์œ„ ํ™•์žฅ(`runner.py:472-491`), ๋ž˜ํผ์˜ `-s` ํ•œ ์ค„(`:193`), ํ…Œ์ŠคํŠธ 3๊ฑด(S4cยทS1eยทS9 ์กฐ๊ฑด๋ถ€ ๋‹จ์–ธ)๋ฟ์ด๊ณ , ์ •์ • ๋Œ€์ƒ๋งŒ ์ •ํ™•ํžˆ ์‹คํŒจโ†’ํ†ต๊ณผ๋กœ ๋ฐ”๋€Œ์—ˆ๋‹ค. ๋ฒ„์ „ ๋ฝ์Šคํ… bump ๋ถ€์žฌ(`4.7.2`, ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๊ฐ€ 4.7.3 ์œผ๋กœ ์ฒ˜๋ฆฌ ์˜ˆ์ •) ์™ธ์— **๋ณ‘ํ•ฉ์„ ๋ง‰์„ ๊ฒฐํ•จ์€ ์—†๋‹ค**. 1ํšŒ์ฐจ H2(loop SKILL.md ์˜ `MAM_DIFF_MAX_*` ์˜๋ฏธ)ยทH4(`$PROMPT` ํ›„ํ–‰ ๊ฐœํ–‰)๋Š” ์ด ๋ธํƒ€ ๋ฒ”์œ„ ๋ฐ–์ด๋ผ ๊ทธ๋Œ€๋กœ ์—ด๋ ค ์žˆ๋‹ค. | # | ๋น„์ฐจ๋‹จ ๊ด€์ฐฐ | ๊ทผ๊ฑฐ | |---|---|---| | I1 | `-s` ๋Š” ๋ฐ”์ดํŠธ ํฌ๊ธฐ ๊ฒ€์‚ฌ๋ผ ๊ฐœํ–‰ยท๊ณต๋ฐฑ๋งŒ ์žˆ๋Š” ํ”„๋กฌํ”„ํŠธ ํŒŒ์ผ์€ ํ†ต๊ณผํ•œ๋‹ค. ํ…์ŠคํŠธ ํ˜•์‹(`--prompt ""`)์˜ ๊ฑฐ๋ถ€์™€ ์™„์ „ํžˆ ๋Œ€์นญ์ด๋ ค๋ฉด `[[ -n "$(tr -d '[:space:]' < "$PROMPT_FILE")" ]]` ๋ฅ˜๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ, ์‹ค๋ฌด์—์„œ ๊ทธ๋Ÿฐ ์ž…๋ ฅ์€ ์—†๋‹ค | `multi-agent-mux-delegate-job:193`, ์‹ค์ธก | ## 6. ๊ต์ฐจ๊ฒ€์ฆ ๊ฐ€์ด๋“œ ```bash RP=/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260924T085626Z-b8987e/1 PY=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python git -C "$RP" rev-parse HEAD 'HEAD^{tree}' # 708262aโ€ฆ / d601c2dโ€ฆ # H1 ์žฌํ˜„ ์Šคํฌ๋ฆฝํŠธ(1ํšŒ์ฐจ ยง1 ์˜ ๊ฒƒ)๋ฅผ ์ •์ •๋œ ๋Ÿฌ๋„ˆ์— โ†’ returned None, leftover [], wrapper called 0 # ๋˜๋Œ๋ฆฐ ์‚ฌ๋ณธ: runner.pyยท๋ž˜ํผ๋ฅผ 59be1c8 ํŒ์œผ๋กœ โ†’ -k "s4c or s1e or s9 or s4 or s1" โ†’ 2 failed / 6 passed / 1 skipped (cd "$RP" && AGENT_PYTHON_BIN="$PY" PYTHONDONTWRITEBYTECODE=1 "$PY" -m pytest -p no:cacheprovider -q --basetemp=/tmp/bt tests/test_issue36_prompt_transport.py) # 23 passed, 1 skipped ``` [VERDICT: PASS]