# ๐Ÿ” Review Report โ€” PR #29 ๋ณ‘ํ•ฉ ์ „ ๋…๋ฆฝ ์žฌ๊ฒ€ํ†  (job `291e38b5`) - **Reviewer**: `planner-reviewer-claude-01` (role: planner,reviewer) ยท 2026-09-20 - **๋Œ€์ƒ**: PR #29 `feat/issue-21-items-4-5-7` **@ `46ea4cc`**, base `main @ 1134d74` (v4.6.0). 20์ปค๋ฐ‹, `git diff --stat 1134d74...HEAD` = **25 files, +3229/โˆ’108**. - **๊ฒ€ํ†  ํŠธ๋ฆฌ**: ์›ŒํฌํŠธ๋ฆฌ `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` (detached @ `46ea4cc`, ์ž์ฒด `.venv`). - **์•ˆ์ „ ๊ทœ์น™ ์ค€์ˆ˜**: ์›ŒํฌํŠธ๋ฆฌ์— ํŒŒ์ผ ์ˆ˜์ •ยท์ปค๋ฐ‹ยทcheckout ์—†์Œ. ๋ณธ ์ฒดํฌ์•„์›ƒ์€ `main @ 1134d74` ๊ทธ๋Œ€๋กœ(checkout/switch/merge ์—†์Œ). `worktree_remove.sh` ๋ฅผ ์‹ค์ œ ์›ŒํฌํŠธ๋ฆฌ์— ์‹คํ–‰ํ•˜์ง€ ์•Š์•˜๋‹ค. ์‹ค์ œ `.mam`ยทmonitor ๋ฐ๋ชฌยท์—์ด์ „ํŠธ ์„ธ์…˜ ์กฐ์ž‘ ์—†์Œ, pushยทmergeยท์ด์Šˆ/PR ์ƒํƒœ ๋ณ€๊ฒฝ ์—†์Œ. ์žฌํ˜„ ์‹คํ—˜์€ scratchpad ์˜ ์ž„์‹œ ์ €์žฅ์†Œ(uuid4 ์ด๋ฆ„)์—์„œ๋งŒ ํ–ˆ๋‹ค. - **heartbeat**: ๋ธŒ๋ฆฌํ”„ ์งํ›„ `started --heartbeat 30` ์„ ๊ฐ€์žฅ ๋จผ์ € ๋ฐœํ–‰ํ–ˆ๋‹ค. ## ๊ฒฐ๋ก  ์š”์•ฝ ๋ธŒ๋ฆฌํ”„์˜ 8๊ฐœ ํ•ญ๋ชฉ์„ ๋ชจ๋‘ ํ™•์ธํ–ˆ๋‹ค. **๋ณ‘ํ•ฉ์„ ๋ง‰์„ ๊ฒฐํ•จ์€ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค.** ํ•ญ๋ชฉ 5(๋ฆฌ๋ทฐ identity)์˜ ์„ค๊ณ„ยท๊ตฌํ˜„ ํ’ˆ์งˆ์€ ๋†’๊ณ , ๊ธฐ๋ณธ ๊ฒฝ๋กœ(์˜ต์…˜ ์—†๋Š” ๋ฃจํ”„)์˜ ํ˜ธํ™˜์„ฑ์€ ์ฝ”๋“œ์ƒ ๋ณด์žฅ๋œ๋‹ค. ์•„๋ž˜ **๋น„์ฐจ๋‹จ ์ง€์  9๊ฑด**(N1โ€“N9)์„ ๋‚จ๊ธด๋‹ค. ๊ทธ์ค‘ N1 ์€ ์žฌํ˜„ ์‹คํ—˜์œผ๋กœ ํ™•์ธํ•œ ์‹ค์ œ ์†Œ์‹ค ๊ฐ€๋Šฅ์„ฑ(ํŒ์ • ์ฆ๊ฑฐ์˜ gc ์†Œ๋ฉธ)์ด๋ฏ€๋กœ ํ›„์† ์ด์Šˆ ๋“ฑ๋ก์„ ๊ถŒํ•œ๋‹ค. ## 1. ํ•ญ๋ชฉ 4 โ€” ์›ŒํฌํŠธ๋ฆฌ ์ค€๋น„ยทํ•ด์ฒด ํ—ฌํผ ### 1-1. `.venv` โ€” ๋ธŒ๋ฆฌํ”„๊ฐ€ ๋ฌผ์€ "์‹ค์ œ venv" ๋Š” ์•„๋‹ˆ๋‹ค (N2, ๋น„์ฐจ๋‹จ) `worktree_prepare.sh:111-118` ์€ ์—ฌ์ „ํžˆ **์‹ฌ๋งํฌ**๋ฅผ ๋งŒ๋“ ๋‹ค(`ln -s "$MAIN/.venv" "$WT/.venv"`). ์‹ค์ œ venv ์ƒ์„ฑ๋„, ์ธํ„ฐํ”„๋ฆฌํ„ฐ ๊ฒฝ๋กœ ์ „๋‹ฌ๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‹ค๋งŒ ์ด๋ฒˆ PRยทbase ์—์„œ **์ปค๋ฐ‹ ์‚ฌ๊ณ  ๊ฒฝ๋กœ๋Š” 3์ค‘์œผ๋กœ ๋‹ซํ˜€ ์žˆ๋‹ค**: | ์ธต | ๊ทผ๊ฑฐ | |---|---| | ์ €์žฅ์†Œ `.gitignore` | base `1134d74` ์˜ `.gitignore:15` ๊ฐ€ `.venv`(์Šฌ๋ž˜์‹œ ์—†์Œ) โ€” ํŒŒ์ผยท๋งํฌยท๋””๋ ‰ํ„ฐ๋ฆฌ ๋ชจ๋‘ ๋ฌด์‹œ | | ๊ณต์œ  `info/exclude` | `worktree_prepare.sh:120-129` ๊ฐ€ `/.venv`ยท`.pytest_cache/` ๋ฅผ `$GIT_COMMON_DIR/info/exclude` ์— ์ถ”๊ฐ€ | | ๋ฐฐํฌ ๊ด€๋ฆฌ ๋ธ”๋ก | `deploy/install.sh:549`ยท`deploy/install_mam.sh:249` ๊ฐ€ `/.venv/` โ†’ `/.venv` (#27 `fadd38c`) | ๋‚จ๋Š” ์œ„ํ—˜์€ ๋‘ ๊ฐ€์ง€์ด๋ฉฐ, ๋‘˜ ๋‹ค ํ—ฌํผ๊ฐ€ ์œ ๋ฐœํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. - `rm -rf "$WT/.venv/"` ์ฒ˜๋Ÿผ **๋์— ์Šฌ๋ž˜์‹œ๋ฅผ ๋ถ™์ด๋ฉด ๋งํฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€** ์ œ์–ด ๋ฃจํŠธ์˜ venv ๋‚ด์šฉ์„ ์ง€์šด๋‹ค. ํ—ฌํผ๋Š” ๊ทธ๋Ÿฐ ํ˜ธ์ถœ์„ ํ•˜์ง€ ์•Š์ง€๋งŒ(์Šคํฌ๋ฆฝํŠธ 2๊ฐœ์˜ `rm -rf` ๋Š” `$gd/modules*` ๋ฟ), ์‚ฌ๋žŒยท์—์ด์ „ํŠธ์˜ ์ˆ˜์ž‘์—…์—์„œ๋Š” ๊ทธ๋Œ€๋กœ ๋‚จ๋Š” ํ•จ์ •์ด๋‹ค. - ์›ŒํฌํŠธ๋ฆฌ์™€ ๋ณธ ์ฒดํฌ์•„์›ƒ์ด venv ๋ฅผ ๊ณต์œ ํ•˜๋ฏ€๋กœ ๋ธŒ๋žœ์น˜๋ณ„ ์˜์กด์„ฑ ๋ถ„๊ธฐ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. โ†’ ๊ถŒ๊ณ : ํ—ฌํผ์— `--venv link|create` ๊ฐ™์€ ์„ ํƒ์ง€๋ฅผ ๋‘๊ฑฐ๋‚˜, ์ตœ์†Œํ•œ SKILL.md ์— "`.venv` ๋Š” ๋งํฌ์ด๋ฏ€๋กœ ์›ŒํฌํŠธ๋ฆฌ์—์„œ `pip install`ยท`rm -rf .venv/` ๊ธˆ์ง€"๋ฅผ ๋ช…์‹œ. ### 1-2. `info/exclude` ๋Š” ๊ณต์œ ๋ฌผ์ด๋‹ค (N3, ๋น„์ฐจ๋‹จ) linked worktree ์˜ `info/exclude` ๋Š” `$GIT_COMMON_DIR` ์˜ ๊ฒƒ์ด๋ฏ€๋กœ prepare ๊ฐ€ **๋ณธ ์ฒดํฌ์•„์›ƒ์˜ ๋ฌด์‹œ ๊ทœ์น™๋„ ๋ฐ”๊พผ๋‹ค**. ์ฝ”๋“œ ์ฃผ์„(`:120-122`)๊ณผ ์„ค๊ณ„ ๋ฌธ์„œ๊ฐ€ ์ด๋ฅผ ๋ช…์‹œํ•˜๊ณ , ์ถ”๊ฐ€๋˜๋Š” ๋‘ ํŒจํ„ด์€ ๋ณธ ์ฒดํฌ์•„์›ƒ์—์„œ๋„ ์‚ฐ์ถœ๋ฌผ์ด๋‹ค. ์ˆ˜์šฉ ๊ฐ€๋Šฅํ•˜๋‚˜, ๋˜๋Œ๋ฆฌ๋Š” ๊ฒฝ๋กœ(remove ์‹œ ์ •๋ฆฌ)๋Š” ์—†๋‹ค. ### 1-3. ์„œ๋ธŒ๋ชจ๋“ˆ opt-inยท๋กค๋ฐฑ `worktree_prepare.sh:131-204`: `--submodules` ์ผ ๋•Œ๋งŒ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , ์ดˆ๊ธฐํ™” ์ „ ์ƒํƒœ(๊ฐ ์„œ๋ธŒ๋ชจ๋“ˆ์˜ git dir ์กด์žฌ ์—ฌ๋ถ€, ๊ณต์šฉ `.git/config` ์˜ `submodule..url|active`)๋ฅผ **์ธ๋ฑ์Šค ๋ฐฐ์—ด**(bash 3.2 ํ˜ธํ™˜)๋กœ ๊ธฐ๋กํ•œ๋‹ค. ์ดˆ๊ธฐํ™” ์•ˆ ๋œ ๊ฒฝ๋กœ์— ํŒŒ์ผ์ด ์ด๋ฏธ ์žˆ์œผ๋ฉด **์•„๋ฌด๊ฒƒ๋„ ๋ฐ”๊พธ๊ธฐ ์ „์—** exit 3 ์œผ๋กœ ๊ฑฐ๋ถ€ํ•œ๋‹ค(R4) โ€” ๋กค๋ฐฑ์ด "์ด๋ฒˆ ์‹œ๋„๊ฐ€ ๋งŒ๋“  ๊ฒƒ"๋งŒ ์ง€์šธ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ „์ œ๋ฅผ ์‹ค์ œ๋กœ ์„ฑ๋ฆฝ์‹œํ‚ค๋Š” ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ๋‹ค. ์‹คํŒจ ์‹œ deinit โ†’ `.git` ํŒŒ์ผ ์ œ๊ฑฐ โ†’ `modules/` ์ œ๊ฑฐ โ†’ ์—†๋˜ `modules/` ์ œ๊ฑฐ โ†’ config ๋ณต์› ์ˆœ์œผ๋กœ ๋˜๋Œ๋ฆฌ๊ณ , ๋งˆ์ง€๋ง‰์— `git status` ๋กœ ์›ŒํฌํŠธ๋ฆฌ๊ฐ€ ์—ฌ์ „ํžˆ ์“ธ ์ˆ˜ ์žˆ๋Š” ์ฒดํฌ์•„์›ƒ์ธ์ง€ ํ™•์ธํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐˆ๋ผ ์ค€๋‹ค. ์‚ฌ๋žŒ์ด ๊ฐ•์ œ ์ œ๊ฑฐ๋กœ ๋” ๋ง๊ฐ€๋œจ๋ฆฌ์ง€ ๋ง๋ผ๋Š” ์•ˆ๋‚ด๊นŒ์ง€ ์žˆ๋‹ค. ### 1-4. `--force` ๊ฐ€ ์ž‘์—…์„ ์žƒ์„ ์ˆ˜ ์žˆ๋Š”๊ฐ€ โ€” ์žƒ์ง€ ์•Š๋Š”๋‹ค `worktree_remove.sh` ์˜ ํŒ์ •์€ **๋ณ€๊ฒฝ์„ ๊ฐ€ํ•˜๊ธฐ ์ „์—** ๋ชจ๋‘ ๋๋‚œ๋‹ค(`:57-121`): ์ž ๊ธˆ, ์‹คํ–‰ ์ค‘ ์„ธ์…˜(`--stop` ์—†์œผ๋ฉด ๊ฑฐ๋ถ€), ์—ด๋ฆฐ ์žก, ํŠธ๋ฆฌ ์ฒญ๊ฒฐ(์„œ๋ธŒ๋ชจ๋“ˆ ํฌํ•จ), ์„œ๋ธŒ๋ชจ๋“ˆ ์†Œ๊ฒฌ. `--force` ๋Š” ์›ŒํฌํŠธ๋ฆฌ git dir ์— `modules/` ๊ฐ€ ์žˆ์–ด git ์ด plain remove ๋ฅผ ๊ฑฐ๋ถ€ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ, ๊ทธ๊ฒƒ๋„ **๋‹จ ํ•œ ๋ฒˆ** ์“ด๋‹ค(`METHOD="single --force"`). - **R1(์ •์ง€ ์ค‘ ์“ฐ๊ธฐ)**: `judge_tree()` ๋ฅผ `--stop` **์ดํ›„ ๋‹ค์‹œ** ์‹คํ–‰ํ•˜๊ณ , ๋‹ฌ๋ผ์กŒ์œผ๋ฉด `:154` ์—์„œ ๊ฑฐ๋ถ€ํ•œ๋‹ค. - **R6(tag/notes ๋กœ๋งŒ ๋ณด์กด๋œ ์ปค๋ฐ‹)**: `lib_py/worktree.py:UNPUSHED_REVS = ("HEAD","--exclude=refs/stash","--all","--not","--remotes")` ๋กœ ๋ชจ๋“  ๋กœ์ปฌ ref ๊ธฐ์ค€ ๋ฏธํ‘ธ์‹œ ์ปค๋ฐ‹์„ ๋ณธ๋‹ค. stash ๋Š” ๋ณ„๋„ ํ™•์ธ. - **R5(deinit ์ €์žฅ์†Œ)**: `submodule foreach` ๋กœ ๋‹ฟ์ง€ ์•Š๋Š” `modules/` ์•„๋ž˜ ์ €์žฅ์†Œ๊นŒ์ง€ `--git-dir` ๋กœ ์ง์ ‘ ๊ฒ€์‚ฌํ•˜๊ณ , ํŒ๋‹จ ๋ถˆ๊ฐ€ํ•œ "๋ถ€๋ถ„ ์ €์žฅ์†Œ"๋Š” ์˜ค๋ฅ˜๋กœ ๋‚จ๊ฒจ ๊ฑฐ๋ถ€ํ•œ๋‹ค. ์ฆ‰ `--force` ์‹œ์ ์—๋Š” "๋ฏธ์ปค๋ฐ‹ยท๋ฏธํ‘ธ์‹œยทstash ์—†์Œ"์ด ์ฆ๋ช…๋ผ ์žˆ๋‹ค. ํ•œ ๊ฐ€์ง€๋งŒ ์ ์–ด ๋‘”๋‹ค(N8): plain/force ์–ด๋А ์ชฝ์ด๋“  git ์€ **๋ฌด์‹œ๋œ ํŒŒ์ผ**์„ ๋ง์—†์ด ์ง€์šด๋‹ค. ์›ŒํฌํŠธ๋ฆฌ์— ํ…Œ์ŠคํŠธ๊ฐ€ ๋‚จ๊ธด `.mam/delegate_job_logs` ๊ฐ™์€ ์‚ฐ์ถœ๋ฌผ์€ ์ œ๊ฑฐ์™€ ํ•จ๊ป˜ ์‚ฌ๋ผ์ง„๋‹ค(์ œ์–ด ํ‰๋ฉด์ด ๋ณธ ์ฒดํฌ์•„์›ƒ์ด๋ฏ€๋กœ ์„ค๊ณ„์ƒ ๋ฌดํ•ด). ๋˜ ํ•˜๋‚˜(N7): ์—ด๋ฆฐ ์žก ๊ฒ€์‚ฌ๋Š” `registry_dirs`(`MAM_JOBS_DIR` โ†’ `WORKSPACE_ROOT/.mam/jobs` โ†’ ํ–‰๋“ค์˜ `control_plane.mam_jobs_dir`)๋งŒ ๋ณธ๋‹ค. ํ–‰์ด ์—†๋Š” ์›ŒํฌํŠธ๋ฆฌ ๋กœ์ปฌ `.mam/jobs` ์˜ ์žก์€ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. prepare ๊ฐ€ ๋งŒ๋“  creator ๋Š” ์ œ์–ด ๋ฃจํŠธ์— ๋ฌถ์ด๋ฏ€๋กœ ๋ชจ๋ธ์ƒ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฒฝ๋กœ์ง€๋งŒ, ์ˆ˜์ž‘์—…์œผ๋กœ ๋งŒ๋“  ์›ŒํฌํŠธ๋ฆฌ์—๋Š” ํ•ด๋‹นํ•œ๋‹ค. ## 2. ํ•ญ๋ชฉ 4 โ€” resume ๊ณ„์•ฝ ๋ณ€๊ฒฝ(`2679ddf`)์€ ๊ธฐ์กด ์‚ฌ์šฉ์ž๋ฅผ ๊นจ์ง€ ์•Š๋Š”๋‹ค `resume_session.sh:50-70` ์ด ํ–‰์˜ `pane.cwd` ์™€ `--workspace` ๋ฅผ ๋น„๊ตํ•ด ๋‹ค๋ฅด๋ฉด exit 2, ๊ฒฝ๋กœ๊ฐ€ ์‚ฌ๋ผ์กŒ์œผ๋ฉด exit 2 ๋กœ ์•ˆ๋‚ดํ•œ๋‹ค. ๊ธฐ์กด ๋‹จ์ผ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ํ–ฅ์ด ์—†๋Š” ์ด์œ : 1. `--workspace` ๋Š” ์›๋ž˜๋ถ€ํ„ฐ **ํ•„์ˆ˜ ์ธ์ž**์ด๊ณ (`:42`), ๊ธฐ์กด ํ–‰์˜ `pane.cwd` ๋Š” ๊ทธ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๊ฒฝ๋กœ๋‹ค. ๋ณธ ์ฒดํฌ์•„์›ƒ์˜ ์‹ค์ œ `.mam/agent-sessions.yaml` 5๊ฐœ ํ–‰ ๋ชจ๋‘ `cwd: /Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux` ๋กœ, ํ‰์†Œ ํ˜ธ์ถœ๊ณผ ์ผ์น˜ํ•œ๋‹ค. 2. ๋น„๊ต๋Š” `mam_abs_workspace`(`lib.sh:1606-1609`, `cd -P โ€ฆ pwd -P`)๋กœ ์ •๊ทœํ™”ํ•œ ๋’ค ํ•˜๋ฏ€๋กœ ์‹ฌ๋งํฌยท์ƒ๋Œ€๊ฒฝ๋กœ ํ‘œ๊ธฐ ์ฐจ์ด๋Š” ํ†ต๊ณผํ•œ๋‹ค. 3. `pane.cwd` ๊ฐ€ ์—†๋Š” ๊ตฌ(่ˆŠ) ํ–‰์€ `ROW_CWD` ๊ฐ€ ๋น„์–ด ๊ฒ€์‚ฌ ์ž์ฒด๋ฅผ ๊ฑด๋„ˆ๋›ด๋‹ค. ๋ฐ”์ธ๋”ฉ ์˜์†์€ **์ด๋ฏธ ์‹คํ–‰ ์ค‘์ธ ๊ฒฝ๋กœ์—๋„** ์ ์šฉ๋œ๋‹ค: `:145` ๊ฐ€ `CONTROL_PLANE_JSON="$CP_JSON"` ๋ฅผ `update_yaml_resumed.sh` ์— ๋„˜๊ธฐ๊ณ , ๊ทธ์ชฝ `:168` ์ด ์ด๋ฅผ ์ฝ์–ด ํ–‰์— ๊ธฐ๋กํ•œ๋‹ค(A13). ๊ณ„์‚ฐ ์‹คํŒจ ์‹œ ์›ŒํฌํŠธ๋ฆฌ๋ฉด exit 2, ์ œ์–ด ๋ฃจํŠธ๋ฉด ๊ฒฝ๊ณ  ํ›„ ๋ฌด๊ธฐ๋ก โ€” create ์ชฝ(`create_session.sh:211-221`)๊ณผ ๊ฐ™์€ ๊ทœ์น™์ด๋‹ค. ## 3. ํ•ญ๋ชฉ 5 โ€” ๋ฆฌ๋ทฐ identity - **๊ธฐ๋ก**: `_record_round`(`runner.py`)๊ฐ€ ๋ผ์šด๋“œ๋งˆ๋‹ค `iteration/base_sha/head_sha/tree_sha/review_commit/isolation/review_path/jobs/verdicts/reviewers(evidence)/stale` ๋ฅผ ๋‚จ๊ธฐ๊ณ  ์ฒดํฌํฌ์ธํŠธ์— `loop_id` ์™€ `review_rounds` ๋ฅผ ์‹ฃ๋Š”๋‹ค. ์Šน๊ฒฉ ์‹œ์—๋Š” ๋ฆฌํฌํŠธ ์˜†์— `report-.identity.json` ์„ ์›์ž์ ์œผ๋กœ(`.tmp` โ†’ `replace`) ์“ด๋‹ค. - **์Šค๋ƒ…์ƒท**: `_worktree_tree()` ๊ฐ€ ์ž„์‹œ `GIT_INDEX_FILE` ๋กœ `read-tree HEAD` โ†’ `add -A` โ†’ `write-tree` ํ•˜์—ฌ **์‹ค์ œ ์ธ๋ฑ์Šคยทstatusยทref ๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค**. ๋ฏธ์ปค๋ฐ‹์ด ์žˆ์œผ๋ฉด `commit-tree` ๋กœ ref ์—†๋Š” ์Šค๋ƒ…์ƒท ์ปค๋ฐ‹์„ ๋งŒ๋“ ๋‹ค. untracked ์ค‘์ฒฉ ์ €์žฅ์†Œ๋Š” gitlink ๋งŒ ๋‚จ์•„ ๊ณ ์ • ๋ถˆ๊ฐ€์ด๋ฏ€๋กœ ๊ฑฐ๋ถ€ํ•˜๊ณ (`_snapshot`), dirty ์„œ๋ธŒ๋ชจ๋“ˆ๋„ ๊ฑฐ๋ถ€ํ•œ๋‹ค(`_dirty_submodules` ๋Š” `--ignore-submodules=none` ์œผ๋กœ ๋ฌผ์–ด `submodule.*.ignore` ์„ค์ •์ด ์ˆจ๊ธฐ์ง€ ๋ชปํ•˜๊ฒŒ ํ•œ๋‹ค). ํŒ์ • ๋ถˆ๊ฐ€(`None`)๋Š” ์ „๋ถ€ ์‹คํŒจ๋กœ ๋–จ์–ด์ง€๋Š” fail-closed ๋‹ค. - **๊ฒฉ๋ฆฌ**: `_open_review_tree` ๋Š” `TMPDIR/mam-review//` ์— detached ์›ŒํฌํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ , **์‹คํŒจํ•˜๋ฉด ๊ฒฉ๋ฆฌ๋ฅผ ๋‚ฎ์ถ”์ง€ ์•Š๊ณ  exit 1**(๋กœ๊ทธ ๋ฌธ๊ตฌ๊นŒ์ง€ ๊ทธ๋ ‡๊ฒŒ ์ ํ˜€ ์žˆ๋‹ค). `_close_review_tree` ๋Š” remove ์‹คํŒจ ์‹œ์—๋งŒ `prune` ์„ ๋ถ€๋ฅด๊ณ , ๋ถ€๋ชจ ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ๋น„์—ˆ์„ ๋•Œ๋งŒ ์ง€์šด๋‹ค. `run()` ์ด `_run_body()` ๋ฅผ `finally: _close_all_review_trees()` ๋กœ ๊ฐ์‹ธ๊ณ  `release_guard` ๋„ ๊ฐ™์€ ์ •๋ฆฌ๋ฅผ ํ•œ๋‹ค. - **stale ์žฌ๋ฆฌ๋ทฐ**: ๋ผ์šด๋“œ ํŒ์ • ๋’ค `_round_is_stale`(ํ˜„์žฌ headยทtree ๋น„๊ต)๋กœ ์†Œ์Šค ๋ณ€๊ฒฝ์„ ์žก์•„ ์Šน์ธ ์ „๋Ÿ‰ ํ๊ธฐ ํ›„ ์žฌ๋ฆฌ๋ทฐ, ๋งˆ์ง€๋ง‰ ๋ผ์šด๋“œ๋ฉด exit 1. ์Šน๊ฒฉ ์ง์ „์—๋„ `_identity_now() != (accepted head, tree)` ๋ฉด "source changed after approval; not promoting" ์œผ๋กœ exit 1(B10). - **์›๊ฒฉ ๋ฆฌ๋ทฐ์–ด**: `_check_remote_recipients` ๊ฐ€ **์–ด๋–ค ์žก๋„ ์œ„์ž„ํ•˜๊ธฐ ์ „์—** ํŒ์ • ์ˆ˜์‹ ์ž ์ „์›์„ ๊ฒ€์‚ฌํ•ด ๊ธฐ๋ณธ ๊ฑฐ๋ถ€, `--remote-review diff-only` ๋งŒ ํ—ˆ์šฉํ•˜๋ฉฐ ๊ทธ๋•Œ diff ๊ฐ€ truncated/unavailable ์ด๋ฉด ์ฆ๊ฑฐ๊ฐ€ ์—†๋‹ค๊ณ  exit 1. - **๊ธฐ๋ณธ ๋™์ž‘ ๋™์ผ์„ฑ**: `_context_active()` ๊ฐ€ ๊ฑฐ์ง“์ด๋ฉด `_worktree_context()` ๋Š” ๋นˆ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  `collect_changes_diff(None)` ์€ ๊ธฐ์กด ํ˜ธ์ถœ๊ณผ ๋™์ผํ•˜๋‹ค. `_dispatch` ์˜ ์ƒˆ ์ธ์ž๋Š” ๊ธฐ๋ณธ๊ฐ’(`remote=False, purpose="work"`)์ด๋ผ ํ”„๋กฌํ”„ํŠธ๋Š” ๋ฐ”์ดํŠธ ๋™์ผํ•˜๋‹ค. ๋‹ค๋งŒ ์˜ต์…˜๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ `Loop id:` ๋กœ๊ทธ 1์ค„๊ณผ ์ฒดํฌํฌ์ธํŠธ์˜ `loop_id` ํ•„๋“œ๊ฐ€ ๋Š˜์—ˆ๋‹ค(N9, ๋ฌดํ•ด). ### N1 (๋น„์ฐจ๋‹จ, ํ›„์† ๊ถŒ๊ณ ) โ€” ์Šค๋ƒ…์ƒท ์ปค๋ฐ‹์ด gc ๋กœ ์‚ฌ๋ผ์ง„๋‹ค `_snapshot` ์˜ ์Šค๋ƒ…์ƒท ์ปค๋ฐ‹์€ ref ๊ฐ€ ์—†๊ณ , ์œ ์ผํ•œ ๋„๋‹ฌ ๊ฒฝ๋กœ์˜€๋˜ ๋ฆฌ๋ทฐ ์›ŒํฌํŠธ๋ฆฌ HEAD ๋Š” `_close_review_tree` ๊ฐ€ ๋ผ์šด๋“œ ํŒ์ • ์งํ›„ ์—†์•ค๋‹ค. ๊ทธ๋ž˜์„œ `identity.json` ์˜ `review_commit`ยท`tree_sha` ๋Š” **๋‚˜์ค‘์— ๊ฒ€์ฆํ•˜๋ ค ํ•  ๋•Œ ์ด๋ฏธ ์—†์„ ์ˆ˜ ์žˆ๋‹ค**. scratchpad ์ž„์‹œ ์ €์žฅ์†Œ์—์„œ ์žฌํ˜„ํ–ˆ๋‹ค(์›ŒํฌํŠธ๋ฆฌ ์ œ๊ฑฐ โ†’ `reflog expire --expire=now --all` โ†’ `gc --prune=now` โ†’ `git cat-file -e ` ์‹คํŒจ). ์ผ์ƒ์ ์œผ๋กœ๋„ unreachable ๊ฐ์ฒด๋Š” ๊ธฐ๋ณธ 2์ฃผ ๋’ค ๋Œ€์ƒ์ด๋‹ค. ๋ฏธ์ปค๋ฐ‹์ด ์žˆ๋˜ ๋ผ์šด๋“œ์—๋งŒ ํ•ด๋‹นํ•˜๋ฉฐ(ํด๋ฆฐ ํŠธ๋ฆฌ๋ฉด `review_commit == head_sha`), ํŒ์ • ์ž์ฒด์˜ ์ •ํ•ฉ์„ฑ์—๋Š” ์˜ํ–ฅ์ด ์—†๋‹ค. โ†’ ๊ถŒ๊ณ : `refs/mam/review//` ๊ฐ™์€ ref ๋กœ ์Šค๋ƒ…์ƒท์„ ๊ณ ์ •ํ•˜๊ณ , ๋ฃจํ”„ ์ข…๋ฃŒ ์‹œ ์ •์ฑ…์ ์œผ๋กœ ์ •๋ฆฌ(๋˜๋Š” ๋ณด์กด). ### N6 (๋น„์ฐจ๋‹จ) โ€” ๊ฐ•์ œ ์ข…๋ฃŒ ์‹œ ๋ฆฌ๋ทฐ ์›ŒํฌํŠธ๋ฆฌ ์ž”์กด `finally`/`release_guard`/์‹œ๊ทธ๋„ ํ•ธ๋“ค๋Ÿฌ๋กœ ์ •์ƒยทTERMยทINT ๊ฒฝ๋กœ๋Š” ๋ฎ์ด์ง€๋งŒ, SIGKILL ์ด๋ฉด `TMPDIR/mam-review/...` ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ ์†Œ์Šค ์ €์žฅ์†Œ์˜ worktree ํ•ญ๋ชฉ์ด ๋‚จ๋Š”๋‹ค. `prune` ์€ remove ์‹คํŒจ ์‹œ์—๋งŒ ๋ถ€๋ฅธ๋‹ค(์˜๋„๋œ ์„ ํƒ, ์ฃผ์„์— ๊ทผ๊ฑฐ ์žˆ์Œ). ## 4. #20 ๊ฒฉ๋ฆฌ โ€” ์„ค๊ณ„์™€ ์‹ค์ธก - `tests/conftest.py` ์˜ `pytest_configure` ๊ฐ€ **์ˆ˜์ง‘ ์ด์ „์—** ambient ๋ฐ”์ธ๋”ฉ 4๊ฐœ๋ฅผ ๊ธฐ๋กํ•˜๊ณ  `AGENT_SESSIONS_YAML`ยท`MAM_JOBS_DIR`ยท`DELEGATE_JOB_LOGS_DIR` ๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค. `WORKSPACE_ROOT` ๋Š” **๋‹ค๋ฅธ ์ฒดํฌ์•„์›ƒ์„ ๊ฐ€๋ฆฌํ‚ฌ ๋•Œ๋งŒ** ์ œ๊ฑฐํ•œ๋‹ค(์ด ์ฒดํฌ์•„์›ƒ์„ ๊ฐ€๋ฆฌํ‚ค๋ฉด lib.sh ๊ฐ€ ์–ด์ฐจํ”ผ ์œ ๋„ํ•˜๋Š” ๊ฐ’). import ์‹œ์ ์— `os.environ` ์„ ๋ณต์‚ฌํ•˜๋Š” ๋ชจ๋“ˆ(`test_orc_onboard.SCRUBBED_ENV`) ๋•Œ๋ฌธ์— ์„ธ์…˜ fixture ๋กœ๋Š” ๋Šฆ๋‹ค๋Š” ์„ค๋ช…์ด ์ •ํ™•ํ•˜๋‹ค. `pytest_unconfigure` ๊ฐ€ ๋˜๋Œ๋ ค in-process `pytest.main()` ํ˜ธ์ถœ์ž๋ฅผ ๋ฐฐ๋ คํ•œ๋‹ค. - autouse `_isolate_mam_state_dirs` ๋Š” **env ๋งŒ** ์„ค์ •ํ•˜๊ณ  ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์œผ๋ฉฐ, ์ˆจ๊น€ ์ด๋ฆ„(`.mam-test-state`)์ด๋ผ `tmp_path` ๋ฅผ ๋‚˜์—ดํ•˜๋Š” ๊ธฐ์กด ํ…Œ์ŠคํŠธ๋ฅผ ๊นจ์ง€ ์•Š๋Š”๋‹ค. `mam_sandbox` ์™€ ํ…Œ์ŠคํŠธ ์ž์‹ ์˜ `setenv/delenv` ๊ฐ€ ๋’ค์— ์‹คํ–‰๋ผ ์ด๊ธด๋‹ค. - ์„ธ์…˜ ๊ฐ€๋“œ `mam_state_leak_guard` + `_mam_state_guard.py` ๋Š” **๋ณด๊ณ ๋งŒ ํ•˜๊ณ  ์ ˆ๋Œ€ ์ง€์šฐ์ง€ ์•Š๋Š”๋‹ค**(์‹ค์ œ ์ œ์–ด ํ‰๋ฉด์ด๋ฏ€๋กœ ์˜ณ๋‹ค). ๊ฐ์‹œ ๋Œ€์ƒ์€ ์ด ์ฒดํฌ์•„์›ƒ์˜ `.mam` ๊ณผ ๊ธฐ๋ก๋œ ๋ฐ”์ธ๋”ฉ์ด ๊ฐ€๋ฆฌํ‚จ ์ œ์–ด ํ‰๋ฉด์ด๋ฉฐ, ๋™์‹œ์— ๋„๋Š” ์‹ค์ œ ์œ„์ž„๊ณผ๋Š” "๋ ˆ์ฝ”๋“œ ๋จผ์ € โ†’ ๋“ฑ๋ก๋œ ์„ธ์…˜" ๊ทœ์น™์œผ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค. - #15 ์˜ตํŠธ์•„์›ƒ(N4, ๋น„์ฐจ๋‹จ): `test_issue15_registry_isolation.py` ์˜ ๋ˆ„์ˆ˜ ๋‹จ์–ธ์ด "์ „์ฒด ๋ชฉ๋ก ๋™์ผ"์—์„œ "์ž๊ธฐ ์žก id ๊ฐ€ ์‹ค์ œ ๋ฃจํŠธ์— ์—†์Œ + ๊ทธ ์‚ฌ์ด ๋Š˜์–ด๋‚œ ํ•ญ๋ชฉ ์ค‘ ์ž๊ธฐ ์„ธ์…˜ ์ด๋ฆ„์ด ์—†์Œ"์œผ๋กœ **์•ฝํ•ด์กŒ๋‹ค**. ๋™์‹œ ์‹คํ–‰๋˜๋Š” ์‹ค์ œ ์œ„์ž„ ๋•Œ๋ฌธ์— ์ „์ฒด ๋น„๊ต๊ฐ€ ์˜คํƒ์„ ๋ƒˆ๋‹ค๋Š” ๊ทผ๊ฑฐ๊ฐ€ ํƒ€๋‹นํ•˜๊ณ , ์„ธ์…˜ ์ด๋ฆ„ ์Šค์บ”์ด ํ•ต์‹ฌ ๋ณด์žฅ์„ ์œ ์ง€ํ•˜๋ฏ€๋กœ ์ˆ˜์šฉํ•œ๋‹ค. ## 5. acceptance5 ์•ˆ์ •ํ™”(`7bc3e1a`ยท`371099a`ยท`e6d2168`) โ€” ๊ณ„์•ฝ์„ ์•ฝํ™”ํ•˜์ง€ ์•Š๋Š”๊ฐ€ | ๋ณ€๊ฒฝ | ํŒ๋‹จ | |---|---| | ์„ธ์…˜ ์ด๋ฆ„ยทHMAC secret ์„ ์‹คํ–‰๋งˆ๋‹ค ์œ ๋‹ˆํฌํ•˜๊ฒŒ(`tag = os.urandom(4).hex()`) | **๊ฐœ์„ **. ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค์— retain ๋œ ๊ณผ๊ฑฐ dispatch ๊ฐ€ ์ด๋ฒˆ ์‹คํ–‰์˜ ์žก ๋Œ€์‹  ๋ฐฐ๋‹ฌ๋˜๋˜ ๊ต์ฐจ ์˜ค์—ผ์„ ์—†์•ค๋‹ค | | `--heartbeat-misses 3 โ†’ 10` | ํ—ˆ์šฉ. ์ด ์‹œํ—˜์˜ ๋ชฉ์ ์€ "์›๊ฒฉ ๋Ÿฌ๋„ˆ๊ฐ€ ๋ธŒ๋กœ์ปค๋งŒ์œผ๋กœ ์žก์„ ๋๋‚ธ๋‹ค"์ด๊ณ , Kร—N ๋ฌด์‘๋‹ต ์ž„๊ณ„ ์ž์ฒด๋Š” AC1ยทAC3 ๊ฐ€ ๊ณ ์ •ํ•œ๋‹ค. ์›๊ฒฉ ๋Ÿฌ๋„ˆ์˜ `started --heartbeat` ๊ณ„์•ฝ์€ ๊ทธ๋Œ€๋กœ๋‹ค | | ๋‹จ์–ธ์— `advanced to 'completed'` ์ถ”๊ฐ€ | ํ—ˆ์šฉ. retain ๋œ ์ข…๋‹จ ์ด๋ฒคํŠธ๋งŒ ๋ฐฐ๋‹ฌ๋œ ๊ฒฝ์šฐ๋„ ํ”„๋กœํ† ์ฝœ์ƒ ์ •๋‹นํ•œ ๋ฐฐ๋‹ฌ ํ™•์ธ์ด๋‹ค | | `_skip_if_broker_did_not_deliver` | ์กฐ๊ฑด๋ถ€ ํ—ˆ์šฉ(N5). skip ์€ "๋Ÿฌ๋„ˆ๊ฐ€ started ๋ฅผ ๋ฐœํ–‰ํ–ˆ๊ณ , ๊ตฌ๋…์ž๊ฐ€ **๋งž๋Š” ํ† ํ”ฝ**์— SUBSCRIBED ํ–ˆ๊ณ , ๊ทธ ์žก์— ๋Œ€ํ•ด **์•„๋ฌด๊ฒƒ๋„**(๋“œ๋กญ ๋กœ๊ทธ์กฐ์ฐจ) ๋ฐ›์ง€ ๋ชปํ–ˆ๋‹ค"๋Š” ์„ธ ์กฐ๊ฑด์ด ๋™์‹œ์— ์„ฑ๋ฆฝํ•  ๋•Œ๋งŒ์ด๋‹ค. HMACยทidentity ๋“œ๋กญ, ์ž˜๋ชป๋œ ํ† ํ”ฝ, ๋ฐœํ–‰ ์‹คํŒจ๋Š” ์—ฌ์ „ํžˆ ์‹คํŒจํ•œ๋‹ค. ๋‹ค๋งŒ ๊ตฌ๋…์ž ๋ฐฐ๋‹ฌ ๊ฒฝ๋กœ์˜ ์‹ค์ œ ํšŒ๊ท€๊ฐ€ ๊ฐ™์€ ์ฆ์ƒ์„ ๋‚ด๋ฉด skip ์œผ๋กœ ๊ฐ€๋ ค์งˆ ์ˆ˜ ์žˆ๋‹ค | ## 6. ์ˆ˜์šฉ ์‚ฌ๋ก€ยท์‹ ๊ทœ ํ…Œ์ŠคํŠธ์˜ ๊ฒฐ์ •์„ฑยท๊ฒฉ๋ฆฌ - `tests/test_issue21_acceptance.py`(A1โ€“A8, 278์ค„): ์‹ค์ œ herdrยท์‹ค์ œ ๋ธŒ๋กœ์ปค๋ฅผ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. mock herdr / stub ์œ„์ž„ + `mam_sandbox`ยท`tmp_path` ๊ธฐ๋ฐ˜์ด๋ฉฐ, ๋ธŒ๋กœ์ปค ๊ด€๋ จ ์–ธ๊ธ‰์€ ์ฃผ์„๊ณผ "๋ธŒ๋กœ์ปค ๋„๋‹ฌ ๋ถˆ๊ฐ€ ์กฐ๊ฑด์—์„œ ๋ž˜ํผ๋ฅผ ๋Œ๋ฆฐ๋‹ค"๋Š” A3 ํ•œ ๊ณณ(`:102`)๋ฟ์ด๋‹ค. - `tests/test_issue21_loop_b.py`(526์ค„, L1โ€“L21): ๋Ÿฌ๋„ˆ๋ฅผ ์ง์ ‘ ๊ตฌ๋™ํ•˜๋˜ ์œ„์ž„ยท๋Œ€๊ธฐ๋ฅผ ์Šคํ…์œผ๋กœ ๋ฐ”๊พธ๊ณ  ์ž„์‹œ git ์ €์žฅ์†Œ์—์„œ ๋ˆ๋‹ค. ๋ธŒ๋กœ์ปค ์˜์กด 0, `time.sleep` ์—†์Œ. L1/L12/L14b/L18b ๊ฐ€ **์˜ต์…˜ ์—†๋Š” ๊ธฐ์กด ๋™์ž‘ ๋ถˆ๋ณ€**์„ ๋ณ„๋„๋กœ ๊ณ ์ •ํ•˜๋Š” ์ ์ด ์ข‹๋‹ค. - `tests/test_issue21_phase2_loop_a.py`(649์ค„): `time.sleep(0.2)` 1ํšŒ(`:312`) ์™ธ์— ์‹œ๊ฐ„ ์˜์กด ์—†์Œ. `deploy/install.sh` ๋ฅผ ์ž„์‹œ ํƒ€๊นƒ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ๋กœ๊ฐ€ ์žˆ์œผ๋‚˜ ๋ชจ๋‘ `tmp_path` ์•ˆ์ด๋‹ค. - `tests/test_issue20_state_isolation.py`(202์ค„): ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ๋‹จ์œ„ + ์ค‘์ฒฉ pytest 1๊ฑด(์„ค๊ณ„๋œ skip). - uuid ์‚ฌ์šฉ์€ `uuid.uuid4()`(issue15 ์˜ `_unique`)๋กœ ๋ธŒ๋ฆฌํ”„ ๊ทœ์น™๊ณผ ๋งž๋Š”๋‹ค. ## 7. ๋ฌธ์„œ ์ •ํ•ฉ์„ฑ๊ณผ ๊ถŒ๊ณ ์˜ ํƒ€๋‹น์„ฑ - `docs/design/worktree-creator-model.md`: ํ•ญ๋ชฉ ํ‘œ๊ฐ€ 1ยท2ยท3ยท6 = ์™„๋ฃŒ(v4.6.0), 4ยท5ยท7 = ์ด ๋ธŒ๋žœ์น˜, 8 = ๋ณด๋ฅ˜๋กœ ๊ฐฑ์‹ ๋๊ณ , ์‹คํ–‰ ์˜ˆ์‹œ๊ฐ€ `worktree_prepare.sh`/`worktree_remove.sh` ๊ธฐ์ค€์œผ๋กœ ๋‹ค์‹œ ์“ฐ์˜€๋‹ค. ํ˜ธํ™˜์„ฑ ํ‘œ์— resume ์˜ exit 2 ์กฐ๊ฑด, remote ๋ฆฌ๋ทฐ์–ด ๊ฑฐ๋ถ€/opt-in, "์‹ค์ œ herdr pane ๊ฐ’ ํ™•์ธ์€ ๋‹จ๊ณ„ C" ๋ผ๋Š” ํ•œ๊ณ„๊ฐ€ ๋ช…์‹œ๋ผ ๊ณผ์žฅ ์—†์ด ์ •ํ™•ํ•˜๋‹ค. - `multi-agent-mux-loop/SKILL.md` ๊ฐ€ `--review-isolation`ยท`--remote-review` ๋ฅผ ํ‘œ์— ์ถ”๊ฐ€ํ•˜๊ณ  ๊ธฐ๋ณธ๊ฐ’ยท๊ฑฐ๋ถ€ ์‹œ์ (์œ„์ž„ ์ด์ „)ยทevidence ๊ธฐ๋ก๊นŒ์ง€ ์ ๋Š”๋‹ค. create/resume/stop SKILL.md ๋„ ํ—ฌํผ๋ฅผ ๋ฐ˜์˜ํ•œ๋‹ค. - PR ๋ณธ๋ฌธ์˜ **๋น„์ฐจ๋‹จ ๊ถŒ๊ณ  4๊ฑด**์„ ์ฝ”๋“œ์—์„œ ํ™•์ธํ–ˆ๊ณ  ๋ชจ๋‘ ์‚ฌ์‹ค์ด๋‹ค: โ‘  A4 ์˜ `_main_state` ๋น„๊ต๊ฐ€ untracked `.agents/`ยท`.mam/` ๋ณ€ํ™”๋ฅผ ๋ณด์ง€ ๋ชปํ•จ, โ‘ก A4 ๊ฐ€ `info/exclude` ์ถ”๊ฐ€๋ฅผ **์„ ๋ฐ˜์˜**ํ•œ ๋’ค ๋น„๊ตํ•จ(`test_issue21_acceptance.py:121-122`), โ‘ข `_close_review_tree` ์˜ remove ์‹คํŒจ ์‹œ `prune` ์ด ์ „์—ญ, โ‘ฃ `worktree_remove.sh` ์˜ ์„œ๋ธŒ๋ชจ๋“ˆ ๊ฑฐ๋ถ€ ์ถœ๋ ฅ ์ค‘๋ณต. ๋„ท ๋‹ค ๋ณ‘ํ•ฉ์„ ๋ง‰์„ ์„ฑ์งˆ์ด ์•„๋‹ˆ๋ฉฐ ํ›„์†์œผ๋กœ ์ ์ ˆํ•˜๋‹ค. - #30(๋ฉˆ์ถ˜ ์—์ด์ „ํŠธ ๊ฐ์ง€) ๋“ฑ๋ก์€ ํƒ€๋‹นํ•˜๋‹ค. #18 ์ด "ํ”„๋กœ์„ธ์Šค ์ƒ์กด"์— ๊ฒฐ์†๋œ ์„ค๊ณ„๋ผ "์‚ด์•„ ์žˆ์ง€๋งŒ ๋ฉˆ์ถ˜" ์ƒํƒœ๋Š” ๊ทธ ๊ณ„์•ฝ ๋ฐ–์ด๋ฉฐ, ํ•ด๊ฒฐ์€ ์•Œ๋ฆผยท์šด์˜ ์ˆ˜์ค€์ด๋ฏ€๋กœ ๋ณ„๋„ ์ด์Šˆ๊ฐ€ ๋งž๋‹ค. ## 8. ์ „์ฒด ์Šค์œ„ํŠธ ์‹คํ–‰ (์ด ๋ฆฌ๋ทฐ์–ด๊ฐ€ ์ง์ ‘, ์›ŒํฌํŠธ๋ฆฌ์—์„œ) ``` cd && PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q -rfE tests/ โ†’ 2 failed, 1011 passed, 2 skipped in 2266.28s (0:37:46) (HEAD 46ea4cc, ์‹คํ–‰ ์ „ํ›„ ๋™์ผ) ``` | ์‹คํŒจ | ํŒ์ • | |---|---| | `test_issue14_delivery_ack.py::test_wrapper_idle_grace_never_outlives_the_wall_clock` | **PR ํšŒ๊ท€ ์•„๋‹˜**. ์ด ์‹œํ—˜์€ PR ์ด ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•˜๋‹ค(`git diff -U0` ์˜ issue14 hunk ๋Š” ์ „๋ถ€ `_job_for_inprocess_publish` ๋’ค์ชฝ `:520` ์ดํ›„). ๋‹จ๋… ์žฌ์‹คํ–‰์—์„œ๋„ close_delay 28.5 s(์ž„๊ณ„ 20+8)๋กœ ์žฌํ˜„๋˜๋ฉฐ, **๋‹ค๋ฅธ ๋ธŒ๋žœ์น˜์— ์ด๋ฏธ `90a289d test(issue-14): widen the wall-clock close margin under suite load` ๊ฐ€ ์žˆ๋‹ค** โ€” ์ž„๊ณ„ ์ž์ฒด๊ฐ€ ์ข๋‹ค๋Š” ๊ธฐ์ง€ ๋ฌธ์ œ๋‹ค | | `test_issue19_subscriber_daemon_lifecycle.py::test_mqtt_loop_sigterm_exits_cleanly` | **๋ธŒ๋กœ์ปค flake**. ํŒŒ์ผ ์ž์ฒด๊ฐ€ PR ๋ณ€๊ฒฝ 0์ค„์ด๊ณ , ๋กœ๊ทธ๊ฐ€ `broker did not accept connection; falling back to polling` ์ด๋‹ค. ๋‹จ๋… ์žฌ์‹คํ–‰ **ํ†ต๊ณผ** | - PR ๋ณธ๋ฌธ์˜ "์ด ํ˜ธ์ŠคํŠธ ๊ธฐ์ค€ ์‹คํŒจ 33๊ฑด(#28)" ์ฃผ์žฅ์€ **๋‚ด ์‹คํ–‰์œผ๋กœ๋Š” ํ™•์ธ๋˜์ง€ ์•Š๋Š”๋‹ค**(๋‚ด ์กฐ๊ฑด์—์„œ๋Š” 2๊ฑด). ์ œ์–ด ํ‰๋ฉด ๋ฐ”์ธ๋”ฉ์ด ์‹ค๋ฆฐ creator pane ์กฐ๊ฑด์—์„œ ์žฌํ˜„๋œ ์ˆ˜์น˜๋กœ ๋ณด์ด๋ฉฐ, ๋‚ด๊ฐ€ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์กฐ๊ฑด์ด ์•„๋‹ˆ๋ฏ€๋กœ ๊ทธ ์ˆ˜์น˜์˜ ์ฐธยท๊ฑฐ์ง“์„ ์ฃผ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ **๋‚ด ์กฐ๊ฑด์—์„œ ์ƒˆ ํšŒ๊ท€๊ฐ€ 0 ์ด๋ผ๋Š” ๊ฒƒ**์ด๋‹ค. ### ๊ฒฉ๋ฆฌยท์œ„์ƒ ์‹ค์ธก (#20, ๋ธŒ๋ฆฌํ”„ ํ•ญ๋ชฉ 4) | ํ•ญ๋ชฉ | ๊ฒฐ๊ณผ | |---|---| | ์‹ค์ œ `.mam/jobs`(๋ณธ ์ฒดํฌ์•„์›ƒ) ์ „ํ›„ | **์ฐจ์ด 0** (๋ ˆ์ฝ”๋“œ ์ถ”๊ฐ€ 0๊ฑด) | | ์‹ค์ œ `.mam/delegate_job_logs` ์ „ํ›„ | **์ฐจ์ด 0** | | ์›ŒํฌํŠธ๋ฆฌ `git status --short --ignored` ์ „ํ›„ | **์™„์ „ ๋™์ผ**(๋ฌด์‹œ ํŒŒ์ผ ํฌํ•จ) โ€” ์Šค์œ„ํŠธ๊ฐ€ ์›ŒํฌํŠธ๋ฆฌ์— ์•„๋ฌด๊ฒƒ๋„ ๋‚จ๊ธฐ์ง€ ์•Š์•˜๋‹ค | | ์›ŒํฌํŠธ๋ฆฌ HEAD | ์‹คํ–‰ ์ „ํ›„ `46ea4cc` | | ๋ณธ ์ฒดํฌ์•„์›ƒ | `main @ 1134d74`, ํด๋ฆฐ | ์ด์ „ ํšŒ์ฐจ(PR #22)์—์„œ ๋‚ด๊ฐ€ ์ง€์ ํ–ˆ๋˜ "๋ฆฌ๋ทฐ์–ด pytest ๊ฐ€ ์›ŒํฌํŠธ๋ฆฌ์— `.mam/delegate_job_logs`ยท`.mam/shim` ์„ ๋‚จ๊ธด๋‹ค" ๊ฐ€ ์ด๋ฒˆ ์‹คํ–‰์—์„œ๋Š” **์žฌํ˜„๋˜์ง€ ์•Š์•˜๋‹ค**. #20 ์ˆ˜์ •์ด ์˜๋„๋Œ€๋กœ ๋™์ž‘ํ•œ๋‹ค. ## 9. ๋ณ‘ํ•ฉ ์ฐจ๋‹จ ์—ฌ๋ถ€ (๋ธŒ๋ฆฌํ”„ ํ•ญ๋ชฉ 8) ์•Œ๋ ค์ง„ ๋ฒ„์ „ bump ๋ฏธํฌํ•จ(์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๊ฐ€ v4.7.0 ์œผ๋กœ ์ฒ˜๋ฆฌ) ์™ธ์— **๋ณ‘ํ•ฉ์„ ๋ง‰์„ ๊ฒฐํ•จ์€ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค**. N1โ€“N9 ๋Š” ๋ชจ๋‘ ๋น„์ฐจ๋‹จ์ด๋ฉฐ, ํ›„์† ์ˆœ์œ„๋Š” N1 > N2 > N5 ์ˆœ์œผ๋กœ ๋ณธ๋‹ค. | # | ์ง€์  | ์„ฑ๊ฒฉ | |---|---|---| | N1 | ์Šค๋ƒ…์ƒท ์ปค๋ฐ‹ยทtree ๊ฐ€ ref ์—†์ด ๋‚จ์•„ gc ๋กœ ์†Œ๋ฉธ โ†’ `identity.json` ์‚ฌํ›„ ๊ฒ€์ฆ ๋ถˆ๊ฐ€(์žฌํ˜„ ํ™•์ธ) | ํ›„์† ์ด์Šˆ ๊ถŒ๊ณ  | | N2 | prepare ์˜ `.venv` ๋Š” ์—ฌ์ „ํžˆ ์‹ฌ๋งํฌ(์‹ค์ œ venv/์ธํ„ฐํ”„๋ฆฌํ„ฐ ๊ฒฝ๋กœ ์•„๋‹˜); ์ปค๋ฐ‹ ๊ฒฝ๋กœ๋Š” 3์ค‘์œผ๋กœ ๋‹ซํ˜”์œผ๋‚˜ `rm -rf .venv/`ยท์˜์กด์„ฑ ๊ณต์œ  ์œ„ํ—˜ ์ž”์กด | ๋ฌธ์„œํ™” ๋˜๋Š” ์˜ต์…˜ํ™” ๊ถŒ๊ณ  | | N3 | prepare ๊ฐ€ ๊ณต์œ  `info/exclude` ๋ฅผ ๋ฐ”๊ฟ” ๋ณธ ์ฒดํฌ์•„์›ƒ์—๋„ ์ ์šฉ(๋˜๋Œ๋ฆผ ๊ฒฝ๋กœ ์—†์Œ) | ๊ธฐ์ง€ยท๋ฌธ์„œํ™”๋จ | | N4 | issue15 ๋ˆ„์ˆ˜ ๋‹จ์–ธ์ด ์ „์ฒด ๋ชฉ๋ก ๋น„๊ต์—์„œ ์ž๊ธฐ ์žก id + ์„ธ์…˜ ์Šค์บ”์œผ๋กœ ์•ฝํ™”(๊ทผ๊ฑฐ ํƒ€๋‹น) | ์ˆ˜์šฉ | | N5 | acceptance5 ์˜ skip ๊ฒฝ๋กœ๊ฐ€ ๊ตฌ๋…์ž ๋ฐฐ๋‹ฌ ํšŒ๊ท€๋ฅผ ๊ฐ€๋ฆด ์—ฌ์ง€(์กฐ๊ฑด์€ ๋งค์šฐ ์ข์Œ), `--heartbeat-misses 3โ†’10` | ์ˆ˜์šฉ | | N6 | SIGKILL ์‹œ ๋ฆฌ๋ทฐ ์›ŒํฌํŠธ๋ฆฌยทworktree ํ•ญ๋ชฉ ์ž”์กด(prune ์€ ์‹คํŒจ ์‹œ์—๋งŒ) | ์‚ฌ์†Œ | | N7 | remove ์˜ ์—ด๋ฆฐ ์žก ๊ฒ€์‚ฌ๊ฐ€ ํ–‰ ์—†๋Š” ์›ŒํฌํŠธ๋ฆฌ ๋กœ์ปฌ `.mam/jobs` ๋ฅผ ๋ชป ๋ด„ | ๋ชจ๋ธ ๋ฐ– ๊ฒฝ๋กœ | | N8 | remove ๋Š” plain/force ๋ชจ๋‘ ๋ฌด์‹œ ํŒŒ์ผ์„ ๋ง์—†์ด ์‚ญ์ œ(์ถ”์  ์ž‘์—… ์†์‹ค์€ ์—†์Œ) | ์ •๋ณด | | N9 | ์˜ต์…˜ ์—†๋Š” ๋ฃจํ”„์—๋„ `Loop id:` ๋กœ๊ทธ 1์ค„๊ณผ ์ฒดํฌํฌ์ธํŠธ `loop_id` ํ•„๋“œ๊ฐ€ ์ถ”๊ฐ€(ํ”„๋กฌํ”„ํŠธ๋Š” ๋ฐ”์ดํŠธ ๋™์ผ) | ์ •๋ณด | ## 10. ๊ต์ฐจ๊ฒ€์ฆ ๊ฐ€์ด๋“œ ```bash WT=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29 (cd "$WT" && PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q -rfE tests/) # 2 failed / 1011 passed (cd "$WT" && PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q \ tests/test_issue14_delivery_ack.py::test_wrapper_idle_grace_never_outlives_the_wall_clock \ tests/test_issue19_subscriber_daemon_lifecycle.py::test_mqtt_loop_sigterm_exits_cleanly) # issue19 ํ†ต๊ณผ, issue14 ์žฌํ˜„ git -C "$WT" diff 1134d74...HEAD -U0 -- tests/test_issue14_delivery_ack.py | grep '^@@' # ์‹คํŒจ ์‹œํ—˜์€ ๋ณ€๊ฒฝ ๋ฐ– # N1 ์žฌํ˜„ (์ž„์‹œ ์ €์žฅ์†Œ์—์„œ๋งŒ) R=$(mktemp -d); cd "$R"; git init -q .; git config user.email t@t; git config user.name t echo a > a; git add a; git commit -qm base; H=$(git rev-parse HEAD); echo dirty > b GIT_INDEX_FILE=$R/idx git read-tree HEAD; GIT_INDEX_FILE=$R/idx git add -A; T=$(GIT_INDEX_FILE=$R/idx git write-tree) C=$(git commit-tree "$T" -p "$H" -m snap); git worktree add -q --detach "$R/wt" "$C"; git worktree remove --force "$R/wt" git reflog expire --expire=now --all; git gc --prune=now -q; git cat-file -e "$C^{commit}" || echo "snapshot gone" ``` [VERDICT: PASS]