From fa59741a9bbd711ef36dcb34d254e1154d633dcf Mon Sep 17 00:00:00 2001 From: Godopu Date: Sun, 6 Sep 2026 13:27:40 +0900 Subject: [PATCH] docs(reports): archive issue #5 consolidated cross-review report (Rev.2) --- .../issue_5_consolidated_review_report.md | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 .agents/reports/issue_5_consolidated_review_report.md diff --git a/.agents/reports/issue_5_consolidated_review_report.md b/.agents/reports/issue_5_consolidated_review_report.md new file mode 100644 index 0000000..9c936b9 --- /dev/null +++ b/.agents/reports/issue_5_consolidated_review_report.md @@ -0,0 +1,142 @@ +# ๐Ÿ“Š Issue #5 ๊ธฐ์ˆ  ์‹ฌ์ธต ๊ฒ€ํ†  ํ†ตํ•ฉ ๋ณด๊ณ ์„œ (Rev. 2) + +- **๋Œ€์ƒ ์ด์Šˆ**: [Gitea Issue #5 โ€” `[Refactor] lib.sh ์ž‘์—… ์œ„์ž„ ๋กœ์ง ๋ฐ run_loop.sh ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ์˜ Python ๋ชจ๋“ˆํ™” (lib_py ์Šน๊ฒฉ)`](https://git.godopu.com/tmpl/multi-agent-mux/issues/5) +- **์ž‘์„ฑ ์ฃผ์ฒด**: Antigravity (์ด๊ด„ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ) +- **๊ฒ€ํ†  ์ฐธ์—ฌ์ž**: 3๊ฐœ ํŒ€ ๋ฆฌ๋” ๋ฆฌ๋ทฐ์–ด ์—์ด์ „ํŠธ ์ „์› ์ฐธ์—ฌ (1์ฐจ ๊ฒ€ํ†  ๋ฐ 2์ฐจ ๊ต์ฐจ ๊ฒ€ํ†  ์™„๋ฃŒ) + - `planner-reviewer-claude-01` + - `reviewer-creator-grok-01` + - `planner-reviewer-codex-01` +- **๊ฒ€ํ†  ์ผ์‹œ**: 2026-09-06 (Rev.1 ๋ฐœํ–‰ ๋ฐ 2์ฐจ ์ •๋ฐ€ ๊ต์ฐจ ๊ฒ€์ฆ ๋ฐ˜์˜) +- **๊ธฐ์ค€ ๋ธŒ๋žœ์น˜ / ์ปค๋ฐ‹**: `refactor` (`47640e2`) +- **์ข…ํ•ฉ ํ•ฉ์˜ ์ƒํƒœ**: **ํŒŒ์ด์ฌ ๋ชจ๋“ˆํ™” ๋ฐฉํ–ฅ์„ฑ ๋งŒ์žฅ์ผ์น˜ ์ง€์ง€ (๋‹จ, ์ž๋™์™„๋ฃŒ ์ฆ๊ฑฐ ๊ณ„์•ฝยทCAS ์ƒํƒœ ์ „์ดยท๋Ÿฐํƒ€์ž„ ๋ถˆ๋ณ€์„ฑ์˜ ํ•„์ˆ˜ ์•ˆ์ „ ์กฐ๊ฑด ์„ ๊ฒฐ์„ ์ „์ œ๋กœ ํ•œ ์กฐ๊ฑด๋ถ€ ๋™์˜์ด๋ฉฐ, ๊ธฐ๊ณ„ ํŒ์ •์šฉ ์ตœ์ข… ๊ตฌํ˜„ PASS๊ฐ€ ์•„๋‹˜)** + +--- + +## 1. ๊ฐœ์š” ๋ฐ ํ•ฉ์˜ ์ˆ˜์ค€์˜ ์‚ฌ์‹ค์  ๊ธฐ์ˆ  + +๋ณธ ๋ณด๊ณ ์„œ๋Š” `lib.sh`์˜ ์ž‘์—… ์œ„์ž„ ๋กœ์ง ๋ฐ `run_loop.sh` ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ์ „์ฒด๋ฅผ ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€(`lib_py`)๋กœ ์Šน๊ฒฉ ๋ฐ ๋ชจ๋“ˆํ™”ํ•˜๋Š” ์ œ์•ˆ(Issue #5)์— ๋Œ€ํ•ด, ์„ธ ํŒ€ ๋ฆฌ๋” ๋ฆฌ๋ทฐ์–ด๊ฐ€ ์ฝ”๋“œ๋ฒ ์ด์Šค ์‹ค์ธก๊ณผ 2์ฐจ์— ๊ฑธ์นœ ๋…๋ฆฝ์  ๊ต์ฐจ ๋ถ„์„์„ ํ†ตํ•ด ๋„์ถœํ•œ ์ตœ์ข… ์ •์ œ ํ†ตํ•ฉ ๋ณด๊ณ ์„œ์ž…๋‹ˆ๋‹ค. + +์„ธ ๋ฆฌ๋ทฐ์–ด ๋ชจ๋‘ **ํŒŒ์ด์ฌ ๋ชจ๋“ˆํ™”์˜ ํ•„์š”์„ฑ๊ณผ ์•„ํ‚คํ…์ฒ˜ ๋ฐฉํ–ฅ์„ฑ์— ๊ณตํ†ต์œผ๋กœ ์ฐฌ์„ฑ**ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ์ฐฌ์„ฑ์€ "๋ฌด์กฐ๊ฑด์  ๊ตฌํ˜„ ์Šน์ธ"์ด ์•„๋‹ˆ๋ฉฐ, ์ž๋™์™„๋ฃŒ ์˜คํƒ ๋ฐฉ์ง€, ๋‹ค์ค‘ Writer ๊ฒฝํ•ฉ, ๋Ÿฐํƒ€์ž„ ์ž๊ธฐ์ˆ˜์ • ํฌ๋ž˜์‹œ(B-13)์˜ ๋‹จ๊ณ„์  ํ•ด์†Œ ์กฐ๊ฑด์— ๋Œ€ํ•ด ์—„๊ฒฉํ•œ ๊ธฐ์ˆ ์  ์•ˆ์ „ ๊ณ„์•ฝ์ด ์ˆ˜๋ฆฝ๋˜์–ด์•ผ ํ•จ์„ ๊ฐ•๋ ฅํžˆ ์š”๊ตฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +### ๐Ÿ“Œ ๋ฆฌ๋ทฐ์–ด๋ณ„ ํ‰๊ฐ€ ๋ฐ ํŒ์ • ํ† ํฐ ํ˜„ํ™ฉ (์‚ฌ์‹ค ๋Œ€์กฐ) + +| ๋ฆฌ๋ทฐ์–ด ์—์ด์ „ํŠธ | ์„ธ์…˜๋ช… | 1์ฐจ ๊ฒ€ํ†  Job | 2์ฐจ ๊ฒ€ํ†  Job | ๊ณต์‹ `[VERDICT: ...]` ํ† ํฐ | ์ข…ํ•ฉ ํ‰๊ฐ€ ์„ฑ๊ฒฉ | +| :--- | :--- | :---: | :---: | :---: | :--- | +| **Claude** | `planner-reviewer-claude-01` | `1919bf5e` | `cdc926e3` | **๋ฏธ๋ฐœํ–‰ (์‚ฐ๋ฌธํ˜• ์ง€์ง€)** | ๋ฐฉํ–ฅ์„ฑ ์ง€์ง€, Herdr 1๊ธ‰ API ์‹ค์žฌ ๋ฐœ๊ฒฌ, 1,621์ค„ subprocess ํ…Œ์ŠคํŠธ ์žฌํƒ€๊ฒŸํŒ… ๊ณต์ˆ˜ ๊ฒฝ๊ณ  | +| **Grok** | `reviewer-creator-grok-01` | `9885ecd6` | `3e3b6d18` | **`[VERDICT: PASS]`** | ๋ฐฉํ–ฅ์„ฑ ์กฐ๊ฑด๋ถ€ PASS, `interactive_ready` ๋ฐฐ์ œ ์‹ค์ธก ์ฆ๋ช…, 15s ๋””๋ฐ”์šด์Šค ๋ฐ 6๋Œ€ ๊ฒŒ์ดํŠธ ์ œ์‹œ | +| **Codex** | `planner-reviewer-codex-01` | `5da43b79` | `14f9eac4` | **๋ฏธ๋ฐœํ–‰ (์กฐ๊ฑด๋ถ€ ๋™์˜)** | ๋ฐฉํ–ฅ์„ฑ ๋™์˜, ์‹คํ–‰ ๊ท€์†(Attribution)ยท๋ฆฌํฌํŠธ ํ™•์ • ๊ณ„์•ฝยท๋‹จ์ผ CAS ์ „์ด ํ•„์ˆ˜ ์กฐ๊ฑดํ™” | + +> 3๋ช… ์ค‘ 1๋ช…(Grok)๋งŒ ๋‹จ๋… ํ–‰ `[VERDICT: PASS]` ํ† ํฐ์„ ๋‚จ๊ฒผ์œผ๋ฉฐ, Claude์™€ Codex๋Š” ๊ตฌ์ฒด์  ์•ˆ์ „ ์กฐ๊ฑด์„ ์š”๊ตฌํ•˜๋Š” ์‚ฐ๋ฌธํ˜• ์กฐ๊ฑด๋ถ€ ๋™์˜๋ฅผ ์ œ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ณธ ํ”„๋กœ์ ํŠธ์˜ ์—„๋ฐ€ํ•œ fail-closed ๊ณ„์•ฝ์— ๋”ฐ๋ผ ์ด๋ฅผ "๋งŒ์žฅ์ผ์น˜ ์ตœ์ข… PASS"๋กœ ์™œ๊ณกํ•˜์ง€ ์•Š๊ณ  **"์•ˆ์ „ ๊ณ„์•ฝ ์„ ๊ฒฐ์„ ์ „์ œ๋กœ ํ•œ ๋ฐฉํ–ฅ์„ฑ ๋งŒ์žฅ์ผ์น˜ ํ•ฉ์˜"**๋กœ ๋ช…๋ฌธํ™”ํ•ฉ๋‹ˆ๋‹ค. + +--- + +## 2. 2์ฐจ ๊ต์ฐจ ๊ฒ€์ฆ์„ ํ†ตํ•ด ๋ณต์›๋œ ํ•ต์‹ฌ ์•ˆ์ „ ๊ณ„์•ฝ + +์„ธ ๋ฆฌ๋ทฐ์–ด์˜ 2์ฐจ ์ •๋ฐ€ ํ”ผ๋“œ๋ฐฑ์„ ํ†ตํ•ด 1์ฐจ ์š”์•ฝ ๊ณผ์ •์—์„œ ๋ˆ„๋ฝ๋˜๊ฑฐ๋‚˜ ๋‹จ์ˆœํ™”๋˜์—ˆ๋˜ ํ•ต์‹ฌ ์—”์ง€๋‹ˆ์–ด๋ง ๊ณ„์•ฝ๋“ค์ด ๋ณต์›๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +### 2-1. ์‹คํ–‰ ๊ท€์†(Execution Attribution) ๋ณด์žฅ ๊ณ„์•ฝ (Codex ๋ณต์›, ์ „์› ์ง€์ง€) +- ๋‹จ์ˆœํžˆ ํ„ฐ๋ฏธ๋„์ด `idle`์ด๋ผ๊ณ  ํ•ด์„œ ์™„๋ฃŒ๋กœ ํŒ๋‹จํ•ด์„œ๋Š” ์•ˆ ๋จ (์„ธ์…˜ ์žฌ์‚ฌ์šฉ ์‹œ ์ด์ „ ์ž‘์—…์˜ ์ž”์—ฌ ์œ ํœด ์ƒํƒœ ์˜ค์ธ ์œ„ํ—˜). +- ์ž๋™์™„๋ฃŒ ํ›„๋ณด๋Š” ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ **4์ค‘ ์‹๋ณ„์ž**๊ฐ€ ์ผ์น˜ํ•ด์•ผ ํ•จ: + `Job ID` + `Attempt ID` + `Agent/Session UUID` + `Pane/Process Generation` +- ๋””์ŠคํŒจ์น˜ ํ™•์ธ ์‹œ์  ์ดํ›„ ํ•ด๋‹น ์‹œ๋„(Attempt)์˜ ์‹ค์ œ ์‹คํ–‰ ๋ฐ ์‘๋‹ต์ด ๊ด€์ธก๋œ ๊ฒฝ์šฐ์—๋งŒ ์™„๋ฃŒ ํ›„๋ณด๋กœ ์ธ์ •. +- ๋™์ผ ์—์ด์ „ํŠธ ์„ธ์…˜์—๋Š” ๋™์‹œ์— ๋‹จ ํ•˜๋‚˜์˜ Job๋งŒ ๋ฐฐ์ •. + +### 2-2. ๋ฆฌํฌํŠธ ํ™•์ • ๊ณ„์•ฝ์˜ ๋‹จ๊ณ„์  ์ง„ํ™” (Grok vs Codex ์ด๊ฒฌ ์ •๋ฆฌ) +- **1์ฐจ/๋ณด์กฐ ํ•„ํ„ฐ (Grok ์ œ์•ˆ)**: `report-final.md` mtime์ด ์ผ์ • ๋””๋ฐ”์šด์Šค ๊ตฌ๊ฐ„(๊ธฐ๋ณธ ๊ถŒ์žฅ โ‰ฅ15์ดˆ, monotonic clock ์ธก์ •) ๋™์•ˆ ๋ถˆ๋ณ€์ผ ๊ฒƒ. +- **2์ฐจ/๊ฐ•ํ™” ๊ณ„์•ฝ (Codex ์ œ์•ˆ)**: ์‹œ๊ฐ„ ๊ฒฝ๊ณผ(15s)๋งŒ์œผ๋กœ๋Š” ํŒŒ์ผ ์“ฐ๊ธฐ ์™„๋ฃŒ๋ฅผ ์™„๋ฒฝํžˆ ์ฆ๋ช…ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ์ž„์‹œ ํŒŒ์ผ ์ž‘์„ฑ ํ›„ **์›์ž์  ์ด๋ฆ„ ๋ณ€๊ฒฝ(Atomic Rename)** ๋ฐ `Attempt ID`์™€ `Report Hash`๊ฐ€ ๋ช…์‹œ๋œ **์™„๋ฃŒ ๋งค๋‹ˆํŽ˜์ŠคํŠธ(Finalization Manifest)** ์ƒ์„ฑ์„ ์ตœ์ข… ๋ชฉํ‘œ๋กœ ์„ค์ •. +- **์ ์šฉ ๊ทœ์น™**: Phase 2a์—์„œ๋Š” mtime ๋””๋ฐ”์šด์Šค(15s)๋ฅผ ๋ณด์กฐ ํ•„ํ„ฐ๋กœ ๊ฒ€์ฆํ•˜๊ณ , Phase 2b์—์„œ๋Š” ํ™•์ • ๊ณ„์•ฝ ๋ฏธ์ง€์› ์ž‘์—…์€ ์ž๋™ ์™„๋ฃŒ ๋Œ€์‹  "๋ณต๊ตฌ ํ›„๋ณด/์ˆ˜๋™ ํ™•์ธ"์œผ๋กœ ๋‚จ๊น€. + +### 2-3. ๋‹จ์ผ ์ƒํƒœ ์ „์ด ์ฃผ์ฒด(Engine)์™€ ์›์ž์  CAS (Compare-And-Swap) +- **์ฑ…์ž„์˜ ์—„๊ฒฉํ•œ ๊ฒฉ๋ฆฌ**: + - `watcher.py`: ์˜ค์ง ๊ด€์ธก ์ฆ๊ฑฐ(์ฆ๊ฑฐ ํŠœํ”Œ: Event, Herdr status, Report path/hash)๋งŒ ์ˆ˜์ง‘ํ•˜์—ฌ ํ๋กœ ์ „๋‹ฌ. + - `engine.py`: ๋ฝ ํ•˜์—์„œ ์ƒํƒœ ์ „์ด๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” **์œ ์ผํ•œ ์ฃผ์ฒด**. +- **์›์ž์  ์ƒํƒœ ์ „์ด ๋ถˆ๋ณ€์‹**: + - Agent์˜ `publish_event`์™€ Engine์˜ Watcher ๊ฐ์‹œ ๋ชจ๋‘ **๊ณตํ†ต ์ „์ด ํ•จ์ˆ˜/์„œ๋น„์Šค**๋ฅผ ์‚ฌ์šฉ. + - `expected attempt` ๋ฐ `๋น„terminal(started/progress)` ์ƒํƒœ์ž„์„ ํ™•์ธํ•œ ํ›„ ์›์ž์ ์œผ๋กœ ์ „์ด (CAS). + - ๋ช…์‹œ์  `error` ๋˜๋Š” `cancelled` ์ƒํƒœ๋ฅผ ์ž๋™ `completed`๋กœ ๋ฎ์–ด์“ฐ์ง€ ์•Š์Œ. + - ์™„๋ฃŒ ํ™•์ • ํ›„ ๋’ค๋Šฆ๊ฒŒ ๋„์ฐฉํ•œ `progress`๋‚˜ `started` ์ด๋ฒคํŠธ๋กœ ์ƒํƒœ๋ฅผ ์—ญํ–‰์‹œํ‚ค์ง€ ์•Š์Œ. +- **์ž ๊ธˆ(Lock) ๋‚ด I/O ๋ฐ ์žฌ์ง„์ž… ๋Œ€๊ธฐ ์œ„ํ—˜ ๋ฐฉ์ง€**: + - Registry Lock ์œ ์ง€ ์ƒํƒœ์—์„œ ์ž์‹ ํ”„๋กœ์„ธ์Šค ํ˜ธ์ถœ(subprocess)์ด๋‚˜ ๋„คํŠธ์›Œํฌ I/O(MQTT)๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์Œ. + - ์งง์€ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ƒํƒœ ํ™•์ • ๋ฐ Pending Notification ๊ธฐ๋ก โž” Lock ์ฆ‰์‹œ ํ•ด์ œ โž” ๋น„๋™๊ธฐ MQTT ๋ฐœํ–‰ ๋ฐ ๋ฉฑ๋“ฑ ์žฌ์‹œ๋„ ์ˆ˜ํ–‰. + - `auth_token != null`์ธ ๊ฒฝ์šฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์˜ ํ† ํฐ์œผ๋กœ HMAC ์„œ๋ช…ํ•˜์—ฌ ์ •๊ทœ ๋ฐœํ–‰ (๋ˆ„๋ฝ ์‹œ silent drop ๋ฐฉ์ง€). + +### 2-4. Herdr ๋„ค์ดํ‹ฐ๋ธŒ API์˜ ์ •ํ™•ํ•œ ํ˜ธ์ถœ ๊ทœ๊ฒฉ ๋ฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ +- **์˜ต์…˜ ๋ช…์‹œ ํ•„์ˆ˜**: + - `herdr agent wait `์˜ CLI ๊ธฐ๋ณธ๊ฐ’์€ `idle, done, or blocked`์ด๋ฉฐ, `blocked`๋Š” permission_required๋‚˜ ์ž…๋ ฅ ๋Œ€๊ธฐ ์ƒํƒœ์ด๋ฏ€๋กœ ์™„๋ฃŒ๊ฐ€ ์•„๋‹˜. + - ๋”ฐ๋ผ์„œ Watcher๋Š” ๋ฐ˜๋“œ์‹œ ์ƒํƒœ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜์—ฌ ํ˜ธ์ถœํ•ด์•ผ ํ•จ: + `herdr agent wait --until idle --until done --timeout ` +- **`interactive_ready`์˜ ์„ฑ๊ฒฉ ๊ทœ์ •**: + - ์—์ด์ „ํŠธ๊ฐ€ ์ฝ”๋”ฉ ์ž‘์—… ์ค‘(`agent_status=working`)์ผ ๋•Œ๋„ `interactive_ready=true`์ž„์ด ์‹ค์ธก์œผ๋กœ ์ฆ๋ช…๋จ. + - ๋”ฐ๋ผ์„œ `interactive_ready`๋Š” ์™„๋ฃŒ ์ถฉ๋ถ„์กฐ๊ฑด์ด ์•„๋‹ˆ๋ฉฐ, ์˜ค์ง `agent_status in {"idle", "done"}`๋งŒ ์™„๋ฃŒ ํ›„๋ณด๋กœ ์ธ์ •. +- **์†Œ์ผ“ ๋‹จ์ ˆ ๋ฐ ๋ฒ„์ „ ๋“œ๋ฆฌํ”„ํŠธ ๊ฐ€๋“œ**: + - Herdr ์†Œ์ผ“ ๋‹จ์ ˆ ์‹œ ๋ฐ˜ํ™˜๋˜๋Š” `unknown`์„ idle๋กœ ์˜ค์ธํ•˜์ง€ ์•Š๊ณ , ์žฌ์—ฐ๊ฒฐ ๋ฐฑ์˜คํ”„ ๋ฐ ์Šค๋ƒ…์ƒท ์žฌ๋™๊ธฐํ™”๋ฅผ ์ˆ˜ํ–‰. + - ์˜์กดํ•˜๋Š” Herdr CLI/JSON ํ•„๋“œ์˜ ์ตœ์†Œ ํ˜ธํ™˜ ๋ฒ„์ „์„ ๊ฒ€์ฆํ•˜๊ณ , ๋ถˆ์ผ์น˜ ์‹œ fail-closed(์ˆ˜๋™ ํ™•์ธ)๋กœ ํด๋ฐฑ. + +### 2-5. ํŠน์ˆ˜ ์ž‘์—…์— ๋Œ€ํ•œ ์˜ˆ์™ธ ๋ฐ Fail-Closed ์›์น™ +- **์™ธ๋ถ€ ๋ถ€์ˆ˜ํšจ๊ณผ ์ž‘์—… ์˜ˆ์™ธ**: + - Gitea ๋Œ“๊ธ€ ๋“ฑ๋ก, Git push, ๋ฐฐํฌ ๋“ฑ ๋ฆฌํฌํŠธ ํŒŒ์ผ ์ƒ์„ฑ ์™ธ์— ์™ธ๋ถ€ ์‹œ์Šคํ…œ์— ๋ถ€์ˆ˜ํšจ๊ณผ๋ฅผ ์ผ์œผํ‚ค๋Š” ์ž‘์—…์€ ๋ฆฌํฌํŠธ ํŒŒ์ผ ์กด์žฌ๋งŒ์œผ๋กœ ์ž๋™์™„๋ฃŒ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์Œ (์™ธ๋ถ€ ์‹คํ–‰ ์™„๋ฃŒ ์ฆ๊ฑฐ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์ˆ˜๋™ ํ™•์ธ ์œ ์ง€). +- **๋ฌดํŒ์ • Reviewer ๋ฆฌํฌํŠธ ์ฒ˜๋ฆฌ**: + - Reviewer ์ž‘์—…์˜ ๋ฆฌํฌํŠธ ๋งˆ์ง€๋ง‰ ์ค„์— `[VERDICT: PASS]` ๋˜๋Š” `[VERDICT: NOT PASS]` ํ† ํฐ์ด ๋ˆ„๋ฝ๋œ ๊ฒฝ์šฐ, `completed` ์Šน๊ฒฉ ์ ˆ๋Œ€ ๊ธˆ์ง€. + - ์ž‘์—… deadline ๋„๋‹ฌ ์‹œ ๋ช…์‹œ์  `error` ๋ฐœํ–‰ ๋˜๋Š” ๋ฐ˜๋ ค ์ฒ˜๋ฆฌ (Fail-Closed ์—„๊ฒฉ ์œ ์ง€). + - Reviewer์˜ `completed`(๋ฆฌ๋ทฐ ํ–‰์œ„ ์™„๋ฃŒ)์™€ `PASS`(๊ฒ€์ฆ ํ†ต๊ณผ)๋ฅผ ์ ˆ๋Œ€ ํ˜ผ๋™ํ•˜์ง€ ์•Š์Œ. + +--- + +## 3. ํ•ฉ์˜๋œ ์•ˆ์ „ ์ƒํƒœ ๋จธ์‹  ์•„ํ‚คํ…์ฒ˜ + +```mermaid +flowchart TD + subgraph Watcher["์ด์ค‘ ๊ฐ์‹œ ์ฒด๊ณ„ (Watcher - ๊ด€์ธก ์ฆ๊ฑฐ ์ˆ˜์ง‘ ์ „์šฉ)"] + W1["์ฑ„๋„ 1: MQTT ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ ๋ฐ ๋กœ์ปฌ ์ž‘์—… ๋ ˆ์ฝ”๋“œ"] + W2["์ฑ„๋„ 2: herdr agent wait --until idle --until done"] + W3["ํŒŒ์ผ ์ฆ๊ฑฐ: ์ง€์ • ๊ฒฝ๋กœ report-final.md mtime ๋””๋ฐ”์šด์Šค (โ‰ฅ15s)"] + end + + subgraph Gate["์•ˆ์ „ ๊ฒ€์ฆ ๊ฒŒ์ดํŠธ (Fail-Closed AND Gate)"] + G1{"4์ค‘ ๊ท€์† ์ผ์น˜?\n(Job ID + Attempt + UUID + Generation)"} + G2{"agent_status in (idle, done)?\n(interactive_ready ๋‹จ๋… ๋ถˆ์ธ์ •)"} + G3{"๋ฆฌํฌํŠธ ํ™•์ • ์™„๋ฃŒ?\n(๋””๋ฐ”์šด์Šค ๊ฒฝ๊ณผ ๋ฐ ๋งค๋‹ˆํŽ˜์ŠคํŠธ ํ™•์ธ)"} + G4{"Reviewer: ๋งˆ์ง€๋ง‰ ํ–‰ ์ •์‹ ํŒ์ • ํ† ํฐ ์กด์žฌ?"} + G5{"์™ธ๋ถ€ ๋ถ€์ˆ˜ํšจ๊ณผ ์ž‘์—… ์—ฌ๋ถ€ ํ™•์ธ\n(๋ถ€์ˆ˜ํšจ๊ณผ ์‹œ ๋ณ„๋„ ์ฆ๊ฑฐ ํ•„์š”)"} + end + + subgraph Engine["๋‹จ์ผ ์ƒํƒœ ์ œ์–ด ์—”์ง„ (Engine - ๋ฐฐํƒ€์  ์ „์ด)"] + E1["๊ณตํ†ต ์ƒํƒœ ์ „์ด ํŠธ๋žœ์žญ์…˜ (CAS Lock)"] + E2["Terminal State Commit & Pending Notification ๊ธฐ๋ก"] + E3["Lock ํ•ด์ œ"] + E4["์ •๊ทœ publish_event ๊ฒฝ๋กœ๋กœ ์ด๋ฒคํŠธ ๋ฐœํ–‰ (HMAC ์„œ๋ช…)"] + E5["๋‹ค์Œ ๋ฃจํ”„ ๋‹จ๊ณ„ ๋””์ŠคํŒจ์น˜"] + end + + W1 & W2 & W3 --> Gate + G1 & G2 & G3 & G4 & G5 -->|๋ชจ๋‘ ์ถฉ์กฑ (AND)| Engine + Gate -->|ํ•˜๋‚˜๋ผ๋„ ๋ฏธ์ถฉ์กฑ| Wait["๊ณ„์† ๋Œ€๊ธฐ (Wait / Fail-Closed)"] +``` + +--- + +## 4. 4๋‹จ๊ณ„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋กœ๋“œ๋งต (์„ธ๋ถ€ ๊ธฐ์ค€ ๋ณด์™„) + +| ๋‹จ๊ณ„ | ์ž‘์—… ๋ชฉํ‘œ | ์ƒ์„ธ ๊ตฌํ˜„ ๋‚ด์šฉ ๋ฐ ํ•„์ˆ˜ ์™„๋ฃŒ ๊ธฐ์ค€ (Gate) | +| :--- | :--- | :--- | +| **Phase 0** | **์‚ฌ์ „ ๊ณ„์•ฝ ๊ณ ์ • ๋ฐ Fixture ์ˆ˜์ง‘** | โ€ข CLI, ์ƒํƒœ ์ด๋ฒคํŠธ, ๋ฆฌํฌํŠธ ํ˜•์‹, ๋ฝ ๊ณ„์•ฝ ๊ธฐ์ค€์„  ๋ฌธ์„œํ™”
โ€ข Herdr 6๊ฐœ ์–ด๋Œ‘ํ„ฐ๋ณ„ ๋Ÿฐํƒ€์ž„ ์ƒํƒœ ์ „์ด fixture ์ˆ˜์ง‘
โ€ข ๋ฏธํ•ฉ์˜ ๊ธฐ์ˆ  ํ•ญ๋ชฉ(๋””๋ฐ”์šด์Šค vs ๋งค๋‹ˆํŽ˜์ŠคํŠธ)์˜ ๋‹จ๊ณ„๋ณ„ ์ฑ„ํƒ ๊ธฐ์ค€ ํ™•์ • | +| **Phase 1** | **`lib_py/delegation/` ๊ตฌ์ถ•** | โ€ข `lib.sh` L1833 ์œ„์ž„ ํ—ฌํผ๋ฅผ ํŒŒ์ด์ฌ ์„œ๋น„์Šค ํด๋ž˜์Šค๋กœ ์ถ”์ถœ
โ€ข `start_watchdog`์€ ์‹ค์ œ ์—ญํ• ์— ๋งž์ถฐ `ensure_subscriber_daemon`์œผ๋กœ ๋ช…๋ช…
โ€ข ์ธ๋ฉ”๋ชจ๋ฆฌ ๋‹จ์ผ MQTT ์—ฐ๊ฒฐ ์žฌ์‚ฌ์šฉ ํ”„๋กœํ† ํƒ€์ž… (๋ ˆ์ดํ„ด์‹œ๋Š” ๋ฒค์น˜๋งˆํฌ๋กœ ๋ณด๊ณ )
โ€ข ๊ธฐ์กด CLI/non-fatal/HMAC/seq/๋””์Šคํฌ ํด๋ฐฑ ํšŒ๊ท€ 100% ํ†ต๊ณผ
โ€ข **์ž๋™์™„๋ฃŒ ์ •์ฑ… ๋ณ€๊ฒฝ ์—†์Œ, ์„ธ์…˜ DB์™€ Job ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ €์žฅ์†Œ ํ†ตํ•ฉ ์‹œ๋„ ๊ธˆ์ง€** | +| **Phase 2a** | **`lib_py/loop/` ์ฝ”์–ด & Shadow Watcher** | โ€ข `engine.py` (๋‹จ์ผ ์ „์ด ์ฃผ์ฒด), `watcher.py` (`herdr agent wait` ๋ž˜ํ•‘) ๊ตฌํ˜„
โ€ข ์ž๋™์™„๋ฃŒ๋Š” ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ  "Would-Complete" ๋กœ๊น…๋งŒ ์ˆ˜ํ–‰ํ•˜๋Š” Shadow ๋ชจ๋“œ ๊ฐ€๋™
โ€ข ์†Œ์ผ“ ๋‹จ์ ˆ/์žฌ์—ฐ๊ฒฐ ๋ฐฑ์˜คํ”„, ๋ถ€๋ถ„ ๋ฆฌํฌํŠธ, ์Šน์ธ ๋Œ€๊ธฐ, ๋„๊ตฌ ์‹คํ–‰ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๊ฒ€์ฆ | +| **Phase 2b** | **์ด์ค‘ ๊ฐ์ง€ ์ž๋™์™„๋ฃŒ ํ™œ์„ฑํ™” (Flag-Gated)** | โ€ข 4์ค‘ ์‹คํ–‰ ๊ท€์† + ๋ฆฌํฌํŠธ ํ™•์ • ๊ณ„์•ฝ + ๋‹จ์ผ CAS ์ „์ด ๊ฒŒ์ดํŠธ ๊ฒ€์ฆ
โ€ข ๋ถ€์ˆ˜ํšจ๊ณผ ์ž‘์—… ์˜ˆ์™ธ ๊ทœ์ • ๋ฐ ๋ฌดํŒ์ • fail-closed ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์™„๋ฃŒ
โ€ข ๊ธฐ๋ณธ off ํ”Œ๋ž˜๊ทธ๋กœ ๋ฐฐํฌ ํ›„ ๊ฒ€์ฆ ์™„๋ฃŒ ์‹œ ๊ธฐ๋ณธ๊ฐ’ ํ™œ์„ฑํ™” | +| **Phase 3** | **`run_loop.sh` Thin Wrapper ์ „ํ™˜ ๋ฐ ํšŒ๊ท€** | โ€ข `run_loop.sh`๋ฅผ ์ธ์ž ์ „๋‹ฌ ๋ž˜ํผ(`exec python3 -m lib_py.loop "$@"`)๋กœ ์ „ํ™˜
โ€ข `--creator` ํ•„์ˆ˜, `--target-agent` ๊ฑฐ๋ถ€, bash 3.2, PYTHONPATH ๊ณ„์•ฝ ๋ณด์กด
โ€ข 5๊ฐœ subprocess ์ „์šฉ ํ…Œ์ŠคํŠธ(~1,621์ค„) ํฌํ•จ **ํ˜„ํ–‰ ์ „์ฒด 469๊ฐœ ํ…Œ์ŠคํŠธ 0-Fail**
โ€ข **์‹ ยท๊ตฌ ์—”์ง„์˜ ๋™์ผ ์ž‘์—…๊ณต๊ฐ„ ๋™์‹œ Writer ๋ณ‘ํ–‰ ๊ธˆ์ง€** | +| **Phase 4** | **๋ถˆ๋ณ€ ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ ๋ฐ B-13 ์ •๋ฆฌ** | โ€ข ๊ธฐ๋™ ์‹œ ์ „์ฒด ๋ชจ๋“ˆ ์ƒ์ฃผ(Eager Import) ๋ฐ ์ž์‹ ์ธํ„ฐํ”„๋ฆฌํ„ฐ/ํ…œํ”Œ๋ฆฟ ๋ถˆ๋ณ€์„ฑ ๊ฒ€์ฆ
โ€ข ์ž‘์—… ๋„์ค‘ ์Šคํ‚ฌ ํŒŒ์ผ ๊ต์ฒด ๋ฐ ๋Šฆ์€ ๊ฒฝ๋กœ(์žฌ์‹ฌ/์Šน๊ฒฉ/๋กค๋ฐฑ) ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ ํ†ต๊ณผ
โ€ข `/tmp/mam-loop-freeze.XXXXXX` ์…ธ ์ž„์‹œ ์šฐํšŒ์ฑ… ์™„์ „ ์ œ๊ฑฐ ๋ฐ ๋ฌธ์„œํ™” | + +--- + +## 5. ๋ช…์‹œ์  ๋น„๋ฒ”์œ„(Out of Scope) ๋ฐ ํ˜ธํ™˜์„ฑ ๊ณ„์•ฝ + +1. **์ €์žฅ์†Œ ํ†ตํ•ฉ ๊ธˆ์ง€**: ๋ณธ ๋ฆฌํŒฉํ† ๋ง์—์„œ TMUX ์„ธ์…˜ ๊ด€๋ฆฌ์šฉ SQLite/YAML ์ €์žฅ์†Œ์™€ ๋น„๋™๊ธฐ ์ž‘์—… ๊ด€๋ฆฌ์šฉ `.mam/jobs/` JSON ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ํ•˜๋‚˜๋กœ ํ†ตํ•ฉํ•˜๋ ค ์‹œ๋„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๋ชฉ์ ๊ณผ ์ˆ˜๋ช…์ด ๋‹ค๋ฆ„). +2. **์›์ƒท ์Šคํ‚ฌ์˜ Live Tree ์‚ฌ์šฉ ์œ ์ง€**: `multi-agent-mux-create`, `resume`, `stop` ๋“ฑ ๋‹จ๋ฐœ์„ฑ ์…ธ ์Šคํฌ๋ฆฝํŠธ๋Š” Freeze ๋ณต์‚ฌ๋ณธ์ด ์•„๋‹Œ ์‹ค์ œ ์ž‘์—… ํŠธ๋ฆฌ์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. +3. **ํƒ€์ž„์•„์›ƒ์˜ 3์› ๋ถ„๋ฆฌ ๋ณด์กด**: (a) ์ „์ฒด ๋ฒฝ์‹œ๊ณ„ ํƒ€์ž„์•„์›ƒ, (b) MQTT ๊ฐ€์ž…์ž idle-timeout, (c) TUI idle ์ž๋™์™„๋ฃŒ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์œ ์ง€ํ•˜๋ฉฐ, ์ž๋™์™„๋ฃŒ๊ฐ€ ๊ธฐ์กด ํƒ€์ž„์•„์›ƒ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋ฎ์–ด์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +--- + +## 6. ๊ฒฐ๋ก  + +2์ฐจ ๊ต์ฐจ ๊ฒ€ํ† ๋ฅผ ํ†ตํ•ด ๋ณธ ํ†ตํ•ฉ ๋ณด๊ณ ์„œ๋Š” ์„ธ ๋ฆฌ๋ทฐ์–ด์˜ ์‹ค์งˆ์  ๋ฐœ๊ฒฌ๊ณผ ๊ธฐ์ˆ ์  ์ฐจ์ด๋ฅผ ์ถฉ์‹คํžˆ ์ˆ˜๋ ดํ•˜์—ฌ ์™„์„ฑ๋„๋ฅผ ๋Œ€ํญ ๋Œ์–ด์˜ฌ๋ ธ์Šต๋‹ˆ๋‹ค. +๋ชจ๋“  ๋ฆฌ๋ทฐ์–ด๊ฐ€ ์ง€์ ํ•œ ๋Œ€๋กœ **Phase 0(์‚ฌ์ „ ๊ณ„์•ฝ ๋ฐ ์–ด๋Œ‘ํ„ฐ fixture ์ •๋ฆฌ)** ์„ ์ฆ‰์‹œ ์ˆ˜๋ฆฝํ•œ ํ›„, **Phase 1(`lib_py/delegation/` ๊ตฌ์ถ• ๋ฐ ์•ˆ์ „ํ•œ ์„œ๋น„์Šค ์ถ”์ถœ)** ์— ์ฐฉ์ˆ˜ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์•ˆ์ „ํ•˜๊ณ  ํ™•์‹คํ•œ ์ง„ํ–‰ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.