From cf5d1142e97079022cbaa151ab30fc0a5016d601 Mon Sep 17 00:00:00 2001 From: Godopu Date: Sun, 20 Sep 2026 21:43:25 +0900 Subject: [PATCH] release(v4.7.0): bump framework and eight skills to 4.7.0 in lockstep; archive PR #29 reviews Issue #21 items 4, 5, 7 (worktree helpers, review identity, acceptance A1-A8), #20 test isolation, #27 .gitignore managed block, #28 host portability. VERSIONS.md carries the entry; the eight review reports of PR #29 (two rounds plus the codex re-review) are archived under .agents/reports//. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff --- .../report-291e38b5.md | 170 ++++++++++++++++ .../report-f1174fc0.md | 189 ++++++++++++++++++ .../report-b880c1f2.md | 71 +++++++ .../report-f6b21a0d.md | 39 ++++ .../reviewer-agy-01/report-d9275506.md | 87 ++++++++ .../reviewer-agy-01/report-f27519ff.md | 122 +++++++++++ .../reviewer-grok-01/report-19d56cde.md | 62 ++++++ .../reviewer-grok-01/report-5f1486ee.md | 63 ++++++ .agents/skills/lib.sh | 2 +- .agents/skills/lib_py/loop/eager.py | 2 +- .../skills/multi-agent-mux-create/SKILL.md | 2 +- .../multi-agent-mux-delegate-job/SKILL.md | 2 +- .agents/skills/multi-agent-mux-loop/SKILL.md | 2 +- .../skills/multi-agent-mux-monitor/SKILL.md | 2 +- .../multi-agent-mux-orc-onboard/SKILL.md | 2 +- .../skills/multi-agent-mux-resume/SKILL.md | 2 +- .../skills/multi-agent-mux-status/SKILL.md | 2 +- .agents/skills/multi-agent-mux-stop/SKILL.md | 2 +- BOOTSTRAP.ko.md | 2 +- BOOTSTRAP.md | 2 +- MESSAGING.md | 2 +- README.ko.md | 2 +- README.md | 2 +- VERSIONS.md | 42 +++- 24 files changed, 849 insertions(+), 26 deletions(-) create mode 100644 .agents/reports/planner-reviewer-claude-01/report-291e38b5.md create mode 100644 .agents/reports/planner-reviewer-claude-01/report-f1174fc0.md create mode 100644 .agents/reports/planner-reviewer-codex-01/report-b880c1f2.md create mode 100644 .agents/reports/planner-reviewer-codex-01/report-f6b21a0d.md create mode 100644 .agents/reports/reviewer-agy-01/report-d9275506.md create mode 100644 .agents/reports/reviewer-agy-01/report-f27519ff.md create mode 100644 .agents/reports/reviewer-grok-01/report-19d56cde.md create mode 100644 .agents/reports/reviewer-grok-01/report-5f1486ee.md diff --git a/.agents/reports/planner-reviewer-claude-01/report-291e38b5.md b/.agents/reports/planner-reviewer-claude-01/report-291e38b5.md new file mode 100644 index 0000000..77cc861 --- /dev/null +++ b/.agents/reports/planner-reviewer-claude-01/report-291e38b5.md @@ -0,0 +1,170 @@ +# ๐Ÿ” 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] diff --git a/.agents/reports/planner-reviewer-claude-01/report-f1174fc0.md b/.agents/reports/planner-reviewer-claude-01/report-f1174fc0.md new file mode 100644 index 0000000..670657d --- /dev/null +++ b/.agents/reports/planner-reviewer-claude-01/report-f1174fc0.md @@ -0,0 +1,189 @@ +# ๐Ÿ” Review Report โ€” PR #29 ๋ธํƒ€ ๋ฆฌ๋ทฐ 2ํšŒ์ฐจ (job `f1174fc0`) + +- **Reviewer**: `planner-reviewer-claude-01` (role: planner,reviewer) ยท 2026-09-20 +- **๋Œ€์ƒ ๋ธํƒ€**: `46ea4cc..08db1da` (3์ปค๋ฐ‹, `git diff --stat` = **16 files, +159/โˆ’34**), base `main 1134d74` (v4.6.0). +- **๊ฒ€ํ†  ํŠธ๋ฆฌ**: ์›ŒํฌํŠธ๋ฆฌ `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` (detached @ `08db1da`). +- **1ํšŒ์ฐจ ๋‚ด ํŒ์ •**: PASS (job `291e38b5`). ์ด๋ฒˆ์—๋Š” **๋ธํƒ€๊ฐ€ ๊ทธ ํŒ์ •์„ ๋’ค์ง‘๋Š”์ง€**๋ฅผ ๋ณธ๋‹ค. +- **์•ˆ์ „ ๊ทœ์น™ ์ค€์ˆ˜**: ์›ŒํฌํŠธ๋ฆฌ ์ฝ๊ธฐ ์ „์šฉ(์ˆ˜์ •ยท์ปค๋ฐ‹ยทcheckout ์—†์Œ, `git status --short` ํด๋ฆฐ ์œ ์ง€). ๋ณธ ์ฒดํฌ์•„์›ƒ `main @ 1134d74` ๊ทธ๋Œ€๋กœ. `worktree_prepare.sh`ยท`worktree_remove.sh` ๋ฅผ ์‹ค์ œ ์›ŒํฌํŠธ๋ฆฌยท๋ณธ ์ฒดํฌ์•„์›ƒ์— ์‹คํ–‰ํ•˜์ง€ ์•Š์•˜๋‹ค โ€” ๋ชจ๋“  ์‹คํ–‰์€ scratchpad ์˜ ์ž„์‹œ ๋ณต์‚ฌ๋ณธ/์ž„์‹œ ์ €์žฅ์†Œ(uuid4 ์ด๋ฆ„)์—์„œ๋งŒ ํ–ˆ๋‹ค. ์‹ค์ œ `.mam`ยท๋ฐ๋ชฌยท์„ธ์…˜ ์กฐ์ž‘ ์—†์Œ, pushยทmergeยทPR ์ƒํƒœ ๋ณ€๊ฒฝ ์—†์Œ, purgeยทscrub ์‹คํ–‰ ์—†์Œ. + +## ๊ฒฐ๋ก  ์š”์•ฝ + +**R1ยทR2ยทR3 ์€ ์‹ค์ œ๋กœ ๋‹ซํ˜”๋‹ค.** ํŠนํžˆ R1/R2 ๋Š” (a) ๊ฒฐํ•จ์„ ์ž„์‹œ ์ €์žฅ์†Œ์—์„œ ์ง์ ‘ ์žฌํ˜„ํ–ˆ๊ณ , (b) ์›ŒํฌํŠธ๋ฆฌ ๋ณต์‚ฌ๋ณธ์—์„œ ์ˆ˜์ •๋งŒ ๋˜๋Œ๋ ธ์„ ๋•Œ ์ƒˆ ํšŒ๊ท€ 4๊ฑด์ด ์ •ํ™•ํžˆ ์‹คํŒจํ•˜๋Š” ๊ฒƒ๊นŒ์ง€ ํ™•์ธํ–ˆ๋‹ค. 1ํšŒ์ฐจ PASS ๋ฅผ ๋’ค์ง‘์„ ์‚ฌ์œ ๋Š” ์—†๋‹ค. ๋น„์ฐจ๋‹จ 4๊ฑด(D1โ€“D4)๋งŒ ๋‚จ๊ธด๋‹ค. ๊ทธ์ค‘ D1 ์€ ์ด๋ฒˆ ๋ธํƒ€๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“  ์ž‘์€ ๊ตฌ๋ฉ์ด๋‹ค. + +## 1. R1/R2 โ€” `--untracked-files=all` (ํ•ญ๋ชฉ 1) + +### 1-1. ๊ฒฐํ•จ ์žฌํ˜„ (์ž„์‹œ ์ €์žฅ์†Œ) + +``` +git config status.showUntrackedFiles no ; echo secret > untracked.txt +git status --porcelain --ignore-submodules=none โ†’ (๋นˆ ์ถœ๋ ฅ) โ† ์˜› ๊ฒ€์‚ฌ: "ํด๋ฆฐ" +git status --porcelain --untracked-files=all --ignore-submodules=none โ†’ ?? untracked.txt โ† ์ˆ˜์ •๋ณธ +``` + +ํ‘œ์‹œ ์„ค์ • ํ•˜๋‚˜๋กœ "๊ฐ•์ œ ์ œ๊ฑฐํ•ด๋„ ๋˜๋Š”๊ฐ€"๋ผ๋Š” ํŒ์ •์ด ๋’ค์ง‘ํ˜”๋‹ค๋Š” codex ์˜ ์ง€์ ์€ ์‚ฌ์‹ค์ด๋‹ค. + +### 1-2. ์„ธ ๊ฒ€์‚ฌ ๋ชจ๋‘์— ๋“ค์–ด๊ฐ”๋Š”๊ฐ€ โ€” ๊ทธ๋ ‡๋‹ค + +| ํŒŒ์ผ:๋ผ์ธ | ๊ฒ€์‚ฌ | +|---|---| +| `.agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh:93` | `judge_tree()` ์˜ ์›ŒํฌํŠธ๋ฆฌ ์ฒญ๊ฒฐ ๊ฒ€์‚ฌ | +| `.agents/skills/lib_py/worktree.py:170` | `submodule_findings()` ์˜ `dirty` ๊ฒ€์‚ฌ(`modules/` ์•„๋ž˜ ๋ชจ๋“  ์ €์žฅ์†Œ) | +| `.agents/skills/lib_py/loop/runner.py:718` | `_dirty_submodules()` (๋ฆฌ๋ทฐ ์Šค๋ƒ…์ƒท ์Šน์ธ ์ „ ์„œ๋ธŒ๋ชจ๋“ˆ ๊ฒ€์‚ฌ) | + +์Šคํ‚ฌ ํŠธ๋ฆฌ ์ „์ฒด์—์„œ `status --porcelain` ์„ ๋ถ€๋ฅด๋Š” ๋‚˜๋จธ์ง€ ๋‘ ๊ณณ๋„ ํ™•์ธํ–ˆ๋‹ค. +- `worktree_prepare.sh:209`: ๋กค๋ฐฑ ํ›„ "์ด ์›ŒํฌํŠธ๋ฆฌ๊ฐ€ ์•„์ง ์“ธ ์ˆ˜ ์žˆ๋Š” ์ฒดํฌ์•„์›ƒ์ธ๊ฐ€"๋งŒ ๋ณด๋Š” ์šฉ๋„(stdout ์€ ๋ฒ„๋ฆฌ๊ณ  stderr ๋งŒ ๋ณธ๋‹ค). ํŒŒ๊ดด ํŒ์ •์ด ์•„๋‹ˆ๋‹ค โ€” ํ”Œ๋ž˜๊ทธ ๋ถˆํ•„์š”. +- `runner.py:581`: ํ”„๋กฌํ”„ํŠธ์˜ `๋ฏธ์ปค๋ฐ‹ ๋ณ€๊ฒฝ: ์žˆ์Œ/์—†์Œ` **ํ‘œ์‹œ์šฉ**. ํŒ์ •์— ์“ฐ์ด์ง€ ์•Š๋Š”๋‹ค(โ†’ D3 ์ฐธ์กฐ). + +### 1-3. ์†Œ์Šค ํŠธ๋ฆฌ ์Šค๋ƒ…์ƒท์€ ๋ณ„๋„ ์กฐ์น˜๊ฐ€ ํ•„์š” ์—†๋Š”๊ฐ€ โ€” ํ•„์š” ์—†๋‹ค + +`_worktree_tree()` ์˜ `git add -A` ๋Š” `status.showUntrackedFiles` ๋ฅผ **๋ณด์ง€ ์•Š๋Š”๋‹ค**. ์ž„์‹œ ์ €์žฅ์†Œ์—์„œ `showUntrackedFiles=no` ๋กœ ์„ค์ •ํ•œ ๋’ค `read-tree HEAD` โ†’ `add -A` โ†’ `write-tree` ํ•œ ํŠธ๋ฆฌ์— `untracked.txt` ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋“ค์–ด๊ฐ”๋‹ค. ์ฆ‰ ์Šค๋ƒ…์ƒท์€ ์ด๋ฏธ untracked ๋ฅผ ๊ณ ์ •ํ•˜๊ณ  ์žˆ์—ˆ๊ณ , ๊ตฌ๋ฉ์€ "์„œ๋ธŒ๋ชจ๋“ˆ ๊ฒ€์‚ฌ"์™€ "์ œ๊ฑฐ ํŒ์ •"์—๋งŒ ์žˆ์—ˆ๋‹ค. ์ˆ˜์ • ๋ฒ”์œ„๊ฐ€ ์ •ํ™•ํ•˜๋‹ค. + +`_change_set_state()`(`runner.py`)์™€ `diff_collect.sh` ์˜ untracked ์ˆ˜์ง‘์€ `git ls-files -o --exclude-standard` ๋ผ ์ด ์„ค์ •์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค. ๋นˆ ๋ณ€๊ฒฝ๋ถ„ fail-closed ๊ฐ€๋“œ๋„ ์•ˆ์ „ํ•˜๋‹ค. + +### 1-4. ์ƒˆ ํšŒ๊ท€๊ฐ€ ์ˆ˜์ • ์—†์ด๋Š” ์‹คํŒจํ•˜๋Š”๊ฐ€ โ€” ํ™•์ธํ•จ + +์›ŒํฌํŠธ๋ฆฌ๋ฅผ `.git`ยท`.venv` ์ œ์™ธํ•˜๊ณ  scratchpad ์— ๋ณต์‚ฌํ•œ ๋’ค, **์„ธ ๊ณณ์˜ `--untracked-files=all` ๋งŒ ์ œ๊ฑฐ**ํ•˜๊ณ  ๋Œ๋ ธ๋‹ค(์›ŒํฌํŠธ๋ฆฌ ์ž์ฒด๋Š” ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•˜๋‹ค). + +``` +4 failed, 5 passed in 14.69s +FAILED test_issue21_phase2_loop_a.py::test_w6e2_untracked_submodule_file_hidden_by_config_blocks_the_forced_removal +FAILED test_issue21_phase2_loop_a.py::test_w6e_work_written_while_stopping_is_not_force_removed[True] +FAILED test_issue21_loop_b.py::test_l17_dirty_submodule_is_refused_or_makes_the_round_stale[untracked-hidden] +FAILED test_issue21_loop_b.py::test_l17_dirty_submodule_is_refused_or_makes_the_round_stale[during-hidden] +``` + +๊ธฐ์กด ๋ณ€ํ˜•(`[False]`, `before`, `before-ignored`, `untracked`, `during`)์€ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ–ˆ๋‹ค. ์ฆ‰ ์ƒˆ ํšŒ๊ท€๋Š” **์ •ํ™•ํžˆ ์ด ์ˆ˜์ •๋งŒ** ๊ฒ€์ฆํ•˜๋ฉฐ, ํšŒ๊ท€ ๋ฐฉ์ง€๋ ฅ์ด ์‹ค์žฌํ•œ๋‹ค. `test_w6e2` ๊ฐ€ ๋‹จ์–ธ ์•ˆ์—์„œ `assert _git(wt,"status","--porcelain","--ignore-submodules=none").stdout == "", "git hides it by default"` ๋กœ ์ „์ œ ์ž์ฒด๋ฅผ ๊ณ ์ •ํ•œ ๊ฒƒ๋„ ์ข‹๋‹ค. + +## 2. R3 โ€” ์›ŒํฌํŠธ๋ฆฌ ์ž์ฒด venv (ํ•ญ๋ชฉ 2) + +`worktree_prepare.sh:111-134` ์˜ ๋ถ„๊ธฐ๋ฅผ ์ฝ๊ณ  ๋ฌธ์„œยทํ…Œ์ŠคํŠธ์™€ ๋Œ€์กฐํ–ˆ๋‹ค(์Šคํฌ๋ฆฝํŠธ๋Š” ์‹ค์ œ ํŠธ๋ฆฌ์— ์‹คํ–‰ํ•˜์ง€ ์•Š์•˜๋‹ค). + +| ์š”๊ตฌ | ๊ตฌํ˜„ | ํŒ์ • | +|---|---|---| +| ์‹ฌ๋งํฌ๊ฐ€ ์•„๋‹Œ ์‹ค์ œ venv | `"$MAIN/.venv/bin/python" -m venv "$WT/.venv"` + `requirements.txt`ยท`requirements-dev.txt` ์„ค์น˜ | **์ถฉ์กฑ**. 1ํšŒ์ฐจ ๋‚ด ์ง€์  N2 ๊ฐ€ ๋‹ซํ˜”๋‹ค | +| ๋ฉฑ๋“ฑ์„ฑ | `[ -x "$WT/.venv/bin/python" ]` โ†’ `VENV_STATE="kept"` | ์ถฉ์กฑ(`test_w1` ์ด `VENV=created` โ†’ `VENV=kept` ์ˆœ์„œ๋ฅผ ๊ณ ์ •) | +| ๋‚จ์€ ๋งํฌ ๊ฑฐ๋ถ€ | `[ -L "$WT/.venv" ]` โ†’ exit 1, **์•„๋ฌด๊ฒƒ๋„ ๋ฐ”๊พธ์ง€ ์•Š์Œ** | ์ถฉ์กฑ(`test_w1b` ๊ฐ€ ๋งํฌ ๋ณด์กด๊นŒ์ง€ ๋‹จ์–ธ) | +| ์ธํ„ฐํ”„๋ฆฌํ„ฐ ๋ถ€์žฌ | `WARNING: ... not found; no venv created` + `VENV=skipped` | ์ถฉ์กฑ | +| ์„ค์น˜ ์‹คํŒจ ์‹œ exit 1, ์›ŒํฌํŠธ๋ฆฌ ์œ ์ง€ | `pip install` ์‹คํŒจ โ†’ ๋ฉ”์‹œ์ง€ `(the worktree is kept)` + exit 1. `git worktree add` ๋Š” ์ด๋ฏธ ๋๋‚œ ๋’ค๋ผ ์‹ค์ œ๋กœ ๋‚จ๋Š”๋‹ค | ์ถฉ์กฑ | +| ๋ฌธ์„œ ์ •ํ•ฉ | `create/SKILL.md:246`, `stop/SKILL.md:123`, ์„ค๊ณ„ ๋ฌธ์„œ 4์žฅ์ด ๋ชจ๋‘ "์ž์ฒด venvยท๋งํฌ ๊ฑฐ๋ถ€ยท`VENV=` ์š”์•ฝ"์œผ๋กœ ๊ฐฑ์‹ ๋จ | ์ถฉ์กฑ | +| ignore ๊ทœ์น™ ์ •ํ•ฉ | `.gitignore` ๊ด€๋ฆฌ ๋ธ”๋ก์˜ `/.venv`(#27) ์™€ ๊ณต์œ  `info/exclude` ์˜ `/.venv` ๋Š” **๋””๋ ‰ํ„ฐ๋ฆฌ์—๋„ ๊ทธ๋Œ€๋กœ ์ ์šฉ**๋œ๋‹ค. `test_w1` ์ด ์ค€๋น„ ํ›„ `status --porcelain` ์ด ๋น„๋Š”์ง€ ๋‹จ์–ธ | ์ถฉ์กฑ | + +`bash -n` ์€ ๋ณ€๊ฒฝ๋œ ๋‘ ์Šคํฌ๋ฆฝํŠธ ๋ชจ๋‘ ํ†ต๊ณผํ•œ๋‹ค. `req=()` ๋ฐฐ์—ด + `[ -f ... ] && req+=(...)` ๊ฐ€ `set -euo pipefail` ์•„๋ž˜์„œ ์กฐ๊ธฐ ์ข…๋ฃŒํ•˜์ง€ ์•Š๋Š”์ง€ macOS `/bin/bash` 3.2.57 ๋กœ ์ง์ ‘ ํ™•์ธํ–ˆ๋‹ค(๋‘ ํŒŒ์ผ ๋ชจ๋‘ ์—†์„ ๋•Œ๋„ ๋ฃจํ”„ ๋๊นŒ์ง€ ๋„๋‹ฌ, rc 0). + +### D1 (๋น„์ฐจ๋‹จ, ์ด๋ฒˆ ๋ธํƒ€๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“  ๊ตฌ๋ฉ) โ€” ์„ค์น˜ ์‹คํŒจ ๋’ค ์žฌ์‹คํ–‰์ด ์กฐ์šฉํžˆ `VENV=kept` + +`pip install` ์ด ์‹คํŒจํ•˜๋ฉด exit 1 ์ด์ง€๋งŒ `$WT/.venv/bin/python` ์€ **์ด๋ฏธ ๋งŒ๋“ค์–ด์ ธ ์žˆ๋‹ค**(`:121` ์—์„œ venv ์ƒ์„ฑ์ด ๋จผ์ € ๋๋‚œ๋‹ค). ๊ทธ๋ž˜์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ทธ๋Œ€๋กœ ์žฌ์‹คํ–‰ํ•˜๋ฉด `:117-118` ์˜ `-x` ๊ฒ€์‚ฌ์— ๊ฑธ๋ ค `VENV=kept` ๋กœ ๋„˜์–ด๊ฐ€๊ณ , **์š”๊ตฌ์‚ฌํ•ญ์ด ์„ค์น˜๋˜์ง€ ์•Š์€ ํ™˜๊ฒฝ**์„ ์ •์ƒ์œผ๋กœ ๋ณด๊ณ ํ•œ๋‹ค. ๋ฉ”์‹œ์ง€๋Š” "fix it and re-run" ์ด๋ผ ์žฌ์‹คํ–‰์„ ์œ ๋„ํ•œ๋‹ค. + +โ†’ ๊ถŒ๊ณ : ์„ค์น˜ ์„ฑ๊ณต์„ ํ‘œ์‹œํ•˜๋Š” ๋งˆ์ปค(์˜ˆ: `$WT/.venv/.mam-requirements-ok`)๋ฅผ ๋‘๊ณ  `kept` ํŒ์ •์— ํ•จ๊ป˜ ์“ฐ๊ฑฐ๋‚˜, `kept` ๊ฒฝ๋กœ์—์„œ๋„ `requirements*.txt` ์„ค์น˜๋ฅผ ํ•œ ๋ฒˆ ๋” ์‹œ๋„. + +### D2 (๋น„์ฐจ๋‹จ, ์ •๋ณด) โ€” ์ค€๋น„ ๋น„์šฉยท๋„คํŠธ์›Œํฌ ์˜์กด + +์ด์ œ ์›ŒํฌํŠธ๋ฆฌ๋งˆ๋‹ค venv ์ƒ์„ฑ + PyPI ์„ค์น˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์˜คํ”„๋ผ์ธ์ด๊ฑฐ๋‚˜ PyPI ๊ฐ€ ๋ง‰ํžŒ ํ˜ธ์ŠคํŠธ์—์„œ๋Š” prepare ๊ฐ€ exit 1 ๋กœ ๋๋‚œ๋‹ค(์ด์ „์—๋Š” ๋งํฌ ํ•œ ๋ฒˆ์ด๋ผ ์ฆ‰์‹œ ๋๋‚ฌ๋‹ค). SKILL.md ์— ์„ค์น˜ ๋™์ž‘์€ ์ ํ˜€ ์žˆ์œผ๋‚˜ "๋„คํŠธ์›Œํฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค"๋Š” ๋ช…์‹œ๋Š” ์—†๋‹ค. + +### D3 (๋น„์ฐจ๋‹จ, ์‚ฌ์†Œ) โ€” ํ”„๋กฌํ”„ํŠธ์˜ `๋ฏธ์ปค๋ฐ‹ ๋ณ€๊ฒฝ` ํ‘œ์‹œ๋Š” ์—ฌ์ „ํžˆ ์„ค์ •์— ์ทจ์•ฝ + +`runner.py:581` ์˜ `git status --porcelain` ์—๋Š” ํ”Œ๋ž˜๊ทธ๊ฐ€ ์—†์–ด, `status.showUntrackedFiles=no` ์ธ ์†Œ์Šค์—์„œ๋Š” untracked ๋งŒ ์žˆ์„ ๋•Œ `๋ฏธ์ปค๋ฐ‹ ๋ณ€๊ฒฝ: ์—†์Œ` ์œผ๋กœ ํ‘œ์‹œ๋œ๋‹ค. ํŒ์ •ยท์Šค๋ƒ…์ƒทยท์„œ๋ธŒ๋ชจ๋“ˆ ๊ฒ€์‚ฌ๋Š” ๋ชจ๋‘ ์•ˆ์ „ํ•˜๋ฏ€๋กœ **ํ‘œ์‹œ ์˜ค์ฐจ**์ผ ๋ฟ์ด์ง€๋งŒ, R1 ์˜ ์ทจ์ง€("ํ‘œ์‹œ ์„ค์ •์ด ํŒ๋‹จ ์žฌ๋ฃŒ๋ฅผ ๋ฐ”๊พธ๋ฉด ์•ˆ ๋œ๋‹ค")๋ฅผ ์ƒ๊ฐํ•˜๋ฉด ๊ฐ™์€ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ฃผ๋Š” ํŽธ์ด ์ผ๊ด€์ ์ด๋‹ค. + +## 3. #28 ์ด์‹์„ฑ ์ˆ˜์ •(`7a94ba2`)์ด ์˜๋„๋ฅผ ์•ฝํ™”์‹œํ‚ค๋Š”๊ฐ€ (ํ•ญ๋ชฉ 3) + +### 3-1. `ps -eo pid=,ppid=,args=` โ€” ์–‘์ชฝ์—์„œ ๊ฐ™์€ ์ถœ๋ ฅ์ธ๊ฐ€ + +์ด ํ˜ธ์ŠคํŠธ(macOS 15, arm64)์—์„œ **๋‘ ํ˜•ํƒœ๋ฅผ ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค์— ๋Œ€ํ•ด ์ง์ ‘ ๋น„๊ต**ํ–ˆ๋‹ค(ํŒŒ์ดํ”„๋กœ ์บก์ฒ˜, ์ฝ”๋“œ์™€ ๋™์ผ ์กฐ๊ฑด). + +``` +['-axo','pid=,ppid=,command='] โ†’ ๊ธธ์ด 575, ์ธ์ž X 400๊ฐœ ๊ทธ๋Œ€๋กœ +['-eo','pid=,ppid=,args='] โ†’ ๊ธธ์ด 575, ์ธ์ž X 400๊ฐœ ๊ทธ๋Œ€๋กœ +``` + +- **๊ตฌ๋ถ„์žยทํ•„๋“œ**: ๋™์ผ(`pid ppid args`), ์ž˜๋ฆผ ์—†์Œ. 400์ž argv ๊ฐ€ ์˜จ์ „ํžˆ ๋‚˜์™”๋‹ค โ€” ํŒŒ์ดํ”„ ์ถœ๋ ฅ์ผ ๋•Œ๋Š” ํญ ์ œํ•œ์ด ๊ฑธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. +- **์ด์‹์„ฑ**: `-e`(๋ชจ๋“  ํ”„๋กœ์„ธ์Šค), `-o`(ํฌ๋งท), `args`(์ „์ฒด ๋ช…๋ นํ–‰)๋Š” POSIX ๋‹ค. ๋ฐ˜๋Œ€๋กœ `-a`+`-x` ์กฐํ•ฉ๊ณผ `command=` ๋Š” BSD ๊ด€์šฉ์ด๊ณ  procps ์—์„œ๋Š” ๊ฒฝ๊ณ ยท๋‹ค๋ฅธ ํ•ด์„์ด ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋‹ค. purge 12๊ฑด์˜ ์›์ธ ๊ฐ€์„ค๋กœ ํƒ€๋‹นํ•˜๋ฉฐ, **macOS ๋™์ž‘์€ ๊ทธ๋Œ€๋กœ**๋‹ค(์œ„ ์‹ค์ธก). +- ๋‚จ๋Š” ์ฐจ์ด: Linux ์˜ `args` ๋„ ์ปค๋„ `/proc//cmdline` ๊ธฐ์ค€์ด๋ผ ๋งค์šฐ ๊ธด argv ๋Š” ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ์ž˜๋ฆด ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ ์ด ์ฝ”๋“œ์˜ ํŒ์ •(์—์ด์ „ํŠธ CLI ์—ฌ๋ถ€, UUID ํฌํ•จ ์—ฌ๋ถ€)์€ ์•ž์ชฝ ํ† ํฐ์— ์˜์กดํ•˜๋ฏ€๋กœ ์‹ค์งˆ ์˜ํ–ฅ์€ ์—†๋‹ค. + +### 3-2. `_normalize_help` + +`optional arguments:` โ†’ `options:` ์น˜ํ™˜ + ๊ณต๋ฐฑ ๋Ÿฐ ์ถ•์•ฝ์ด๋‹ค. ์ฆ‰ **๋ ˆ์ด์•„์›ƒ(์ค„๋ฐ”๊ฟˆยท๋“ค์—ฌ์“ฐ๊ธฐ) ๋™๋“ฑ์„ฑ์€ ํฌ๊ธฐํ•˜๊ณ  ๋‚ด์šฉ ๋™๋“ฑ์„ฑ๋งŒ** ๋ณธ๋‹ค. ๊ณจ๋“  ์บก์ฒ˜๊ฐ€ 3.9 ์—์„œ ๋– ์กŒ๊ณ  ๋Œ€์ƒ ํ˜ธ์ŠคํŠธ๊ฐ€ 3.14 ๋ผ๋Š” ์‚ฌ์ •์ƒ ํƒ€๋‹นํ•˜๋ฉฐ, ํ”Œ๋ž˜๊ทธยท์ธ์žยท์„ค๋ช… ๋ฌธ๊ตฌ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์—ฌ์ „ํžˆ ์‹คํŒจํ•œ๋‹ค(์‹œํ—˜์˜ ๋ชฉ์ ์€ "shim ์ด ๊ตฌํ˜„๊ณผ CLI ์ƒ ๊ตฌ๋ถ„ ๋ถˆ๊ฐ€"์ด๋ฏ€๋กœ ์œ ์ง€). ์•ฝํ™”๋Š” ์ œํ•œ์ ์ด๊ณ  ๋Œ€์•ˆ(๋ฒ„์ „๋ณ„ ๊ณจ๋“  ์บก์ฒ˜)์€ ์œ ์ง€๋น„๊ฐ€ ํฌ๋‹ค โ€” ์ˆ˜์šฉ. + +### 3-3. `test_workspace_scope.py` ์˜ `bash -c` + +`shell=True` ๋Š” `/bin/sh` ๋ฅผ ์“ฐ๋ฏ€๋กœ dash ์—์„œ `source` ๊ฐ€ ์—†๋‹ค. ๋ฌธ์ž์—ด์„ ๊ทธ๋Œ€๋กœ `["bash","-c", cmd]` ๋กœ ์˜ฎ๊ธด ๊ฒƒ์€ ์ตœ์†Œ ๋ณ€๊ฒฝ์ด๋ฉฐ ๊ฒ€์ฆ ๋‚ด์šฉ(=`resolve_herdr_session` ์˜ ์ถœ๋ ฅ)์€ ๊ทธ๋Œ€๋กœ๋‹ค. ์ˆ˜์šฉ. + +### 3-4. conftest ์˜ `MAM_ENV_FILE` ๊ณ ์ • + +`tests/conftest.py:173-177` ์ด autouse ํ”ฝ์Šค์ฒ˜์—์„œ `MAM_ENV_FILE` ์„ `tmp_path/.mam-test-state/.mam.env.absent` ๋กœ ๊ณ ์ •ํ•œ๋‹ค. ๋ฐฉํ–ฅ์€ #20ยท#15 ์™€ ๊ฐ™์€ **๊ฒฉ๋ฆฌ ๊ฐ•ํ™”**์ด๊ณ , ์‹ค์ œ `.mam.env` ๋ฅผ ์ฝ์–ด์•ผ ํ•˜๋Š” ์‹œํ—˜์€ ์Šค์Šค๋กœ `MAM_ENV_FILE` ์„ ์„ค์ •ํ•œ๋‹ค(`test_issue14/18/5_phase1/21_worktree_prereqs/20_state_isolation` ๋ชจ๋‘ ์ž์ฒด ์„ค์ •์„ ๊ฐ–๊ณ  ์žˆ๋‹ค). "๋ธŒ๋กœ์ปค ์—†์Œ" ๊ณ„์—ด ์‹œํ—˜์ด ํ˜ธ์ŠคํŠธ์˜ ์‹ค ์„ค์ •์„ ์ฃผ์›Œ ์ฝ๋˜ ๊ฒƒ์ด ์˜คํžˆ๋ ค ๊ฒฐํ•จ์ด์—ˆ์œผ๋ฏ€๋กœ ๊ณ„์•ฝ์„ ์•ฝํ™”์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค. + +## 4. `90a289d` ๋ฒฝ์‹œ๊ณ„ ์—ฌ์œ  8 โ†’ 15 s (ํ•ญ๋ชฉ 4) + +์ด ์‹œํ—˜์ด ๊ณ ์ •ํ•˜๋Š” ๊ฒฐํ•จ์€ "idle grace ๊ฐ€ wall clock ์„ **๋ฌด์‹œ**ํ•ด `2 ร— TIMEOUT`(โ‰ˆ40 s) ์— ๋‹ซํžˆ๋Š” ๊ฒƒ"์ด๋‹ค. ์ž„๊ณ„๋Š” `20 + 15 = 35 s` ์ด๋ฏ€๋กœ ๊ฒฐํ•จ ์‹œ๋‚˜๋ฆฌ์˜ค(40 s)๋Š” ์—ฌ์ „ํžˆ ์‹คํŒจํ•œ๋‹ค. ์ •์ƒ ์ข…๋ฃŒ๋Š” ๋‚ด๊ฐ€ 1ํšŒ์ฐจ ์ „์ฒด ์Šค์œ„ํŠธ์—์„œ ์‹ค์ธกํ•œ ๊ฐ’์ด 28.49 sยท28.50 s(๋‹จ๋… ์žฌ์‹คํ–‰ ํฌํ•จ 2ํšŒ ์ผ์น˜)์˜€๋‹ค. + +``` +์ •์ƒ 28~29 s < ์ƒˆ ์ž„๊ณ„ 35 s < ๊ฒฐํ•จ 40 s +``` + +์—ฌ์œ ๋Š” 5 s ๋กœ ์ข์•„์กŒ์ง€๋งŒ ๊ณ„์•ฝ(โ€œwall clock ์„ ๋„˜๊ธฐ์ง€ ์•Š๋Š”๋‹คโ€)์˜ ํŒ๋ณ„๋ ฅ์€ ๋‚จ๋Š”๋‹ค. 1ํšŒ์ฐจ์—์„œ ๋‚ด๊ฐ€ ์œ ์ผํ•œ ๋น„-flake ์‹คํŒจ๋กœ ๋ณด๊ณ ํ–ˆ๋˜ ํ•ญ๋ชฉ์ด ์ด ์ปค๋ฐ‹์œผ๋กœ ํ•ด์†Œ๋œ๋‹ค. ๋‹ค๋งŒ `timeout_s` ๊ฐ€ ๋” ์ปค์ง€๋ฉด `2 ร— TIMEOUT` ๊ณผ `TIMEOUT + 15` ์˜ ๊ฐ„๊ฒฉ์€ ๋ฒŒ์–ด์ง€๊ณ  ์ž‘์•„์ง€๋ฉด ์ข์•„์ง€๋ฏ€๋กœ, **๊ณ ์ • ์—ฌ์œ (+15)๋ณด๋‹ค ๋น„์œจ(์˜ˆ: `timeout_s * 1.5`)** ์ด ๊ณ„์•ฝ์„ ๋” ์ •ํ™•ํžˆ ํ‘œํ˜„ํ•œ๋‹ค(D4, ๋น„์ฐจ๋‹จ ๊ถŒ๊ณ ). + +## 5. ํ…Œ์ŠคํŠธ ์‹คํ–‰ (์›ŒํฌํŠธ๋ฆฌ, ์ด ๋ฆฌ๋ทฐ์–ด๊ฐ€ ์ง์ ‘) + +``` +cd && PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q -rfEs \ + tests/test_issue21_phase2_loop_a.py tests/test_issue21_loop_b.py tests/test_issue21_worktree_prereqs.py \ + tests/test_issue21_acceptance.py tests/test_workspace_scope.py tests/test_issue5_phase1_delegation.py \ + tests/test_deploy_freshness.py tests/test_version_consistency.py +โ†’ 208 passed in 322.94s (failed 0 ยท errors 0 ยท skipped 0) +``` + +- ๋ธŒ๋ฆฌํ”„๊ฐ€ ์ง€์ ํ•œ **`tests/test_issue28_*.py` ๋Š” ์ด ํŠธ๋ฆฌ์— ์—†๋‹ค**(#28 ์ˆ˜์ •์€ ๊ธฐ์กด ํŒŒ์ผ์— ๋“ค์–ด๊ฐ”๋‹ค) โ€” ๊ทœ์น™๋Œ€๋กœ ๊ฑด๋„ˆ๋›ฐ์—ˆ๋‹ค. +- **skip 0 ๊ฑด**: `test_deploy_freshness.py` ์˜ ์ƒˆ `skipif`(D-11~D-19, D-22~D-30)๋Š” ์ด ํ˜ธ์ŠคํŠธ์—์„œ **์ „๋ถ€ ์‹คํ–‰**๋๋‹ค(์„œ๋ธŒ๋ชจ๋“ˆ ์ž์‚ฐ์ด ์žˆ์œผ๋ฏ€๋กœ ์˜๋„๋Œ€๋กœ๋‹ค). ๋ธŒ๋ฆฌํ”„ ํ•ญ๋ชฉ 3 ์˜ ์šฐ๋ ค("๋กœ์ปฌ ์ž์‚ฐ์ด ์žˆ๋Š” ํ˜ธ์ŠคํŠธ์—์„œ๋Š” ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค")๋Š” ์‹ค์ธก์œผ๋กœ ํ•ด์†Œ. +- ๊ฐ€์žฅ ๋А๋ฆฐ ์‹œํ—˜ 10๊ฑด์€ ๋ชจ๋‘ herdr mockยท์ •์ง€/์žฌ๊ฐœ ๊ฒฝ๋กœ์ด๋ฉฐ ๋ธŒ๋กœ์ปค ํƒ€์ž„์•„์›ƒ ๊ณ„์—ด ์‹คํŒจ๋Š” ์—†์—ˆ๋‹ค. + +### ๊ฒฉ๋ฆฌยท์œ„์ƒ ์‹ค์ธก + +| ํ•ญ๋ชฉ | ๊ฒฐ๊ณผ | +|---|---| +| ์‹ค์ œ `.mam/jobs` ์ „ํ›„ | **์ฐจ์ด 0** | +| ์›ŒํฌํŠธ๋ฆฌ `git status --short --ignored` ์ „ํ›„ | **์™„์ „ ๋™์ผ** | +| ์›ŒํฌํŠธ๋ฆฌ HEAD / ๋ณธ ์ฒดํฌ์•„์›ƒ | `08db1da` / `main 1134d74` (๋‘˜ ๋‹ค ๋ณ€ํ™” ์—†์Œ) | +| ์ƒŒ๋“œ๋ฐ•์Šค ์‹คํ—˜ | scratchpad ์ž„์‹œ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ๋งŒ(๋ณต์‚ฌ๋ณธ ๋ฆฌ๋ฒ„ํŠธ ์‹คํ–‰ ํฌํ•จ). ์‹ค์ œ ์Šคํฌ๋ฆฝํŠธยท์›ŒํฌํŠธ๋ฆฌ์— prepare/remove ์‹คํ–‰ ์—†์Œ | + +## 6. ๋ณ‘ํ•ฉ ์ฐจ๋‹จ ์—ฌ๋ถ€ (ํ•ญ๋ชฉ 5) + +๋ฒ„์ „ ๋ฝ์Šคํ… bump ๋ถ€์žฌ(`lib.sh:32`ยท`eager.py:16` ๋ชจ๋‘ `4.6.0`, ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๊ฐ€ 4.7.0 ์œผ๋กœ ์ฒ˜๋ฆฌ ์˜ˆ์ •) ์™ธ์— **๋ณ‘ํ•ฉ์„ ๋ง‰์„ ๊ฒฐํ•จ์€ ์—†๋‹ค**. 1ํšŒ์ฐจ PASS ํŒ์ •์€ ์ด๋ฒˆ ๋ธํƒ€๋กœ ๋’ค์ง‘ํžˆ์ง€ ์•Š์œผ๋ฉฐ, 1ํšŒ์ฐจ์— ๋‚ด๊ฐ€ ๋‚จ๊ธด ๋น„์ฐจ๋‹จ ์ง€์  ์ค‘ **N2(.venv ์‹ฌ๋งํฌ)๋Š” ์ด ๋ธํƒ€๋กœ ํ•ด์†Œ**๋๋‹ค. 1ํšŒ์ฐจ์˜ N1(์Šค๋ƒ…์ƒท ์ปค๋ฐ‹์ด ref ์—†์ด ๋‚จ์•„ gc ๋กœ ์†Œ๋ฉธ)์€ ๊ทธ๋Œ€๋กœ ๋‚จ์•„ ์žˆ์œผ๋‹ˆ ํ›„์† ์ด์Šˆ๋กœ ์œ ์ง€ํ•˜๊ธฐ๋ฅผ ๊ถŒํ•œ๋‹ค. + +| # | ์ด๋ฒˆ ํšŒ์ฐจ ์ง€์  | ์„ฑ๊ฒฉ | +|---|---|---| +| D1 | pip ์„ค์น˜ ์‹คํŒจ ํ›„ ์žฌ์‹คํ–‰์ด `VENV=kept` ๋กœ ์กฐ์šฉํžˆ ํ†ต๊ณผ | ํ›„์† ๊ถŒ๊ณ (์ด๋ฒˆ ๋ธํƒ€๊ฐ€ ๋งŒ๋“  ๊ฒƒ) | +| D2 | prepare ๊ฐ€ ์›ŒํฌํŠธ๋ฆฌ๋งˆ๋‹ค venv ์ƒ์„ฑ + PyPI ์„ค์น˜๋ฅผ ์š”๊ตฌ(์˜คํ”„๋ผ์ธ exit 1) | ์ •๋ณดยท๋ฌธ์„œํ™” | +| D3 | `runner.py:581` ์˜ ํ‘œ์‹œ์šฉ status ์—๋Š” `--untracked-files=all` ์ด ์—†์Œ | ์‚ฌ์†Œ(์ผ๊ด€์„ฑ) | +| D4 | ๋ฒฝ์‹œ๊ณ„ ์—ฌ์œ ๋ฅผ ๊ณ ์ •๊ฐ’(+15 s)์ด ์•„๋‹ˆ๋ผ ๋น„์œจ๋กœ ๋‘๋Š” ํŽธ์ด ๊ณ„์•ฝ์— ์ถฉ์‹ค | ์‚ฌ์†Œ | + +## 7. ๊ต์ฐจ๊ฒ€์ฆ ๊ฐ€์ด๋“œ + +```bash +WT=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29 +# R1 ๊ฒฐํ•จ ์žฌํ˜„ (์ž„์‹œ ์ €์žฅ์†Œ์—์„œ๋งŒ) +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; git config status.showUntrackedFiles no; echo secret > untracked.txt +git status --porcelain --ignore-submodules=none # (๋นˆ ์ถœ๋ ฅ) +git status --porcelain --untracked-files=all --ignore-submodules=none # ?? untracked.txt +GIT_INDEX_FILE=$R/idx git read-tree HEAD; GIT_INDEX_FILE=$R/idx git add -A +git ls-tree -r --name-only "$(GIT_INDEX_FILE=$R/idx git write-tree)" # untracked.txt ๊ฐ€ ๋“ค์–ด ์žˆ๋‹ค(= add -A ๋Š” ์„ค์ • ๋ฌด์‹œ) +# ์ˆ˜์ • ์—†์ด๋Š” ํšŒ๊ท€๊ฐ€ ์‹คํŒจํ•˜๋Š”์ง€ (์›ŒํฌํŠธ๋ฆฌ ๋ณต์‚ฌ๋ณธ์—์„œ๋งŒ) +S=$(mktemp -d); rsync -a --exclude .venv --exclude .git --exclude .mam "$WT/" "$S/" +sed -i '' 's/ --untracked-files=all --ignore-submodules=none/ --ignore-submodules=none/' \ + "$S/.agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh" "$S/.agents/skills/lib_py/loop/runner.py" +sed -i '' 's/"git status --porcelain --untracked-files=all"/"git status --porcelain"/' "$S/.agents/skills/lib_py/worktree.py" +(cd "$S" && PYTHONDONTWRITEBYTECODE=1 "$WT/.venv/bin/python" -m pytest -p no:cacheprovider -q \ + tests/test_issue21_phase2_loop_a.py::test_w6e2_untracked_submodule_file_hidden_by_config_blocks_the_forced_removal \ + tests/test_issue21_phase2_loop_a.py::test_w6e_work_written_while_stopping_is_not_force_removed \ + tests/test_issue21_loop_b.py::test_l17_dirty_submodule_is_refused_or_makes_the_round_stale) # 4 failed, 5 passed +# ps ํ˜•ํƒœ ๋น„๊ต(์ž˜๋ฆผยท๊ตฌ๋ถ„์ž) +python3 - <<'PY' +import subprocess,sys,time +p=subprocess.Popen([sys.executable,"-c","import time;time.sleep(6)","X"*400]); time.sleep(1) +for f in (["-axo","pid=,ppid=,command="],["-eo","pid=,ppid=,args="]): + l=[x for x in subprocess.run(["ps",*f],capture_output=True,text=True).stdout.splitlines() if x.split()[:1]==[str(p.pid)]] + print(f, len(l[0]), l[0].count("X")) +p.wait() +PY +``` + +[VERDICT: PASS] diff --git a/.agents/reports/planner-reviewer-codex-01/report-b880c1f2.md b/.agents/reports/planner-reviewer-codex-01/report-b880c1f2.md new file mode 100644 index 0000000..36d4125 --- /dev/null +++ b/.agents/reports/planner-reviewer-codex-01/report-b880c1f2.md @@ -0,0 +1,71 @@ +# PR #29 independent review + +Job: b880c1f2. Reviewer: planner-reviewer-codex-01. +Review target: detached `multi-agent-mux-wt/pr-29` at `46ea4cc1e4f664ca35711c05c3fa71258e169697`, compared with main `1134d74b3884d5818a4f738f5c9441b80f3f8802`. + +**NOT PASS.** Two reproduced correctness defects remain in the handling of hidden untracked submodule files. Preparation also fails the brief's explicit environment requirement. These are separate from the known pending 4.7.0 release bump. No broad redesign is necessary: the guard corrections are narrow and were verified in disposable fixtures. + +## Blocking findings + +### R1 โ€” P1: removal can silently delete untracked submodule data + +Locations: `.agents/skills/lib_py/worktree.py:167` and `.agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh:91` (the resulting force-removal path is at line 161). + +Both cleanliness checks inherit Git's `status.showUntrackedFiles` configuration. `--porcelain` does not override that setting. In an initialized submodule with `git config status.showUntrackedFiles no`, a normal, non-ignored untracked file is hidden from both the parent status and the submodule `foreach` check. The helper consequently reports that all checks passed and executes `git worktree remove --force`, deleting the file. + +I reproduced this using the existing `ctl_sub` sandbox fixture and the actual preparation/removal scripts, with no real worktree removal: + +1. Prepare a temporary worktree with its local test submodule initialized. +2. Set `status.showUntrackedFiles=no` in that submodule and create `valuable-untracked.txt` there. +3. Parent `status --porcelain --ignore-submodules=none` returns an empty string; explicit submodule `status --porcelain --untracked-files=all` reports the file. +4. Removal returns **0**, prints `checks: locked/sessions/jobs/clean/submodules -> passed; removal method: single --force`, and the file no longer exists. + +**Correction:** explicitly use `--untracked-files=all` in the parent and recursive submodule safety checks, retaining `--ignore-submodules=none`; do not let display preferences govern deletion eligibility. Add regressions with this configuration both before removal and for the post-stop recheck. I changed only the disposable fixture's copy of the submodule check to `git status --porcelain --untracked-files=all --ignore-submodules=none`: removal then returned **3** and preserved the file. The reviewed source remains unchanged. + +### R2 โ€” P1: the review snapshot can approve a dirty submodule it did not capture + +Location: `.agents/skills/lib_py/loop/runner.py:717` (`_dirty_submodules`). + +The review-identity guard has the same configuration dependency. With `status.showUntrackedFiles=no` inside a submodule, untracked content is not rejected. The snapshot records only the submodule gitlink, so that content is absent from the review tree and from the identity comparison. A stubbed PASS is then accepted and promoted, although the documented dirty-submodule refusal should have prevented any review. + +Using the existing temporary `wt_repo` and `_drive` fixtures, I added a clean submodule, set this configuration, and created a non-ignored untracked file. The actual controller returned **0**, dispatched **one review**, and wrote **one PASS identity sidecar**. This demonstrates the controller admission/promotion defect; it does not represent a real reviewer's assessment. + +**Correction:** add `--untracked-files=all` to `_dirty_submodules` so both snapshot admission and subsequent stale/pre-promotion checks reject these files. An in-memory override of only that status command made the same probe return **1**, dispatch zero reviews, and write no sidecar. Add configuration-sensitive tests for both initial and mid-round untracked changes. + +### R3 โ€” P2: preparation still aliases the control environment + +Location: `.agents/skills/multi-agent-mux-create/scripts/worktree_prepare.sh:111`โ€“118; related assertion: `tests/test_issue21_phase2_loop_a.py:264`. + +The brief explicitly asks for a real worktree venv rather than a symlink, or explicit interpreter-path handling. The helper instead executes `ln -s "$MAIN/.venv" "$WT/.venv"`; its test requires that link, and the design/skill documents describe it. This is an implementation-and-test requirement gap, not a failing existing test. A temporary fixture confirmed that writing through the worktree `.venv` changes the control root's environment. The PR review checkout's own real venv does not establish what this helper creates. + +**Correction:** create an independent environment with the intended dependencies, or use the brief's explicit interpreter-path alternative without creating a misleading local environment alias. Update the W1 assertion and corresponding preparation documentation. The loop's review prompt already provides an absolute interpreter path, but that does not change preparation behavior for creators. + +## Other review results + +- **R1โ€“R6 removal defenses:** the post-stop tree recheck, stash checks, retained/deinitialized submodule repository checks, partial-repository refusals, and traversal of HEAD plus local refs including tags are present. The existing regressions cover those cases and passed. They do not exercise `status.showUntrackedFiles=no`, which explains the additional R1 failure above. Initialization rollback retains preexisting submodule repositories/configuration in the tested cases; initialization over a nonempty uninitialized path is refused. +- **Resume and bindings:** the original-cwd check uses canonical paths, runs before session resolution, rejects missing/mismatching recorded cwd with exit 2, and leaves rows without cwd compatible. Single-workspace resume at its recorded path and already-running binding persistence passed. Create now starts immediate reconcile/watchdog from the control runtime, and preparation supplies `MAM_NO_SUBSCRIBER_DAEMON=1`. +- **Identity workflow:** snapshots use a temporary index, include ordinary untracked source files, pin the diff to the snapshot, and generate detached review worktrees. Stale rounds discard approvals; promotion rechecks identity. Failure to create a review worktree does not silently lower isolation. Remote verdict recipients are refused before delegation unless explicitly admitted as diff-only; truncated/unavailable evidence is refused for those recipients. Self-review uses the identity path. These behaviors passed focused tests, subject to R2. With no worktree/base options, existing delegation prompts and the two-argument diff collector remain compatible; output is not literally byte-identical everywhere because loop-id logging/checkpoint data are new. +- **#20 isolation:** ambient bindings are recorded/dropped before collection, and function-scoped defaults route registry/audit state to tmp_path. The #15 default-resolution tests explicitly remove overrides where necessary and check their own job/session identities. The guard watches recorded control-plane paths, not only the review checkout. Direct directory snapshots found **zero added entries** in main and review checkout `.mam/jobs` and `.mam/delegate_job_logs` during this review's tests. +- **Acceptance A1โ€“A8:** these are sandbox/mock integration tests, not proof of environment delivery inside a real herdr pane. The documentation now states that boundary. The tests exercise caller arguments, routing, stale review rejection, cleanup, and loop locking; all passed in the focused run. A3 uses a deliberately unreachable broker, so failure timing is bounded but the suite is not uniformly instantaneous. Real-herdr acceptance remains separate evidence to obtain before claiming complete operational validation. +- **acceptance5 changes:** unique session/secret values remove the old fixed-session collision, retained-terminal delivery is a valid acknowledgement path, and product `started --heartbeat` behavior is unchanged. The test passed in this run. Caution: the new skip predicate at `tests/test_issue18_liveness_contract.py:1183` checks local subscription plus absence of observed events; it does not independently establish the topic actually published by the runner. Its claim that a wrong publish topic must still fail is stronger than its evidence. A controlled broker or assertions against publisher topic/outcome evidence would make this classification more reliable. Do not treat a skipped acceptance run as a PASS. +- **Documentation/follow-ups:** the brief's four nonblocking follow-ups are reasonable: strengthen A4 beyond status/path equality to compare untracked contents; make shared info/exclude mutation and its pre-seeding in A4 explicit; scope fallback prune to owned review metadata; deduplicate submodule refusal output. A4 currently proves only its stated tuple invariants. Tracking these under the proposed #30 follow-up is appropriate, but I did not independently inspect the external issue's registration/content. R1/R2 above must be fixed before merge rather than deferred as those polish items. +- **Release:** 4.7.0 lockstep changes and release notes remain a known merge prerequisite. Version-consistency tests passing on 4.6.0 do not fulfill it. `git diff --check 1134d74...HEAD` passed; `bash -n` passed for all eight changed shell files. + +## Verification record + +All pytest runs used the requested worktree's own `.venv/bin/python`, `PYTHONDONTWRITEBYTECODE=1`, and `-p no:cacheprovider`. Additional probes were injected in memory through pytest collection and used existing temporary fixtures; only the fix-direction probe altered a fixture's disposable source copy. + +| Run | Result | +|---|---| +| `test_issue21_acceptance.py`, `test_issue21_phase2_loop_a.py`, `test_issue21_loop_b.py`, `test_issue21_worktree_prereqs.py`, `test_issue20_state_isolation.py`, `test_issue15_registry_isolation.py`, `test_version_consistency.py` | **118 passed, 1 skipped**, 289.64 s. Skip is the outer collection of the deliberately nested S6 probe. | +| `test_issue14_delivery_ack.py`, `test_issue18_liveness_contract.py`, `test_issue6_turn_handshake.py` | **56 passed, 1 failed**, 331.60 s. | +| Isolated wall-clock regression rerun | **1 failed**, 31.24 s; close delay 28.5585 s against 28 s. | +| R1/R2 safety assertions against unmodified implementation | Both failed as described above, reproducing the blockers. | +| R1 disposable-copy correction / R2 in-memory correction | Each passed: removal preserved the file; identity refused before review. | +| Preparation alias probe | Confirmed the symlink and cross-environment write. | + +The regression-suite failure was `test_wrapper_idle_grace_never_outlives_the_wall_clock`, `tests/test_issue14_delivery_ack.py:513`: the expected error status and return code were observed, but close delay was **28.7779 s**, exceeding the **28 s** allowance. The isolated rerun also failed at **28.5585 s**. The test body and wrapper are unchanged by this PR; the changed #14 code is its separate in-process publication helper. This timing failure remains unresolved in this environment and is not the evidence for R1/R2. I did not run the base suite or independently classify #28's 33 failures; these results do not justify a blanket environmental explanation. + +No product files, source refs, production sessions, or monitor state were changed. Main remained on `1134d74`; the reviewed worktree remained on `46ea4cc`. The verdict concerns this exact tree, not later orchestrator release/fix commits. + +[VERDICT: NOT PASS] diff --git a/.agents/reports/planner-reviewer-codex-01/report-f6b21a0d.md b/.agents/reports/planner-reviewer-codex-01/report-f6b21a0d.md new file mode 100644 index 0000000..e78593e --- /dev/null +++ b/.agents/reports/planner-reviewer-codex-01/report-f6b21a0d.md @@ -0,0 +1,39 @@ +# PR #29 delta review โ€” 2nd pass + +Reviewer: planner-reviewer-codex-01. Job: f6b21a0d. + +Reviewed the detached worktree `multi-agent-mux-wt/pr-29` at `80a5614`, comparing `46ea4cc..HEAD` (commits `7a94ba2`, `90a289d`, `08db1da`, and `80a5614`). The three findings from the first review are closed by the delta. I found no additional merge-blocking defect in the reviewed changes. + +## R1/R2: hidden untracked submodule files + +The removal guard now uses `git status --porcelain --untracked-files=all --ignore-submodules=none` for the worktree, and `lib_py.worktree.submodule_findings()` uses `git status --porcelain --untracked-files=all` inside every checked submodule. The identity runner's `_dirty_submodules()` uses the same explicit flag. This defeats `status.showUntrackedFiles=no` and remains independent of `submodule.*.ignore` and `diff.ignoreSubmodules`. + +The source-tree snapshot still uses a temporary index and `git add -A`; that operation already sees untracked files independent of display settings, so no extra snapshot change is needed. The new regression cases cover hidden files before removal, files written while stopping, hidden files that appear during a review, and hidden files present before a review. The prior destructive probes were repeated against disposable fixtures: with the delta, removal returned exit 3 and preserved the untracked file; review identity refused before dispatch and produced no sidecar. R1 and R2 are therefore resolved. + +## R3: worktree virtual environments + +`worktree_prepare.sh` now refuses an existing `.venv` symlink, creates an independent venv with the control root's `.venv/bin/python`, installs the delegate-job requirements plus root `requirements.txt`/`requirements-dev.txt` when present, and reports `VENV=created`. A usable existing environment is idempotently retained as `VENV=kept`. When the control interpreter is absent it warns and reports `VENV=skipped`; it does not invent a link. If dependency installation fails, the half-created venv is removed and the worktree remains for correction and retry. The tests cover creation, the kept path, link refusal, missing interpreter, and failed-install cleanup. + +The design document and create/stop skill documentation now describe the independent environment, network requirement, link refusal, and cleanup semantics. The managed `.gitignore` block and shared Git `info/exclude` still ignore `.venv` and `.pytest_cache`; the new environment is not tracked. I found the implementation and documentation consistent. + +## Portability delta (#28) + +The `bash -c` change in `test_workspace_scope.py` is appropriate because the test invokes the Bash `source` builtin. Help-output normalization collapses layout-only whitespace and the Python 3.9/3.14 option-group spelling while still comparing the actual normalized content. The deployment freshness markers skip only when the relevant `nats-docker` assets are absent. This checkout does not contain the optional `nats-docker/docker-compose.yaml`, so those asset-dependent cases skip here; `PRIVATE_SERVER.md` is present and its checks run. The test suite must still run those checks when a checkout has the assets. The process scanners now use POSIX `ps -eo pid=,ppid=,args=`, which is available on macOS and Linux and preserves the command text needed by the scanners. `MAM_ENV_FILE` is pinned to a deliberately absent per-test path, while tests that need broker configuration can override it. + +## Wall-clock test change + +The close-delay allowance changed from `timeout_s + 8` to `timeout_s + 15`. This is a test-only adjustment for observed suite load; it still rejects the original roughly 2ร— timeout failure mode (40 seconds against a 35-second limit in the test's 20-second scenario). The previously failing scenario now passes in an isolated rerun, and the remote acceptance test also passes. The wider allowance is a reasonable host-load calibration, though future runs should retain the measured close-delay evidence rather than relying on the assertion alone. + +## Verification + +All tests were run in the requested detached worktree with its `.venv/bin/python`, `PYTHONDONTWRITEBYTECODE=1`, and `pytest -p no:cacheprovider`. + +- Delta-focused suites (`test_issue21_phase2_loop_a.py`, `test_issue21_loop_b.py`, `test_issue21_acceptance.py`, `test_issue21_worktree_prereqs.py`, `test_issue5_phase1_delegation.py`, `test_workspace_scope.py`, `test_deploy_freshness.py`, `test_version_consistency.py`): **209 passed** in 323.16 s. Asset-dependent freshness tests skip because this checkout has no `nats-docker` assets; this is the intended skip condition. +- Isolated wall-clock close and remote acceptance rerun: **2 passed** in 49.79 s. +- `bash -n` on all eight changed shell files: passed. +- `git diff --check 1134d74...HEAD`: passed. +- Direct snapshots before/after testing found no new entries in either checkout's real `.mam/jobs` or `.mam/delegate_job_logs`. + +The known release prerequisite remains outside this delta: the orchestrator must perform the 4.7.0 version lockstep bump and release entry before merge. Main stayed at `1134d74`; the review worktree stayed at `80a5614`; no source, checkout, production registry, monitor, or session state was modified. + +[VERDICT: PASS] diff --git a/.agents/reports/reviewer-agy-01/report-d9275506.md b/.agents/reports/reviewer-agy-01/report-d9275506.md new file mode 100644 index 0000000..da1b245 --- /dev/null +++ b/.agents/reports/reviewer-agy-01/report-d9275506.md @@ -0,0 +1,87 @@ +# PR #29 Delta Review Report (Round 2) + +- **Reviewer**: Antigravity (`herdr:reviewer-agy-01`) +- **Job ID**: `d9275506` +- **Target PR**: [PR #29](https://git.godopu.com/tmpl/multi-agent-mux/pulls/29) (`feat/issue-21-items-4-5-7` @ `08db1da`, previous review target `46ea4cc`, base `main` `1134d74` / v4.6.0) +- **Review Target Worktree**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` (detached @ `08db1da`) +- **Base Checkout**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux` (`main` @ `1134d74`, unmodified) +- **Status**: Review Complete + +--- + +## 1. Executive Summary + +This independent second-round review examines the 3 delta commits (`46ea4cc..08db1da`) in PR #29 responding to review feedback (specifically Codex R1, R2, R3, host portability fixes for Issue #28, and a wall-clock test timing adjustment): +1. `7a94ba2`: Portable test adaptations for Linux/Python 3.14 (#28) and POSIX `ps -eo pid=,ppid=,args=` in process scanners. +2. `90a289d`: Widened wall-clock close margin (`timeout_s + 15`) in `test_issue14_delivery_ack.py` to eliminate multi-core load timing jitter. +3. `08db1da`: Codex R1โ€“R3 resolutions: + - Added `--untracked-files=all` across all three git status checks (`lib_py/worktree.py`, `worktree_remove.sh`, and `runner.py::_dirty_submodules`), preventing `status.showUntrackedFiles=no` from bypassing safety checks. + - Replaced `.venv` symlinking in `worktree_prepare.sh` with dedicated virtualenv creation (`$WT/.venv`) via the control root interpreter (`$MAIN/.venv/bin/python`), including automatic `requirements*.txt` installation, link refusal (`exit 1`), and interpreter fallback (`VENV=skipped`). + - Added comprehensive regressions (`test_w1`, `test_w1b`, `test_w6e[hidden]`, `test_w6e2`, `test_l17[untracked-hidden,during-hidden]`). + +All 5 checklist verification items were evaluated in worktree `pr-29`. The delta commits resolve all feedback points without weakening existing contracts or regressing Round 1 functionality. + +--- + +## 2. Checklist Item-by-Item Verification + +### (1) Untracked-Safe Status Checks (Codex R1 & R2) +- **Three Inspection Points**: `--untracked-files=all` has been added to all 3 critical cleanliness evaluation sites: + 1. `lib_py/worktree.py` line 167: + `checks = (("dirty", "git status --porcelain --untracked-files=all"), ...)` + 2. `.agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh` line 91: + `if ! dirty="$(git -C "$WT" status --porcelain --untracked-files=all --ignore-submodules=none 2>&1)"; then` + 3. `.agents/skills/lib_py/loop/runner.py` line 718 (`_dirty_submodules`): + `r = _git(wt, "submodule", "foreach", "--recursive", "--quiet", "git status --porcelain --untracked-files=all")` +- **Prevention of Data Loss & Stale Approvals**: Standard `git status --porcelain` respects `status.showUntrackedFiles=no`. Without `--untracked-files=all`, untracked files in submodules or worktrees with that setting configured were hidden from status checks, allowing `worktree_remove.sh` to execute a destructive forced removal over uncommitted work, and allowing the review loop to treat a dirty tree as clean. The explicit flag forces git to report all untracked files. +- **Source Tree Snapshot Guard**: In `runner.py`, the source tree review snapshot is captured using `git -C add -A`. By git specification, `git add -A` captures all untracked, modified, and deleted files unconditionally, completely ignoring `status.showUntrackedFiles`. Hence, no additional flag is required for snapshot creation. +- **Regression Tests**: + - `test_w6e[hidden=True]` and `test_w6e2_untracked_submodule_file_hidden_by_config_blocks_the_forced_removal` in `tests/test_issue21_phase2_loop_a.py`. + - `test_l17[untracked-hidden, during-hidden]` in `tests/test_issue21_loop_b.py`. + All pass and confirm that config-hidden untracked files fail closed. + +### (2) Dedicated Worktree Virtualenv (Codex R3) +- **Idempotency**: `worktree_prepare.sh` checks `if [ -e "$WT/.venv" ]; then VENV_STATE="kept"`. Existing virtual environments are preserved without re-creation. +- **Symlink Refusal**: `if [ -L "$WT/.venv" ]; then echo "ERROR: ... is a symbolic link..." >&2; exit 1; fi`. Rejects aliased symlinks to guarantee environment isolation. +- **Interpreter Absent**: If `$MAIN/.venv/bin/python` is unavailable, it outputs `WARNING: no interpreter at '$MAIN/.venv/bin/python'; no venv created` and proceeds with `VENV_STATE="skipped"`. +- **Installation Failure Handling**: If `pip install -q -r "$req"` fails on requirements files, it exits with code 1 and outputs `ERROR: ... the worktree was kept - remove it with worktree_remove.sh --path '$WT'`. The worktree is preserved for debugging rather than left broken or deleted unexpectedly. +- **Documentation & Ignore Alignment**: + - `docs/design/worktree-creator-model.md` and SKILL guides (`create`, `stop`) have been updated to describe dedicated venvs and interpreter requirements. + - The `.gitignore` managed block (`/.venv`) and `info/exclude` (`/.venv`, `.pytest_cache/`) correctly ignore `$WT/.venv` as a directory. + +### (3) Host Portability Fixes (Issue #28) +- **`_normalize_help`**: In `tests/test_issue5_phase1_delegation.py`, replaces `optional arguments:` with `options:` and normalizes whitespace runs. This accommodates Python 3.10+ argparse formatting without weakening validation of flags, positional arguments, or descriptions. +- **`test_deploy_freshness.py` Skips**: Uses `needs_docker_assets` / `needs_private_server_doc` decorators that skip only when external `nats-docker` or private server documentation assets are missing. On environments with assets present (including this macOS verification host), all 97 tests ran and passed. +- **`conftest.py` `MAM_ENV_FILE` Isolation**: Anchors `MAM_ENV_FILE` to a non-existent sandbox path (`tmp_path / ".mam.env.absent"`) by default, preventing ambient `.mam.env` values from leaking into test runs. Tests requiring specific env files explicitly override it. +- **Process Scanning (`ps -eo pid=,ppid=,args=`)**: POSIX-compliant process listing in `lib_py/agents/base.py` and `lib_py/delegation/heartbeat.py` replaces BSD-only `-ww` flags, ensuring identical, untruncated column parsing across macOS and Linux. + +### (4) Wall-Clock Close Margin (`90a289d`) +- **Test Integrity**: In `tests/test_issue14_delivery_ack.py`, `test_wrapper_idle_grace_never_outlives_the_wall_clock` verifies that heartbeats extend the idle window up to the wall-clock deadline (`timeout_s = 20`), without allowing idle grace to outlive the deadline. +- **Margin Adjustment**: The assertion threshold was updated from `timeout_s + 8` to `timeout_s + 15` (35s). The original bug was counting sleeps, which ran for ~2x TIMEOUT (40s). A 35s ceiling easily catches a 40s overrun while preventing ~50ms timing jitter failures caused by heavy CPU load during full-suite execution with mock latency. Tested directly: passed in 31.35s. + +### (5) Merge Readiness +- `git diff 1134d74...HEAD --check` passes with **0** whitespace/formatting errors. +- Version bump to 4.7.0 (MINOR) and `VERSIONS.md` update are confirmed as scheduled for the orchestrator release commit. +- Zero functional, stability, or security regressions. + +--- + +## 3. Test Execution Summary + +Executed in worktree `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29`: +| Test Suite | Result | Duration | +| :--- | :---: | :---: | +| `tests/test_version_consistency.py` | **3 passed** | 0.22s | +| `tests/test_workspace_scope.py` | **2 passed** | 0.14s | +| `tests/test_issue5_phase1_delegation.py` + `tests/test_deploy_freshness.py` | **97 passed** | 32.48s | +| `tests/test_issue21_phase2_loop_a.py` | **28 passed** | 125.85s | +| `tests/test_issue21_loop_b.py` | **34 passed** | 11.01s | +| `tests/test_issue14_delivery_ack.py` (wall-clock test) | **1 passed** | 31.35s | + +--- + +## 4. Conclusion + +The 3 delta commits cleanly resolve Codex review items R1โ€“R3, address host portability under Python 3.14/Linux (Issue #28), and stabilize the wall-clock test under multi-core load. All contracts remain fully preserved. + +[VERDICT: PASS] diff --git a/.agents/reports/reviewer-agy-01/report-f27519ff.md b/.agents/reports/reviewer-agy-01/report-f27519ff.md new file mode 100644 index 0000000..8c82405 --- /dev/null +++ b/.agents/reports/reviewer-agy-01/report-f27519ff.md @@ -0,0 +1,122 @@ +# PR #29 Review Report: Worktree Operating Model Phase 2 (Items 4, 5, 7, Test Isolation #20, Deploy #27) + +- **Reviewer**: Antigravity (`herdr:reviewer-agy-01`) +- **Job ID**: `f27519ff` +- **Target PR**: [PR #29](https://git.godopu.com/tmpl/multi-agent-mux/pulls/29) (`feat/issue-21-items-4-5-7` @ `46ea4cc`, base `main` `1134d74` / v4.6.0) +- **Review Target Worktree**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` (detached @ `46ea4cc`) +- **Base Repository**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux` (`main` @ `1134d74`, unmodified) +- **Status**: Review Complete + +--- + +## 1. Executive Summary + +PR #29 implements the Phase 2 components of the Worktree Creator Operating Model (Issue #21 items 4, 5, 7), comprehensive test suite isolation (#20), and installer `.gitignore` handling (#27) across 20 commits (+3,229 / -108 lines, 25 files changed). + +An independent review was performed from the dedicated worktree `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` without modifying any files in either the worktree or the main working copy. + +All 8 verification items have been thoroughly analyzed and verified: +- **Worktree Helpers (`worktree_prepare.sh`, `worktree_remove.sh`)**: Rigorously safe against data loss (defenses R1โ€“R6) with opt-in submodules, rollback on failed init, and guarded `--force` execution only after verifying that zero uncommitted changes, unpushed commits (including tags/detached HEAD), or stashes exist. +- **Resume CWD Guard**: Safely enforces resuming in original cwd (exit 2) while maintaining 100% backward compatibility for single-workspace users, and persists control-plane bindings on already-running paths. +- **Review Identity & Isolation**: Robust per-round snapshots `(base_sha, head_sha, tree_sha)`, detached review worktrees (`--review-isolation`), stale-round re-review on creator commit, pre-promotion re-check, and durable sidecar metadata (`.identity.json`). Default behavior without options remains byte-for-byte identical. +- **Test State Isolation (#20)**: Zero test leakage into real `.mam` verified live on the control plane. Ambient bindings are dropped before test collection, test-specific tmp roots are injected via autouse fixture, and session-scoped leak guards detect anomalies. +- **Test Suite Results**: 100% pass across all new and prerequisite test suites (132 tests passing deterministically). `git diff --check` passes with 0 errors. +- **Known Limitation**: Version bump to 4.7.0 (MINOR) and `VERSIONS.md` update are properly scheduled for the orchestrator release commit. + +--- + +## 2. Item-by-Item Verification + +### (1) Worktree Helpers & Data Loss Defenses (Issue #21 Item 4) +- **`worktree_prepare.sh`**: + - **`.venv` Handling**: Creates a symlink `ln -s "$MAIN/.venv" "$WT/.venv"` (linking to the control root's virtual environment rather than duplicating packages). If an existing non-symlink `.venv` is present, it issues a warning and leaves it intact. Commit `fadd38c` (#27) updated the managed `.gitignore` rule from `/.venv/` to `/.venv`, ensuring git ignores the `.venv` symlink in worktrees across all forms (file, directory, or symlink). + - **Shared Excludes**: Adds `/.venv` and `.pytest_cache/` directly into `$GIT_COMMON_DIR/info/exclude`. + - **Opt-in Submodules**: Submodules are initialized only when `--submodules` is explicitly supplied. Without `--submodules`, no submodule checkout or `modules/` directory is created. + - **Submodule Rollback (R3 & R4)**: If `git submodule update --init --recursive` fails, the helper rolls back any partially created submodule checkouts, removes `.git` pointer files, deletes newly created module gitdirs in `$gd/modules/`, and restores `.git/config` keys. Pre-existing submodule checkouts are preserved. If an uninitialized submodule path already contains files prior to init, it refuses upfront (`exit 3`) before modifying anything (R4). +- **`worktree_remove.sh` & Data Loss Defenses (R1โ€“R6)**: + - Pre-flight checks run before any mutations: + 1. Linked worktree check & lock check (`is-locked`, exit 3 if locked). + 2. Running sessions check: refuses unless `--stop` is specified. + 3. Open jobs check: verifies no open jobs exist for sessions in the worktree. + 4. Clean working tree check: `git status --porcelain --ignore-submodules=none`. + 5. Submodule inspection: verifies `dirty`, `unpushed`, and `stashes` across all repositories under `modules/` (including deinitialized and nested submodules via `_module_gitdirs`). + - **Post-Stop Re-Check (R1)**: After `--stop` halts sessions, open jobs are re-checked, and `judge_tree` is executed a second time immediately before removal. This prevents data loss if a session being stopped wrote files, committed, or created stashes during termination. + - **Unpushed Commits (R6)**: Uses `UNPUSHED_REVS = ("HEAD", "--exclude=refs/stash", "--all", "--not", "--remotes")`. This inspects commits reachable from HEAD, detached HEAD, local branches, and tags. Commits preserved only by local tags in submodules are protected. + - **Single `--force` Execution**: Git by design refuses plain `git worktree remove` on any worktree containing a `modules/` directory. `worktree_remove.sh` performs a single `git worktree remove --force` *only* after confirming R1โ€“R6 pass completely. If any check fails, it exits with code 3 and leaves the entire worktree untouched. Manual, unguarded `git worktree remove --force` is explicitly warned against. + +### (2) Resume Contract & Binding Persistence (`2679ddf`) +- **Original CWD Validation**: + - `resume_session.sh` verifies `mam_abs_workspace "$WORKSPACE"` matches `mam_abs_workspace "$ROW_CWD"`. If called with an altered workspace path, it terminates with `exit 2` and a clear error message (`Pass --workspace '$ROW_CWD' to resume it in its original workspace`). + - If the directory recorded in `pane.cwd` no longer exists (e.g. after worktree removal), it exits with code 2 explaining that recreating the worktree at that path makes the session resumable again. +- **Backward Compatibility**: For standard single-workspace users, the workspace matches `ROW_CWD`, so resume executes normally without disruption. +- **Already-Running Path Binding Persistence**: When resuming an already-running session, `CONTROL_PLANE_JSON="$CP_JSON" update_yaml_resumed.sh` is invoked, persisting the control-plane binding into `agent-sessions.yaml`. +- **Loud Binding Failure**: If control-plane binding computation fails: + - For a worktree (workspace != control root), it fails closed with `exit 2` ("refusing to resume a session that would use the wrong control plane"). + - For the control root itself, it issues a WARNING and continues without `control_plane`, preserving resiliency. + +### (3) Review Identity & Isolation (Issue #21 Item 5) +- **Snapshot Identity**: + - `lib_py/loop/runner.py` records `(loop_id, base_sha, head_sha, tree_sha)` for each review round. + - Verification targets are embedded directly into reviewer prompts. +- **`--review-isolation worktree`**: + - Creates a temporary detached worktree at the snapshot's review commit (with uncommitted/untracked changes pinned) so reviewers run tests without contaminating the creator's working tree. + - The review worktree is cleanly closed via `_close_review_tree` at the conclusion of each round, even upon failure (`_release_review_trees`). +- **Stale Round Detection**: If the creator commits or modifies files during an active review round, the round is identified as stale, approvals are discarded, and a fresh review round is triggered. +- **Pre-Promotion Re-Check**: Before promoting passed reviews to durable artifacts (`.mam/reviews/...`), `_identity_now()` is verified against the approved round's `(head_sha, tree_sha)`. If the tree changed after review approval, promotion fails closed with `exit 1`. +- **Durable Identity Evidence**: Promoted reports are accompanied by a JSON sidecar `report-{jid}.identity.json` detailing loop ID, SHAs, reviewer identity, review commit, and verification timestamp. +- **Remote Reviewers (`--remote-review`)**: Since remote reviewers cannot access local worktree paths, identity mode refuses remote reviewers by default, or allows them strictly as diff-only via `--remote-review diff-only`. +- **Dirty Submodules**: Submodule dirtiness is detected and causes reviews to be refused or marked stale. +- **Zero Drift Without Options**: When run without isolation/identity options, `identity` evaluates to `False`, no review worktree is created, and the prompt text matches previous releases byte-for-byte. + +### (4) Test State Isolation (#20 Commits `3433ffd` ~ `46ea4cc`) +- **Ambient Binding Drop**: `tests/conftest.py`'s `pytest_configure` records and unsets ambient `AGENT_SESSIONS_YAML`, `MAM_JOBS_DIR`, `DELEGATE_JOB_LOGS_DIR`, and foreign `WORKSPACE_ROOT`. +- **Per-Test TMP Roots**: The autouse fixture `_isolate_mam_state_dirs` redirects `MAM_JOBS_DIR` and `DELEGATE_JOB_LOGS_DIR` to `tmp_path / ".mam-test-state"` for every individual test. +- **Resolution Tests Opt-Out**: Tests in `tests/test_issue15_registry_isolation.py` that test default fallback resolution explicitly opt out by calling `monkeypatch.delenv`. +- **Control Plane Leak Guard**: `mam_state_leak_guard` snapshots all real `.mam/jobs` and `.mam/delegate_job_logs` directories before test session execution and asserts at session finish that zero unexpected records were leaked. +- **Live Verification**: Inspection of the host control plane `.mam/jobs` and `.mam/delegate_job_logs` confirmed **0** leaked test records. Only active delegation job `f27519ff` is present. + +### (5) Acceptance5 Stabilization Commits (`7bc3e1a`, `371099a`, `e6d2168`) +- **Session & Secret Randomization**: Uses a unique randomized session name (`sim-worker-{tag}`) and secret (`issue18-shared-secret-{tag}`) per run. This eliminates the collision failure mode where public MQTT broker retained messages from prior test runs were served to new tests. +- **Widened Idle Window & Terminal Event Recognition**: Expands missed heartbeat tolerance (10 misses x 2s) and recognizes delivery confirmed by retained terminal events (`advanced to 'completed'`). +- **Targeted Skip on Broker Non-Delivery**: Skips only when provable broker non-delivery occurs (e.g. no CONNACK from broker, or broker packet drop). +- **Contract Maintained**: Protocol assertions (`assert proc.returncode == 0`, `status == "completed"`, `transport == "remote"`) remain fully intact; the contract is not weakened. + +### (6) Acceptance Cases A1โ€“A8 & Test Suite Performance +- **Hermeticity & Determinism**: + - All 9 acceptance tests in `tests/test_issue21_acceptance.py` run against `mock_herdr` and `mam_sandbox`/`tmp_path`. + - Zero requirement for a live `herdr` daemon or external MQTT network broker. +- **Test Execution Metrics (executed in `pr-29`)**: + - `tests/test_version_consistency.py`: **3 passed** in 0.22s + - `tests/test_issue20_state_isolation.py`: **7 passed, 1 skipped** in 0.34s + - `tests/test_issue21_acceptance.py`: **9 passed** in 37.23s + - `tests/test_issue21_loop_b.py`: **32 passed** in 10.01s + - `tests/test_issue21_phase2_loop_a.py`: **25 passed** in 122.37s + - `tests/test_issue15_registry_isolation.py`: **7 passed** in 27.11s + - `tests/test_issue21_worktree_prereqs.py`: **35 passed** in 112.28s + - `tests/test_issue6_turn_handshake.py`: **14 passed** in 69.39s + - **Total**: **132 passed, 1 skipped**, 100% pass rate. + +### (7) Documentation Integrity & Non-Blocking Recommendations +- **Documentation Alignment**: + - `docs/design/worktree-creator-model.md` accurately updates the implementation status matrix: items 1, 2, 3 (v4.6.0), items 4, 6 (Loop A), items 5, 7 (Loop B), with item 8 appropriately held for future work. + - Skill docs (`multi-agent-mux-create`, `multi-agent-mux-stop`, `multi-agent-mux-resume`, `multi-agent-mux-loop`) document helper script invocations, `--stop`, `--review-isolation`, and `--remote-review`. + - Compatibility matrix clearly defines runtime behavior across create, resume, stop, status, reconcile, delegate-job, and loop. +- **4 Non-Blocking Recommendations**: + 1. A4 untracked comparison (pre-applying info/exclude). + 2. info/exclude pre-reflection. + 3. Prune global guidance when worktree directory is deleted externally. + 4. Deduplication of submodule refusal messages. + All 4 are sound, low-severity improvements properly deferred to Issue #30. + +### (8) Merge Readiness Assessment +- `git diff 1134d74...HEAD --check` completed with **0** errors (no trailing whitespace, no merge artifacts). +- Known limitation: Version bump to 4.7.0 (MINOR) and entry in `VERSIONS.md` are reserved for the orchestrator release commit. +- No functional regressions, security hazards, or data loss vulnerabilities found. + +--- + +## 3. Final Conclusion + +PR #29 completes Phase 2 of the Worktree Creator Operating Model with exemplary rigor in data safety, deterministic test isolation, and contract preservation. + +[VERDICT: PASS] diff --git a/.agents/reports/reviewer-grok-01/report-19d56cde.md b/.agents/reports/reviewer-grok-01/report-19d56cde.md new file mode 100644 index 0000000..ceec388 --- /dev/null +++ b/.agents/reports/reviewer-grok-01/report-19d56cde.md @@ -0,0 +1,62 @@ +# ๐Ÿ” Review Report โ€” Job 19d56cde (PR #29 ๋ธํƒ€ `46ea4cc...08db1da`) + +- **Reviewer**: `reviewer-grok-01` ยท 2026-09-14 +- **ํŠธ๋ฆฌ**: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` @ `08db1da` +- **๋ธํƒ€**: `git log`/`git diff` **`46ea4cc..HEAD`** (3์ปค๋ฐ‹). 1ํšŒ์ฐจ grok PASS @ 46ea4cc ๋ฅผ ๋’ค์ง‘์ง€ ์•Š์Œ. +- **๋ณธ ์ฒดํฌ์•„์›ƒ**: `main` `1134d74` ์œ ์ง€. ์›ŒํฌํŠธ๋ฆฌ ๊นจ๋—. ํ—ฌํผ๋ฅผ ์‹ค์ œ ํŠธ๋ฆฌ์— ์‹คํ–‰ํ•˜์ง€ ์•Š์Œ. + +`git diff --check 46ea4cc...HEAD` ํด๋ฆฐ. + +## (1) Codex R1/R2 โ€” ๋‹ซํž˜ + +`--untracked-files=all` ์ด ์„ธ ๊ฒ€์‚ฌ์— ๋ชจ๋‘ ์žˆ์Œ: + +| ์œ„์น˜ | ๋ช…๋ น | +|---|---| +| `worktree_remove.sh` `judge_tree` | `status --porcelain --untracked-files=all --ignore-submodules=none` | +| `lib_py/worktree.py` submodule dirty | `status --porcelain --untracked-files=all` | +| `runner.py` `_dirty_submodules` | ๋™์ผ + `--ignore-submodules=none` | + +์ž„์‹œ ์ƒŒ๋“œ๋ฐ•์Šค: `status.showUntrackedFiles=no` ์ด๋ฉด ํ”Œ๋ž˜๊ทธ ์—†์ด porcelain ์ด ๋น„๊ณ , `--untracked-files=all` ์ด๋ฉด `?? secret.bin`. **`git add -A` ๋Š” ๊ทธ ์„ค์ •๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ untracked ๋ฅผ ์Šคํ…Œ์ด์ง•**ํ•˜๋ฏ€๋กœ ๋ฆฌ๋ทฐ ์Šค๋ƒ…์ƒท(`runner.py:698` `("add", "-A")`)์€ ์ถ”๊ฐ€ ์กฐ์น˜ ๋ถˆํ•„์š”. + +ํšŒ๊ท€: `test_w6e[hidden]`, `test_w6e2`, `test_l17[untracked-hidden,during-hidden]`. ํ”Œ๋ž˜๊ทธ ์—†์ด๋Š” ์ˆจ์€ untracked ๊ฐ€ force ์ œ๊ฑฐยท์Šค๋ƒ…์ƒท์„ ํ†ต๊ณผํ–ˆ์„ ํ…Œ์ŠคํŠธ. + +## (2) Codex R3 โ€” ๋‹ซํž˜ + +`worktree_prepare.sh:111-136`: ๋งํฌ๋ฉด exit 1(๋ฌด๋ณ€๊ฒฝ), ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ `bin/python` ์ด๋ฉด `VENV=kept`, ์ปจํŠธ๋กค ๋ฃจํŠธ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋กœ `python -m venv` + `requirements*.txt` ์„ค์น˜ ์‹คํŒจ ์‹œ **์›ŒํฌํŠธ๋ฆฌ ์œ ์ง€ยทexit 1**, ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์—†์œผ๋ฉด ๊ฒฝ๊ณ ยท`VENV=skipped`. `test_w1` / `test_w1b` ๊ฐ€ created/kept/skip/link ๊ฑฐ๋ถ€. SKILL.mdยทdesign doc ์ผ์น˜. `.gitignore` ๊ด€๋ฆฌ ๋ธ”๋กยท`info/exclude` `/.venv` ์™€ ์ •ํ•ฉ(์ž์ฒด venv ๋Š” untracked ๋กœ ์•ˆ ๋ณด์ž„). + +## (3) #28 ์ด์‹์„ฑ โ€” ์˜๋„ ์œ ์ง€ + +- `test_workspace_scope.py`: `bash -c` (`source` ๋Š” dash ์— ์—†์Œ). +- argparse help: `_normalize_help` ๊ฐ€ ๊ทธ๋ฃน๋ช…ยท๊ณต๋ฐฑ๋งŒ ์ •๊ทœํ™”. ์˜ต์…˜ ํ† ํฐ ์œ ์‹ค์€ ์—ฌ์ „ํžˆ ์‹คํŒจ. +- `conftest` autouse `MAM_ENV_FILE` โ†’ ์—†๋Š” ํŒŒ์ผ. ์‹ค `.mam.env` ๋ฅผ ์“ฐ๋ ค๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ์ง์ ‘ set. T1-4 ๊ฐ€ ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค๋ฅผ ํƒ€๋˜ ๊ฒฝ๋กœ๋ฅผ ๋ง‰์Œ. +- deploy d11โ€“d19 / d22โ€“d30 skip ์€ ์ž์‚ฐ ๋ถ€์žฌ ์‹œ๋งŒ. **์ด ์›ŒํฌํŠธ๋ฆฌ์—๋Š” `nats-docker/PRIVATE_SERVER.md` ์™€ `nats-docker/docker/docker-compose.yaml` ์ด ์žˆ์–ด skip ์—†์ด ์‹คํ–‰๋จ** (์•„๋ž˜ ์Šค์œ„ํŠธ 0 skipped). +- `ps -eo pid=,ppid=,args=`: ์ด macOS ์—์„œ BSD `-axo โ€ฆ command=` ์™€ ๊ฐ™์€ 3์—ด ์ถœ๋ ฅ. Linux POSIX ์™€ ๋งž์ถค. + +## (4) 90a289d ์—ฌ์œ  + +`timeout_s + 8` โ†’ `+ 15`. ํ•€ํ•˜๋Š” ๊ฒฐํ•จ์€ ~2ร—TIMEOUT(~40s, timeout=20). ํ•˜ํ•œ `elapsed >= timeout_s - 1` ์œ ์ง€. ์—ฌ์œ  15s ๋Š” 2ร— ์™€ ๊ฒน์น˜์ง€ ์•Š์Œ. ๊ณ„์•ฝ ๊ฒ€์ฆ์„ ๋ฌดํšจํ™”ํ•˜์ง€ ์•Š์Œ. + +## (5) ๋ฝ์Šคํ… + +4.7.0 bump ๋Š” ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ์˜ˆ์ •. ๋ณ‘ํ•ฉ ์ฐจ๋‹จ ์•„๋‹˜. + +## ํ…Œ์ŠคํŠธ (์›ŒํฌํŠธ๋ฆฌ) + +``` +PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q \ + tests/test_issue21_phase2_loop_a.py tests/test_issue21_loop_b.py \ + tests/test_issue21_worktree_prereqs.py tests/test_issue21_acceptance.py \ + tests/test_workspace_scope.py tests/test_issue5_phase1_delegation.py \ + tests/test_deploy_freshness.py tests/test_version_consistency.py +``` + +(`test_issue28_*.py` ์—†์Œ, ๊ฑด๋„ˆ๋œ€) + +**208 passed in 314.15s**, skip 0. + +## ์ข…ํ•ฉ + +1ํšŒ์ฐจ PASS ์œ ์ง€. Codex R1โ€“R3 ์ด ์ฝ”๋“œยทํšŒ๊ท€ยท๋ฌธ์„œ์™€ ๋งž๋‹ค. ์„ค๊ณ„ ์žฌ์ž‘์—… ๋ถˆํ•„์š”. + +[VERDICT: PASS] diff --git a/.agents/reports/reviewer-grok-01/report-5f1486ee.md b/.agents/reports/reviewer-grok-01/report-5f1486ee.md new file mode 100644 index 0000000..2f3a7dd --- /dev/null +++ b/.agents/reports/reviewer-grok-01/report-5f1486ee.md @@ -0,0 +1,63 @@ +# ๐Ÿ” Review Report โ€” Job 5f1486ee (PR #29 @ `46ea4cc`) + +- **Reviewer**: `reviewer-grok-01` ยท 2026-09-14 +- **ํŠธ๋ฆฌ**: ์›ŒํฌํŠธ๋ฆฌ `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-29` detached `46ea4cc` +- **๋ฒ ์ด์Šค**: main `1134d74` (๋ณธ ์ฒดํฌ์•„์›ƒ์€ ๊ทธ๋Œ€๋กœ `main`) +- **๋ฒ”์œ„**: `git log`/`git diff` **`1134d74...HEAD`** (์›ŒํฌํŠธ๋ฆฌ) +- **์•ˆ์ „**: ์›ŒํฌํŠธ๋ฆฌยท๋ณธ ์ฒดํฌ์•„์›ƒ ๋ฏธ์ˆ˜์ •. `worktree_remove.sh` ๋ฏธ์‹คํ–‰(์ƒŒ๋“œ๋ฐ•์Šค ํ…Œ์ŠคํŠธ๋งŒ). ์‹ค์ œ `.mam` ๋ฏธ์กฐ์ž‘. + +`git diff --check 1134d74...HEAD` ํด๋ฆฐ. 20 ์ปค๋ฐ‹. + +## (1) ์›ŒํฌํŠธ๋ฆฌ ํ—ฌํผ (ํ•ญ๋ชฉ 4) + +**prepare `.venv`**: ์‹ค์ œ venv ๊ฐ€ ์•„๋‹ˆ๋ผ **์ปจํŠธ๋กค ๋ฃจํŠธ `.venv` ์˜ ์‹ฌ๋งํฌ** (`worktree_prepare.sh:111-117`). ์„ค๊ณ„ ๋ฌธ์„œ์˜ `ln -s` ์™€ ๊ฐ™์Œ. ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋Š” ์ปจํŠธ๋กค ๋ฃจํŠธ ๊ฒฝ๋กœ. ๊ณต์œ  `info/exclude` ์— `/.venv`, `.pytest_cache/` (`:120-129`) โ€” ๋ณธ ์ฒดํฌ์•„์›ƒ exclude ์—๋„ ๋“ค์–ด๊ฐ€๋ฉฐ, ๋น„์ฐจ๋‹จ ๊ถŒ๊ณ (์„ ๋ฐ˜์˜)์™€ ๊ฐ™์Œ. + +**์„œ๋ธŒ๋ชจ๋“ˆ opt-in** (`--submodules`). ์‹คํŒจ ์‹œ ์ด๋ฒˆ ์‹œ๋„๋งŒ deinitยทmodules ์‚ญ์ œยทconfig ๋ณต์› (`:174-198`). ๋น„์–ด ์žˆ์ง€ ์•Š์€ ๋ฏธinit ๊ฒฝ๋กœ๋Š” ๋ณ€๊ฒฝ ์ „ ๊ฑฐ๋ถ€ (`:163-172`). + +**remove R1โ€“R6**: ๊ฒ€์‚ฌ ์ „๋ถ€ ์„ ํ–‰, ์‹คํŒจ ์‹œ ๋ฌด๋ณ€๊ฒฝ(exit 3). `--stop` ํ›„ ์žฌ๊ฒ€์‚ฌ(R1). ์„œ๋ธŒ๋ชจ๋“ˆ stash ๊ฑฐ๋ถ€. `git worktree remove --force` ๋Š” submodule trace ๊ฐ€ ์žˆ๊ณ  judge ๊ฐ€ ํ†ต๊ณผํ•œ ๋’ค์—๋งŒ (`:158-163`). ๋ฏธ์ปค๋ฐ‹/๋ฏธpush ๋Š” `UNPUSHED_REVS` (`worktree.py:27-30`: HEAD + `--all` โˆ’ stash โˆ’ remotes, ํƒœ๊ทธ ํฌํ•จ R6) ๋กœ ๊ฑฐ๋ถ€. **๊ฐ•์ œ ์ œ๊ฑฐ๊ฐ€ ๋ณด์กด๋œ ์ปค๋ฐ‹์„ ์‚ผํ‚ค๋Š” ๊ฒฝ๋กœ๋Š” ๊ฒ€์‚ฌ ํ†ต๊ณผ๋ฅผ ์ „์ œ๋กœ ๋ง‰ํ˜€ ์žˆ๋‹ค.** ์ž ๊ธด ์›ŒํฌํŠธ๋ฆฌ๋Š” force ๋„ ๊ฑฐ๋ถ€. + +## (2) resume (2679ddf) + +์›๋ž˜ `pane.cwd` ์™€ `--workspace` ๋ถˆ์ผ์น˜ยท๊ฒฝ๋กœ ์—†์Œ โ†’ exit 2 (`resume_session.sh:61-69`). **๋‹จ์ผ ์›Œํฌ์ŠคํŽ˜์ด์Šค** ์‚ฌ์šฉ์ž๋Š” cwd=์ฒดํฌ์•„์›ƒ์ด๋ผ ๊ธฐ์กด๊ณผ ๊ฐ™๋‹ค. `ROW_CWD` ์—†๋Š” ๋ ˆ๊ฑฐ์‹œ ํ–‰์€ ๊ฒ€์‚ฌ๋ฅผ ๊ฑด๋„ˆ๋œ€. ์ปจํŠธ๋กค ํ‰๋ฉด ๋ฐ”์ธ๋”ฉ์€ ํ–‰ ๊ฐ’์ด ์ด๊น€; ๊ณ„์‚ฐ ์‹คํŒจ๋Š” ์›ŒํฌํŠธ๋ฆฌ๋ฉด exit 2, ์ปจํŠธ๋กค ๋ฃจํŠธ๋ฉด ๊ฒฝ๊ณ  (`:92-98`). ์ด๋ฏธ ์‹คํ–‰ ์ค‘ ๊ฒฝ๋กœ์—๋„ ์˜์†ํ™”(์ฃผ์„ A13). ๊ธฐ์กด resume ์„ ๊นจ์ง€ ์•Š์Œ. + +## (3) ๋ฆฌ๋ทฐ identity (ํ•ญ๋ชฉ 5) + +`(loop_id, base_sha, head_sha)` ๊ธฐ๋กยทstale (`runner.py:831-840`). `--review-isolation` ๊ธฐ๋ณธ `worktree` ์ด์ง€๋งŒ **`--worktree`/`--base` ์—†์„ ๋•Œ `_worktree_context` ๋Š” ๋นˆ ๋ฌธ์ž์—ด** (`:564-570`) โ€” ํ”„๋กฌํ”„ํŠธ ๋ฐ”์ดํŠธ ๋™์ผ. ์˜ต์…˜๋งŒ ์ฃผ๋ฉด ๊ฒฝ๊ณ  ํ›„ ๋ฌด์‹œ (`:266-267`). detached ๋ฆฌ๋ทฐ ์›ŒํฌํŠธ๋ฆฌ add/remove (`:775-786`). dirty ์„œ๋ธŒ๋ชจ๋“ˆ ๊ฑฐ๋ถ€. `--remote-review` ๊ธฐ๋ณธ refuse. ์Šน๊ฒฉ ์ „ ์žฌํ™•์ธ ๊ฒฝ๋กœ ์กด์žฌ. ๊ธฐ๋ณธ(์˜ต์…˜ ์—†์Œ) ํšŒ๊ท€ ์—†์Œ. + +## (4) #20 ๊ฒฉ๋ฆฌ + +`pytest_configure` ๊ฐ€ ambient `MAM_JOBS_DIR`/`DELEGATE_JOB_LOGS_DIR`/`AGENT_SESSIONS_YAML` ์„ ์ˆ˜์ง‘ ์ „์— pop (`conftest.py:139-147`). ๋งค ํ…Œ์ŠคํŠธ autouse ๊ฐ€ tmp `.mam-test-state` ๋กœ ๋ณด๋ƒ„ (`:161-172`). issue15 ํ•ด์„ ํ…Œ์ŠคํŠธ๋Š” ์ž์ฒด `delenv` ๋กœ ์˜ตํŠธ์•„์›ƒ. ์„ธ์…˜ ๊ฐ€๋“œ๋Š” ๊ธฐ๋ก๋œ ์ œ์–ด ํ‰๋ฉด (`mam_state_leak_guard`). + +**์ง์ ‘ ํ™•์ธ**: ํ…Œ์ŠคํŠธ ์ „ํ›„ ๋ณธ ์ฒดํฌ์•„์›ƒ `.mam/jobs/*.json` **53**, `delegate_job_logs` **547** โ€” ์ฆ๊ฐ€ ์—†์Œ. ์›ŒํฌํŠธ๋ฆฌ `git status` ๊นจ๋—. + +## (5) acceptance5 + +์„ธ์…˜ยทsecret ๋Ÿฐ๋งˆ๋‹ค ๊ณ ์œ  (`test_issue18_liveness_contract.py:1194-1201`). skip ์€ ๋Ÿฌ๋„ˆ๊ฐ€ started ๋ฅผ ์ฐ๊ณ  ๊ตฌ๋…์ž๊ฐ€ ๊ทธ job ์„ ์ „ํ˜€ ๋ชป ๋ฐ›์€ ๊ฒฝ์šฐ๋งŒ (`:1164-1186`). started `--heartbeat` ๊ณ„์•ฝ์€ ์œ ์ง€. ์•ฝํ™” ์—†์Œ. + +## (6) A1โ€“A8 + +`test_issue21_acceptance.py`: mock herdr / mam_sandbox. ์‹ค์ œ herdr pane ์€ stage C. ๊ฒฐ์ •์ . ์ง€์ • ์Šค์œ„ํŠธ์— loop_b ์ „์ฒด๋Š” ์—†์Œ(์ž‘์„ฑ ์ธก ๋ณ„๋„). + +## (7)โ€“(8) ๋ฌธ์„œยท๋ฒ„์ „ + +design docยทSKILL ์ •ํ•ฉ. ๋น„์ฐจ๋‹จ 4๊ฑด(exclude ๊ณต์œ , prune, ์„œ๋ธŒ๋ชจ๋“ˆ ๊ฑฐ๋ถ€ ์ค‘๋ณต ์ถœ๋ ฅ ๋“ฑ)์€ ๋ณ‘ํ•ฉ ๋น„์ฐจ๋‹จ. ๋ฝ์Šคํ… 4.7.0 ์€ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ์˜ˆ์ • โ€” **๋ณ‘ํ•ฉ ์ฐจ๋‹จ ์•„๋‹˜**. ๋ธŒ๋žœ์น˜๋Š” 4.6.0. + +## ํ…Œ์ŠคํŠธ + +์›ŒํฌํŠธ๋ฆฌ: + +``` +PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q \ + tests/test_issue21_acceptance.py tests/test_issue21_phase2_loop_a.py \ + tests/test_issue21_worktree_prereqs.py tests/test_issue20_state_isolation.py \ + tests/test_issue15_registry_isolation.py tests/test_issue6_turn_handshake.py \ + tests/test_version_consistency.py +``` + +**100 passed, 1 skipped in 359.97s.** ๋ณธ ์ฒดํฌ์•„์›ƒ `main` `1134d74` ์œ ์ง€. + +## ์ข…ํ•ฉ + +ํ•ญ๋ชฉ 4ยท5ยท7ยท#20ยท#27 ์ด ์ฝ”๋“œยทํ…Œ์ŠคํŠธ์™€ ๋งž๋‹ค. `git worktree remove --force` ๋Š” ๊ฒ€์‚ฌ ํ†ต๊ณผ ํ›„์—๋งŒ. ์„ค๊ณ„ ์žฌ์ž‘์—… ๋ถˆํ•„์š”. + +[VERDICT: PASS] diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index 30ac927..d099771 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -29,7 +29,7 @@ export WORKSPACE_ROOT # Framework semantic version. Single runtime source of truth; kept in lockstep # with VERSIONS.md and the 8 SKILL.md frontmatters by tests/test_version_consistency.py. # NOTE: unlike other MAM_* variables this one is intentionally NOT env-overridable. -MAM_VERSION="4.6.0" +MAM_VERSION="4.7.0" export MAM_VERSION AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}" diff --git a/.agents/skills/lib_py/loop/eager.py b/.agents/skills/lib_py/loop/eager.py index 4fb4b34..d746479 100644 --- a/.agents/skills/lib_py/loop/eager.py +++ b/.agents/skills/lib_py/loop/eager.py @@ -13,7 +13,7 @@ import sys from typing import List, Sequence # Framework version locked with framework constants -FRAMEWORK_VERSION = "4.6.0" +FRAMEWORK_VERSION = "4.7.0" CORE_MODULES: Sequence[str] = ( # Standard library modules diff --git a/.agents/skills/multi-agent-mux-create/SKILL.md b/.agents/skills/multi-agent-mux-create/SKILL.md index dcc06c9..b63c35f 100644 --- a/.agents/skills/multi-agent-mux-create/SKILL.md +++ b/.agents/skills/multi-agent-mux-create/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-create description: "Create a new agent session (claude, antigravity/agy) in a dedicated herdr session for context-preserving long-running work. Always creates a herdr session โ€” never backgrounds with nohup/disown. Writes the new session to .mam/agent-sessions.yaml. Use when you want to start a fresh agent (no prior UUID) for a new project workspace." -version: 4.6.0 +version: 4.7.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md index 84ed3f4..2da2678 100644 --- a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md +++ b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-delegate-job description: "Delegate a unit of work to any autonomous agent (claude-code, hermes, agy, grok-build, codex, or a human) and observe it asynchronously over an MQTT event channel. Supported roles include orchestrator, worker, and reviewer." -version: 4.6.0 +version: 4.7.0 author: godopu license: MIT platforms: [linux, macos, windows] diff --git a/.agents/skills/multi-agent-mux-loop/SKILL.md b/.agents/skills/multi-agent-mux-loop/SKILL.md index e13c2b3..a9fbb90 100644 --- a/.agents/skills/multi-agent-mux-loop/SKILL.md +++ b/.agents/skills/multi-agent-mux-loop/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-loop description: "Run an autonomous planning-execution-review loop using multiple agents (Planner, Creator, Reviewers) in the workspace. Automatically orchestrates plan discussion, code changes, and peer reviews until a unanimous PASS is achieved or the maximum iteration limit is reached." -version: 4.6.0 +version: 4.7.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-monitor/SKILL.md b/.agents/skills/multi-agent-mux-monitor/SKILL.md index 570c860..01ca21f 100644 --- a/.agents/skills/multi-agent-mux-monitor/SKILL.md +++ b/.agents/skills/multi-agent-mux-monitor/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-monitor description: "Run a long-lived reconciler that watches .mam/agent-sessions.yaml against the actual herdr/agent runtime state and reconciles them. Use when you want live visibility into which agent sessions are running, which are dead, which have stale YAML entries, and which have new session ids that haven't been recorded yet. Runs as a persistent loop (`reconcile.sh --subscribe`) that keeps going until it times out, idles out, or is interrupted." -version: 4.6.0 +version: 4.7.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md b/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md index 95cb8b0..9b33333 100644 --- a/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md +++ b/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-orc-onboard description: "Register current or specified orchestrator session UUID into agent-sessions.yaml orchestrator_uuids list to prevent sub-agent discovery capture." -version: 4.6.0 +version: 4.7.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-resume/SKILL.md b/.agents/skills/multi-agent-mux-resume/SKILL.md index a5a163b..ace7f82 100644 --- a/.agents/skills/multi-agent-mux-resume/SKILL.md +++ b/.agents/skills/multi-agent-mux-resume/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-resume description: "Resume an existing agent (claude, antigravity/agy) conversation by UUID into a herdr session. Reads .mam/agent-sessions.yaml for the saved session/conversation id, spawns (or reuses) a herdr session of the matching name, and runs `claude -r ` or `agy --conversation ` inside. Use when you want to reattach to a previous session's context, or revive a session whose herdr died but the agent's conversation is still on disk." -version: 4.6.0 +version: 4.7.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-status/SKILL.md b/.agents/skills/multi-agent-mux-status/SKILL.md index 73f0b30..304b52b 100644 --- a/.agents/skills/multi-agent-mux-status/SKILL.md +++ b/.agents/skills/multi-agent-mux-status/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-status description: "Read-only instant snapshot of all agent herdr sessions โ€” name, YAML status, herdr alive, pane cmd/cwd, resume UUID on disk, and any drift. No mutation. Reuses reconcile.sh --dry-run for the diff logic. Use when you want to know 'what's running RIGHT NOW' without spinning up the monitor loop." -version: 4.6.0 +version: 4.7.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-stop/SKILL.md b/.agents/skills/multi-agent-mux-stop/SKILL.md index 8d49505..afbf1cf 100644 --- a/.agents/skills/multi-agent-mux-stop/SKILL.md +++ b/.agents/skills/multi-agent-mux-stop/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-stop description: "Stop an agent herdr session (claude, antigravity/agy) and update .mam/agent-sessions.yaml. Default stops gracefully and marks status=stopped with conversation preserved for resume. Does NOT delete on-disk conversation artifacts (jsonl/db) โ€” those are preserved unless --purge-conversation is passed. Use when ending a work session, switching to a different one, or cleaning up before a fresh start." -version: 4.6.0 +version: 4.7.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/BOOTSTRAP.ko.md b/BOOTSTRAP.ko.md index 4f568b3..c4f866c 100644 --- a/BOOTSTRAP.ko.md +++ b/BOOTSTRAP.ko.md @@ -2,7 +2,7 @@ ๋ณธ ๋ฌธ์„œ๋Š” `herdr_agent_orchestration` ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ๋ฐ ๋ฉ”์‹œ์ง• ๋ฐฑํ”Œ๋ ˆ์ธ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ์— ๋„์ž…ํ•˜์—ฌ ์ด์‹ํ•˜๊ณ , ์ƒˆ๋กœ์šด ๊ฐœ๋ฐœ์ž/์—์ด์ „ํŠธ๊ฐ€ ์ดˆ๊ธฐ ๊ฐ€๋™์„ ์‹œ์ž‘ํ•  ๋•Œ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ํ™˜๊ฒฝ ์„ค์ • ๋ฐ ๊ตฌ์ถ• ์ ˆ์ฐจ๋ฅผ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค. -[![Version: v4.6.0](https://img.shields.io/badge/version-v4.6.0-blue.svg)](VERSIONS.md) +[![Version: v4.7.0](https://img.shields.io/badge/version-v4.7.0-blue.svg)](VERSIONS.md) ์ƒˆ๋กœ์šด ์—์ด์ „ํŠธ๋Š” ์ด ์•ˆ๋‚ด์„œ์— ๊ธฐ์ˆ ๋œ ์ ˆ์ฐจ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•˜์—ฌ ์ดˆ๊ธฐ ํ™˜๊ฒฝ์„ ์•ˆ์ •์ ์œผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/BOOTSTRAP.md b/BOOTSTRAP.md index fa304e2..880dead 100644 --- a/BOOTSTRAP.md +++ b/BOOTSTRAP.md @@ -2,7 +2,7 @@ This document guides you through the setup and initialization procedures required to adopt the `herdr_agent_orchestration` orchestration and messaging backplane workflow in a new project, enabling a new developer or agent to get up and running quickly. -[![Version: v4.6.0](https://img.shields.io/badge/version-v4.6.0-blue.svg)](VERSIONS.md) +[![Version: v4.7.0](https://img.shields.io/badge/version-v4.7.0-blue.svg)](VERSIONS.md) A new agent can follow the steps in this guide sequentially to establish a stable and reliable initial environment. diff --git a/MESSAGING.md b/MESSAGING.md index 9efb484..ebc80c7 100644 --- a/MESSAGING.md +++ b/MESSAGING.md @@ -1,6 +1,6 @@ # Messaging System Technical Analysis & Architecture Report -Applies to: MAM v4.6.0 +Applies to: MAM v4.7.0 This report provides a comprehensive, deep-dive analysis of the messaging system implemented in the `multi-agent-mux-delegate-job` skill. It covers the MQTT broker architecture, event protocols, job lifecycles, codebase internals, cross-system integration, and a list of known limitations along with production recommendations. diff --git a/README.ko.md b/README.ko.md index 1faac87..2bb889d 100644 --- a/README.ko.md +++ b/README.ko.md @@ -2,7 +2,7 @@ Herdr์™€ MQTT ๋ธŒ๋กœ์ปค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์ถ•๋œ ๊ณ ์‹ ๋ขฐ์„ฑ **๋‹ค์ค‘ ์—์ด์ „ํŠธ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ๋ฐ ๋ฉ”์‹œ์ง• ๋ฐฑํ”Œ๋ ˆ์ธ** ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. `claude`, `agy`, `hermes`, `grok`, `opencode`, `codex` ๋“ฑ ๋‹ค์ค‘ LLM ๋ฐฑ์—”๋“œ ์—์ด์ „ํŠธ ์ „๋ฐ˜์— ๊ฑธ์ณ ์žฅ์‹œ๊ฐ„ ์ˆ˜ํ–‰๋˜๋Š” ์ž‘์—…(์ฝ”๋“œ ์ƒ์„ฑ, ๋ฆฌํŒฉํ† ๋ง, ๋ณด์•ˆ ๊ฒ€ํ†  ๋“ฑ)์„ ์กฐ์ •, ๊ฒฉ๋ฆฌ ๋ฐ ๊ฐ์‚ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. -[![Version: v4.6.0](https://img.shields.io/badge/version-v4.6.0-blue.svg)](VERSIONS.md) +[![Version: v4.7.0](https://img.shields.io/badge/version-v4.7.0-blue.svg)](VERSIONS.md) --- diff --git a/README.md b/README.md index 931688d..611b1f5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An advanced, high-reliability **Multi-Agent Orchestration & Messaging Backplane** framework built on Herdr and MQTT. It is designed to coordinate, isolate, and audit long-running agent tasks (such as code generation, refactoring, and security reviews) across multiple LLM backend clients (`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`). -[![Version: v4.6.0](https://img.shields.io/badge/version-v4.6.0-blue.svg)](VERSIONS.md) +[![Version: v4.7.0](https://img.shields.io/badge/version-v4.7.0-blue.svg)](VERSIONS.md) --- diff --git a/VERSIONS.md b/VERSIONS.md index dc0804c..0f2c9af 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -6,8 +6,8 @@ ## ๐Ÿ“Œ ํ˜„์žฌ ๋ฒ„์ „ ๊ฐœ์š” (Current Release) -- **ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒ„์ „**: `v4.6.0` -- **์ตœ์‹  ๋ฆด๋ฆฌ์Šค ์ผ์‹œ**: 2026-09-14 (KST) +- **ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒ„์ „**: `v4.7.0` +- **์ตœ์‹  ๋ฆด๋ฆฌ์Šค ์ผ์‹œ**: 2026-09-20 (KST) - **๊ธฐ์ค€ ๋ธŒ๋žœ์น˜**: `main` - **ํ•ต์‹ฌ ์•„ํ‚คํ…์ฒ˜**: - **Terminal ์ƒํƒœ ๋‹จ๋ฐฉํ–ฅ ๋ถˆ๋ณ€์„ฑ (`engine.py`, Issue #6 Phase 1)**: `completed`, `error`, `cancelled` ํ„ฐ๋ฏธ๋„ ์ƒํƒœ ์ง„์ž… ํ›„ ํ›„์† ์ด๋ฒคํŠธ๋‚˜ ์—…๋ฐ์ดํŠธ์— ์˜ํ•œ ๋ฎ์–ด์“ฐ๊ธฐ๋ฅผ ์—„๊ฒฉํžˆ ์ฐจ๋‹จ(Terminal Immutability). `registry.py close` ์„œ๋ธŒ์ปค๋งจ๋“œ ๋ฐ fail-closed ์กฐ๊ธฐ ์ข…๋ฃŒ ๋„์ž…. @@ -15,29 +15,49 @@ - **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. - **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.6.0`๋กœ ๋™๊ธฐํ™”๋˜์–ด ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. +๋ชจ๋“  8๊ฐœ ์Šคํ‚ฌ์€ YAML frontmatter ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(`author`, `version`, `platforms`, `environments`) ํ‘œ์ค€ํ™”๋ฅผ ํ†ตํ•ด `v4.7.0`๋กœ ๋™๊ธฐํ™”๋˜์–ด ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. | ์Šคํ‚ฌ๋ช… | ๋ฒ„์ „ | ์—ญํ•  ๋ฐ ์ฃผ์š” ์ฑ…์ž„ | ์ƒํƒœ | | :--- | :---: | :--- | :---: | -| **`multi-agent-mux-create`** | `4.6.0` | ์—์ด์ „ํŠธ ์„ธ์…˜ ์‹ ๊ทœ ์ƒ์„ฑ ๋ฐ Herdr ์ปจํ…Œ์ด๋„ˆ ๊ฒฉ๋ฆฌ ์Šคํฐ | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-stop`** | `4.6.0` | ๋Œ€ํ™” UUID ์›์ž์  ์บก์ฒ˜ ๋ฐ ์„ธ์…˜ ์•ˆ์ „ ์ข…๋ฃŒ (Graceful Stop) | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-resume`** | `4.6.0` | ์˜จ๋””์Šคํฌ ๋Œ€ํ™” ์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ Tier-1 ์ดˆ๊ณ ์† ์„ธ์…˜ ๋ณต์› | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-status`** | `4.6.0` | ์‹ค์‹œ๊ฐ„ Herdr ์„ธ์…˜ ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋“œ๋ฆฌํ”„ํŠธ ์Šค๋ƒ…์ƒท ์กฐํšŒ | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-monitor`** | `4.6.0` | YAML โ†” ๋Ÿฐํƒ€์ž„ ์ƒํƒœ ๊ฐ„ ์ž์œจ ์กฐ์ •์ž (Reconciler Loop) | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-delegate-job`** | `4.6.0` | MQTT ์ด๋ฒคํŠธ ์ฑ„๋„ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ๋‹จ์œ„ ์ž‘์—… ์œ„์ž„ | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-loop`** | `4.6.0` | Planner-Creator-Reviewer 3์ž ์ž์œจ ๊ณ„ํšยท์‹คํ–‰ยทํ”ผ์–ด๋ฆฌ๋ทฐ ๋ฃจํ”„ | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-orc-onboard`** | `4.6.0` | ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ UUID ๊ฒฉ๋ฆฌ ๋“ฑ๋ก ๋ฐ ์„œ๋ธŒ ์„ธ์…˜ ์˜ค์—ผ ๋ฐฉ์ง€ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-create`** | `4.7.0` | ์—์ด์ „ํŠธ ์„ธ์…˜ ์‹ ๊ทœ ์ƒ์„ฑ ๋ฐ Herdr ์ปจํ…Œ์ด๋„ˆ ๊ฒฉ๋ฆฌ ์Šคํฐ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-stop`** | `4.7.0` | ๋Œ€ํ™” UUID ์›์ž์  ์บก์ฒ˜ ๋ฐ ์„ธ์…˜ ์•ˆ์ „ ์ข…๋ฃŒ (Graceful Stop) | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-resume`** | `4.7.0` | ์˜จ๋””์Šคํฌ ๋Œ€ํ™” ์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ Tier-1 ์ดˆ๊ณ ์† ์„ธ์…˜ ๋ณต์› | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-status`** | `4.7.0` | ์‹ค์‹œ๊ฐ„ Herdr ์„ธ์…˜ ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋“œ๋ฆฌํ”„ํŠธ ์Šค๋ƒ…์ƒท ์กฐํšŒ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-monitor`** | `4.7.0` | YAML โ†” ๋Ÿฐํƒ€์ž„ ์ƒํƒœ ๊ฐ„ ์ž์œจ ์กฐ์ •์ž (Reconciler Loop) | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-delegate-job`** | `4.7.0` | MQTT ์ด๋ฒคํŠธ ์ฑ„๋„ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ๋‹จ์œ„ ์ž‘์—… ์œ„์ž„ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-loop`** | `4.7.0` | Planner-Creator-Reviewer 3์ž ์ž์œจ ๊ณ„ํšยท์‹คํ–‰ยทํ”ผ์–ด๋ฆฌ๋ทฐ ๋ฃจํ”„ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-orc-onboard`** | `4.7.0` | ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ UUID ๊ฒฉ๋ฆฌ ๋“ฑ๋ก ๋ฐ ์„œ๋ธŒ ์„ธ์…˜ ์˜ค์—ผ ๋ฐฉ์ง€ | โœ… ๋ฐฐํฌ | --- ## ๐Ÿ“‹ ๋ฒ„์ „๋ณ„ ์ƒ์„ธ ๋ณ€๊ฒฝ ๋‚ด์—ญ (Changelog) +### ๐Ÿš€ `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 ์— ๋‚จ์•„ ์žˆ๋‹ค.