diff --git a/.agents/reports/creator-claude-01/plan-df85e978.md b/.agents/reports/creator-claude-01/plan-df85e978.md new file mode 100644 index 0000000..9835d9b --- /dev/null +++ b/.agents/reports/creator-claude-01/plan-df85e978.md @@ -0,0 +1,251 @@ +# ๐Ÿ“‘ Issue #5 Phase 2b ๊ตฌํ˜„ ๊ณ„ํš์„œ โ€” ์ด์ค‘ ๊ฐ์ง€ ์ž๋™์™„๋ฃŒ (Flag-Gated / Opt-In) + +- **Job**: `df85e978` / **Planner**: `creator-claude-01` +- **๊ธฐ์ค€ ์ปค๋ฐ‹**: `4a5916a` (branch `refactor`, working tree clean) / Python `3.9.6` / herdr `0.8.2` / **์ „์ฒด ํ…Œ์ŠคํŠธ 664** +- **์š”๊ตฌ ์ถœ์ฒ˜**: `implementation_plan.md` ยง2 Phase 2b ํ–‰, `task.md` P2B-01~P2B-03, `state-transitions.md` ยง3~ยง6 +- **๋ฌธ์„œ ์„ฑ๊ฒฉ**: Worker๊ฐ€ ๊ทธ๋Œ€๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ๊ณ„๋ณ„ ๋ช…์„ธ. ยง1์˜ ๋ชจ๋“  ์ธ์šฉ์€ ๋ผ์ด๋ธŒ ์ฝ”๋“œยท์‹คํ–‰์œผ๋กœ ์ง์ ‘ ํ™•์ธํ–ˆ๋‹ค. + +--- + +## 0. ์š”์•ฝ (TL;DR) + +1. **๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์“ฐ๋Š” ์ฃผ์ฒด๊ฐ€ ์ด Phase์˜ ์ „๋ถ€๋‹ค.** ์›Œ์ฒ˜๊ฐ€ ์Šค์Šค๋กœ ํ•ด์‹œ๋ฅผ ๊ณ„์‚ฐํ•ด ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๋ฉด ๊ทธ๊ฒƒ์€ "์›Œ์ฒ˜๊ฐ€ ์•ˆ์ •๋œ ํŒŒ์ผ์„ ๋ดค๋‹ค"๋Š” ์ž๊ธฐ ์ฆ๋ช…์ผ ๋ฟ์ด๋ฉฐ, Round 2์—์„œ Codex๊ฐ€ R1์œผ๋กœ ๊ธฐ๊ฐํ•œ ๋ฐ”๋กœ ๊ทธ ํ˜•ํƒœ๋‹ค. **๋๋‚ฌ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•„๋Š” ์ชฝ(์—์ด์ „ํŠธ)์ด ํ™•์ •์„ ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค**(ยงD1). +2. **๊ทธ๋ž˜์„œ ๋ฏธ์ง€์› ์—์ด์ „ํŠธ๋Š” ์ž๋™์œผ๋กœ fail-closed๊ฐ€ ๋œ๋‹ค.** ํ™•์ • ์„ ์–ธ์„ ํ•˜์ง€ ์•Š์œผ๋ฉด ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ์—†๊ณ , ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ์—†์œผ๋ฉด `policy`๊ฐ€ ์ด๋ฏธ `hold`ํ•œ๋‹ค(Phase 2a์—์„œ ๊ตฌํ˜„ยทํ…Œ์ŠคํŠธ ์™„๋ฃŒ). P2B-03์˜ "๋ฏธ์ง€์› ๊ฒฝ๋กœ 100% ๋ณด์กด"์€ ์ƒˆ ๋ถ„๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ **๊ธฐ์กด ๋™์ž‘์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ๊ท€๊ฒฐ**์ด๋‹ค. +3. **`attempt_id`์™€ `iteration`์˜ ์˜๋ฏธ ์ถฉ๋Œ์„ ๋จผ์ € ์ •๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.** ์—”์ง„์€ `record.get("attempt_id", record.get("iteration"))`๋กœ ๋Œ€์ฒดํ•˜๋Š”๋ฐ, ๋ ˆ์ฝ”๋“œ์—๋Š” `attempt_id`๊ฐ€ **์—†๊ณ ** `iteration`๋งŒ ์žˆ๋‹ค(์‹ค์ธก). ์ง€๊ธˆ์€ ์šฐ์—ฐํžˆ ๊ฐ’์ด ๊ฒน์ณ ๋™์ž‘ํ•˜์ง€๋งŒ ๋‘ ๊ฐœ๋…์€ ๋‹ค๋ฅด๋ฉฐ, ์ž๋™์™„๋ฃŒ ๊ฒŒ์ดํŠธ๊ฐ€ ์ด ๊ฐ’์— ๊ฑธ๋ฆฌ๋ฏ€๋กœ **๋ช…์‹œ์  ๊ฒฐ์ • ์—†์ด ์ง„ํ–‰ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค**(ยงD2). +4. **์ง€์—ฐ ์•Œ๋ฆผ์€ ์ƒˆ ์ €์žฅ์†Œ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.** `state-transitions.md` ยง5๊ฐ€ "๋…๋ฆฝ Outbox DB ์‹ ์„ค ๊ธˆ์ง€"๋ฅผ ๋ช…์‹œํ–ˆ๊ณ , ๋ ˆ์ฝ”๋“œ ์•ˆ `pending_notification` ํ•„๋“œ๋ฅผ ๊ฐ™์€ CAS ํŠธ๋žœ์žญ์…˜์—์„œ ์“ด๋‹ค(ยงD4). +5. **์ž๋™์™„๋ฃŒ ํ—ˆ์šฉ ๋ฐฑ์—”๋“œ๋Š” ์‹ค์ธก ์ฆ๊ฑฐ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ํ•œ์ •ํ•œ๋‹ค.** Phase 0 ๋Šฅ๋ ฅ ํ–‰๋ ฌ ์‹ค์ธก: ์ •์ง€ ์ƒํƒœ(`idle`/`done`) ๊ด€์ธก ์ฆ๊ฑฐ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์€ **claudeยทgrok(idle), codex(done)** ๋ฟ์ด๊ณ  **agyยทhermesยทopencode๋Š” ์—†๋‹ค**(ยง1-5). ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ผœ์ ธ๋„ ์ด ์…‹ ์™ธ์—๋Š” ์ž๋™์™„๋ฃŒํ•˜์ง€ ์•Š๋Š”๋‹ค. +6. **664๊ฐœ ๋ฌดํšŒ๊ท€ + ์‹ ๊ทœ.** Phase 2a์™€ ๋™์ผํ•˜๊ฒŒ ํŠน์„ฑํ™” ์„ ํ–‰ โ†’ ๊ตฌํ˜„ โ†’ ๋ฌธ์„œ ์ˆœ์œผ๋กœ ์ปค๋ฐ‹ 5๊ฐœ. + +--- + +## 1. Planner ์‚ฌ์ „ ์‹ค์ธก + +### 1-1. ์ž๋™์™„๋ฃŒ์— ํ•„์š”ํ•œ ๊ฒƒ ์ค‘ **์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ** + +| ํ•„์š” | ํ˜„ํ™ฉ (์‹ค์ธก) | +|---|---| +| ์™„๋ฃŒ ๋งค๋‹ˆํŽ˜์ŠคํŠธ | **์—†์Œ.** ์ €์žฅ์†Œ ์–ด๋””์—๋„ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์“ฐ๊ฑฐ๋‚˜ ์ฝ๋Š” ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ๊ฐ€ 0๊ฑด. ๋ฆฌํฌํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ์—๋Š” `report-final.md` ํ•˜๋‚˜๋ฟ | +| ๋ฆฌํฌํŠธ ์›์ž์  ํ™•์ • | **์—†์Œ.** ์—์ด์ „ํŠธ๊ฐ€ `report-final.md`๋ฅผ ์ง์ ‘ ์“ด๋‹ค. tmp+rename ์—†์Œ | +| `attempt_id` ๋ ˆ์ฝ”๋“œ ํ•„๋“œ | **์—†์Œ.** ๋ ˆ์ฝ”๋“œ์—๋Š” `iteration`๋งŒ ์กด์žฌ | +| `pending_notification` | **์—†์Œ.** `grep` 0๊ฑด | +| `MAM_AUTO_COMPLETE` | **์—†์Œ.** `grep` 0๊ฑด (์ฐธ๊ณ : `MAM_ENGINE_CAS` 5๊ฑด, `MAM_DELEGATION_PY` 4๊ฑด์€ ์กด์žฌ) | + +### 1-2. ์ด๋ฏธ ์ค€๋น„๋˜์–ด ์žˆ๋Š” ๊ฒƒ (Phase 2a ์‚ฐ์ถœ๋ฌผ) + +| ์žˆ์Œ | ์œ„์น˜ | +|---|---| +| ๋‹จ์ผ CAS ์ „์ด ๊ฒฝ๊ณ„ + ๊ถŒํ•œ ์ถ• | `lib_py/delegation/engine.py::transition` | +| `expected_attempt` fail-closed | ๋ ˆ์ฝ”๋“œ๊ฐ€ attempt๋ฅผ ๋ชป ๋Œ€๋ฉด ๊ฑฐ๋ถ€ (Round 1 R2) | +| ๊ด€์ธก ์ฆ๊ฑฐ **ํ•„์ˆ˜** ํŒ์ • | `lib_py/loop/policy.py` โ€” 4์‹๋ณ„์ž + ๋งค๋‹ˆํŽ˜์ŠคํŠธ `attempt_id` ๋ถ€์žฌ ์‹œ `hold` (Round 2 R1) | +| ๋งค๋‹ˆํŽ˜์ŠคํŠธ ๋ฏธํ™•์ • ์‹œ `hold` | `policy.decide`์˜ `manifest_committed`/`hash_verified` ๊ฒŒ์ดํŠธ | +| `herdr agent wait` ๋ž˜ํ•‘ยท๋ฐฑ์˜คํ”„ | `lib_py/loop/watcher.py` | +| ์“ฐ๊ธฐ ๊ฒฝ๋กœ ์—†๋Š” shadow ๋Ÿฌ๋„ˆ | `lib_py/loop/shadow.py` | +| ์ง€์—ฐ ์•Œ๋ฆผ์˜ ์ˆœ์„œ ๋ณด์žฅ ๊ธฐ๋ฐ˜ | `transition_seq` + per-job ๊ฐ์‚ฌ ๋ฝ (Round 2 R4) | + +**์ฆ‰ Phase 2b๋Š” ํŒ์ •๊ธฐ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ด๋ฏธ fail-closed๋กœ ํŒ์ •ํ•˜๊ณ  ์žˆ๋Š” ๊ฒŒ์ดํŠธ์— "์ง„์งœ ์ฆ๊ฑฐ"๋ฅผ ๊ณต๊ธ‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ปค๋ฐ‹์— ์—ฐ๊ฒฐํ•˜๋Š” ์ž‘์—…์ด๋‹ค.** + +### 1-3. `attempt_id` vs `iteration` โ€” ์‹คํ–‰์œผ๋กœ ํ™•์ธํ•œ ์ถฉ๋Œ + +``` +record keys with attempt semantics: {'iteration': 1} +transition(completed, expected_attempt=1) -> committed=True running -> completed +transition(completed, expected_attempt=2) -> committed=False stale attempt: expected 2, record has 1 +``` +์—”์ง„์€ `attempt_id`๊ฐ€ ์—†์œผ๋ฉด `iteration`์„ ์“ด๋‹ค. ๋ž˜ํผ์˜ loop ํ๋ฆ„์—์„œ๋Š” `redispatch`๊ฐ€ `iteration`์„ ์ฆ๊ฐ€์‹œํ‚ค๋ฏ€๋กœ ๋‘ ๊ฐ’์ด ์šฐ์—ฐํžˆ ์ผ์น˜ํ•˜์ง€๋งŒ, `iteration`์€ **๋ฃจํ”„ ํšŒ์ฐจ**์ด๊ณ  `attempt_id`๋Š” **๋””์ŠคํŒจ์น˜ ์‹œ๋„**๋‹ค. ์ง์ ‘(`--type direct`) ์žฌ๋””์ŠคํŒจ์น˜๋‚˜ ํฌ๋ž˜์‹œ ํ›„ ์žฌ์‹œ๋„์—์„œ ๊ฐˆ๋ผ์ง„๋‹ค. ์ž๋™์™„๋ฃŒ ๊ฒŒ์ดํŠธ๊ฐ€ ์ด ๊ฐ’์— ๊ฑธ๋ฆฌ๋ฏ€๋กœ ยงD2์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ๊ฒฐ์ •ํ•œ๋‹ค. + +### 1-4. ๋ฏธ์ง€์› ๊ฒฝ๋กœ ๋ณด์กด์˜ ์‹ค์ œ ๋ฉ”์ปค๋‹ˆ์ฆ˜ + +`job_subscriber._check_disk_fallback`(`:60-72`)์€ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ status๋ฅผ ์ฝ์–ด terminal์ด๋ฉด ์ฆ‰์‹œ ์ข…๋ฃŒํ•œ๋‹ค. ์ฆ‰ **์ž๋™์™„๋ฃŒ๊ฐ€ `completed`๋ฅผ ์“ฐ๋ฉด ๋ž˜ํผ๋Š” ๋ช…์‹œ ์ด๋ฒคํŠธ์™€ ๊ตฌ๋ณ„ํ•˜์ง€ ๋ชปํ•œ๋‹ค.** ์ด๋Š” ์˜๋„๋œ ๊ฒฐํ•ฉ์ด์ง€๋งŒ, ๋™์‹œ์— "์ž๋™์™„๋ฃŒ๊ฐ€ ํ‹€๋ฆฌ๋ฉด ๋ž˜ํผ๊ฐ€ ์กฐ์šฉํžˆ ์ž˜๋ชป๋œ ์™„๋ฃŒ๋ฅผ ๋ฐ›๋Š”๋‹ค"๋Š” ๋œป์ด๋ฏ€๋กœ ยงD6์˜ ๊ฐ์‚ฌ ํ‘œ์‹์ด ํ•„์ˆ˜๋‹ค. + +### 1-5. ์ž๋™์™„๋ฃŒ ํ—ˆ์šฉ ๋ฐฑ์—”๋“œ (Phase 0 ๋Šฅ๋ ฅ ํ–‰๋ ฌ ์‹ค์ธก) + +``` +claude observed slots: ['idle'] +agy observed slots: ['working', 'wait_timeout'] +hermes observed slots: NONE +grok observed slots: ['idle'] +opencode observed slots: NONE +codex observed slots: ['done'] +``` +์ž๋™์™„๋ฃŒ๋Š” **์ •์ง€ ์ƒํƒœ ๊ด€์ธก**์„ ์ „์ œ๋กœ ํ•œ๋‹ค. `idle`/`done` ์ฆ๊ฑฐ๊ฐ€ ์žˆ๋Š” **claudeยทgrokยทcodex**๋งŒ ํ›„๋ณด์ด๋ฉฐ, `agy`๋Š” `working`๋งŒ ๊ด€์ธก๋ผ ์ •์ง€ ํŒ์ •์˜ ์‹ค์ธก ๊ทผ๊ฑฐ๊ฐ€ ์—†๊ณ , `hermes`ยท`opencode`๋Š” ๊ด€์ธก 0๊ฑด์ด๋‹ค. + +### 1-6. ์—์ด์ „ํŠธ ์ง€์‹œ๋ฌธ ์ฃผ์ž… ์ง€์  (์กฐ์ • ๋น„์šฉ) + +`create_session.sh:483`๊ณผ ๋ž˜ํผ `:202`/`:319`๊ฐ€ `publish_event.py --job `๋ฅผ ์ง€์‹œ๋ฌธ์— ๋„ฃ๋Š”๋‹ค. ํ™•์ • ์„ ์–ธ ๋ช…๋ น์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด **์ด ๋‘ ๊ณณ๋งŒ** ๋ฐ”๊พธ๋ฉด ๋œ๋‹ค. + +--- + +## 2. ์„ค๊ณ„ ๊ฒฐ์ • + +### D1. ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋Š” **๋๋‚œ ์ชฝ์ด ์„ ์–ธํ•œ๋‹ค** (P2B-01์˜ ํ•ต์‹ฌ) + +``` +์—์ด์ „ํŠธ: ์ž‘์—… ์ข…๋ฃŒ ์‹œ + publish_event.py --job --finalize-report [--side-effect =] ... + โ‘  .tmp ๋ฅผ ๋กœ ์›์ž์  rename (์ด๋ฏธ ์ตœ์ข…๋ณธ์ด๋ฉด rename ์ƒ๋žต) + โ‘ก SHA-256 ๊ณ„์‚ฐ + โ‘ข /report-manifest.json ์›์ž์  ๊ธฐ๋ก: + { schema_version, job_id, attempt_id, report_path, sha256, + finalized_at, side_effects: [...], declared_by } +``` + +- **์›Œ์ฒ˜๋Š” ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.** ๋งŒ๋“ค๋ฉด ์ž๊ธฐ ์ฆ๋ช…์ด ๋˜์–ด Round 2 R1์—์„œ ๊ธฐ๊ฐ๋œ ํ˜•ํƒœ๊ฐ€ ๋œ๋‹ค. ์›Œ์ฒ˜๋Š” **์ฝ๊ณ  ๊ฒ€์ฆ๋งŒ** ํ•œ๋‹ค(ํ•ด์‹œ ์žฌ๊ณ„์‚ฐ ํ›„ ๋Œ€์กฐ). +- **๋ฏธ์ง€์› ์—์ด์ „ํŠธ๋Š” ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š์œผ๋ฉด ๋œ๋‹ค** โ€” ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ์—†์œผ๋ฉด `policy`๊ฐ€ ์ด๋ฏธ `hold`ํ•œ๋‹ค. P2B-03์˜ "explicit event ๊ฒฝ๋กœ 100% ๋ณด์กด"์ด **์ƒˆ ์ฝ”๋“œ ์—†์ด** ์„ฑ๋ฆฝํ•œ๋‹ค. +- ๋ฆฌํฌํŠธ๊ฐ€ ์žฌ์ž‘์„ฑ๋˜๋ฉด ํ•ด์‹œ๊ฐ€ ์–ด๊ธ‹๋‚˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ์ฆ‰์‹œ ๋ฌดํšจ๊ฐ€ ๋œ๋‹ค(ยง4 ๊ณ„์•ฝ ๊ทธ๋Œ€๋กœ). + +### D2. `attempt_id`๋ฅผ ๋ ˆ์ฝ”๋“œ์˜ 1๊ธ‰ ํ•„๋“œ๋กœ ์Šน๊ฒฉ (๊ฒฐ์ • ํ•„์š” ์‚ฌํ•ญ) + +**๊ถŒ๊ณ : ์Šน๊ฒฉํ•œ๋‹ค.** ๊ทผ๊ฑฐ: +- ์—”์ง„์˜ `expected_attempt`๋Š” ์ด๋ฏธ "๋ ˆ์ฝ”๋“œ๊ฐ€ attempt๋ฅผ ๋Œ€์ง€ ๋ชปํ•˜๋ฉด ๊ฑฐ๋ถ€"๋กœ fail-closed๋‹ค(Round 1 R2). ์ž๋™์™„๋ฃŒ๊ฐ€ ์ด ๊ฒŒ์ดํŠธ๋ฅผ ํ†ต๊ณผํ•˜๋ ค๋ฉด ๋ ˆ์ฝ”๋“œ๊ฐ€ attempt๋ฅผ ๋Œ€์•ผ ํ•œ๋‹ค. +- `iteration` ๋Œ€์ฒด๋Š” ์šฐ์—ฐํ•œ ์ผ์น˜์— ์˜์กดํ•œ๋‹ค. ๋‘ ๊ฐœ๋…์ด ๊ฐˆ๋ผ์ง€๋Š” ์ˆœ๊ฐ„ **CAS ๊ฐ€๋“œ๊ฐ€ ์กฐ์šฉํžˆ ๋ฌด๋ ฅํ™”**๋œ๋‹ค. + +๊ตฌํ˜„: +- `register_job`์ด `attempt_id: 1`์„ ๊ธฐ๋ก. `engine`์˜ `redispatch`๊ฐ€ `iteration`๊ณผ ํ•จ๊ป˜ `attempt_id`๋ฅผ ์ฆ๊ฐ€. +- ์—”์ง„์˜ `record.get("attempt_id", record.get("iteration"))` ๋Œ€์ฒด๋ฅผ **์ œ๊ฑฐ**ํ•˜๊ณ  `attempt_id`๋งŒ ๋ณธ๋‹ค(๋Œ€์ฒด๋Š” ์˜๋ฏธ ํ˜ผ๋™์˜ ์›์ฒœ). +- **์Šคํ‚ค๋งˆ ์˜ํ–ฅ**: ํ•„๋“œ ์ถ”๊ฐ€์ด๋ฏ€๋กœ ๊ธฐ์กด ๋ฆฌ๋”๋Š” ๋ฌด์‹œํ•˜๋ฉด ๋˜๊ณ  `SCHEMA_VERSION`์€ 1์„ ์œ ์ง€ํ•œ๋‹ค. ๋‹ค๋งŒ Phase 1์ด "๋ ˆ์ฝ”๋“œ ํ•„๋“œ ๋ถˆ๋ณ€"์„ ๊ณ„์•ฝ์œผ๋กœ ๋ช…์‹œํ–ˆ์œผ๋ฏ€๋กœ **์ด ๊ณ„ํš์„œ๊ฐ€ ๊ทธ ๊ณ„์•ฝ์˜ ๋ณ€๊ฒฝ์„ ๋ช…์‹œ์ ์œผ๋กœ ์š”์ฒญ**ํ•œ๋‹ค. ๋ฆฌ๋ทฐ์–ด ์Šน์ธ ์—†์ด ์ง„ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. +- ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜: `attempt_id`๊ฐ€ ์—†๋Š” ๊ธฐ์กด ๋ ˆ์ฝ”๋“œ๋Š” `iteration` ๊ฐ’์œผ๋กœ 1ํšŒ ์ฑ„์šด๋‹ค(์ฝ๊ธฐ ์‹œ lazy, ์“ฐ๊ธฐ๋Š” ๋‹ค์Œ ์ „์ด์—์„œ). + +### D3. ์ž๋™์™„๋ฃŒ ๊ฒŒ์ดํŠธ (๊ธฐ์กด ํŒ์ •๊ธฐ์— ์ฆ๊ฑฐ๋ฅผ ๊ณต๊ธ‰) + +`shadow.ShadowRunner`๋ฅผ `AutoCompleteRunner`๋กœ ํ™•์žฅํ•˜๋˜ **ํŒ์ • ๋กœ์ง์€ ์†๋Œ€์ง€ ์•Š๋Š”๋‹ค**. ์ˆœ์„œ: + +1. `watcher.wait_once`๋กœ ์ •์ง€ ์ƒํƒœ ๊ด€์ธก โ†’ `collector_attribution` ๋ด‰ํˆฌ์— 4์‹๋ณ„์ž๋ฅผ ์ฑ„์›Œ ์ฆ๊ฑฐ ๊ตฌ์„ฑ. +2. `report-manifest.json` ์ฝ๊ธฐ โ†’ ํ•ด์‹œ **์žฌ๊ณ„์‚ฐ ํ›„ ๋Œ€์กฐ**(๋งค๋‹ˆํŽ˜์ŠคํŠธ ์ž๊ธฐ ์‹ ๊ณ ๋ฅผ ์‹ ๋ขฐํ•˜์ง€ ์•Š๋Š”๋‹ค) โ†’ `report_manifest.attempt_id`๋ฅผ ์ฆ๊ฑฐ์— ํฌํ•จ. +3. `policy.decide` โ†’ `candidate_approved`๊ฐ€ ์•„๋‹ˆ๋ฉด ์ข…๋ฃŒ(๋กœ๊ทธ๋งŒ). +4. `candidate_approved`๋ฉด **๊ฒŒ์ดํŠธ 3์ค‘ ํ™•์ธ ํ›„** `engine.transition(job_id, "completed", expected_attempt=, source="auto_complete")`. + - ๊ฒŒ์ดํŠธ: ํ”Œ๋ž˜๊ทธ ON + ๋ฐฑ์—”๋“œ ํ—ˆ์šฉ๋ชฉ๋ก(ยง1-5) + ์žก๋ณ„ opt-in. +5. ์ปค๋ฐ‹ ๊ฒฐ๊ณผ๊ฐ€ `committed=False`๋ฉด **์•„๋ฌด ์ผ๋„ ์—†์—ˆ๋˜ ๊ฒƒ์œผ๋กœ ์ฒ˜๋ฆฌ**ํ•˜๊ณ  ๊ฐ์‚ฌ์— ๊ธฐ๋ก(๋Šฆ์€ conflict๋Š” ์—”์ง„์ด ์ด๋ฏธ ๊ฑฐ๋ถ€ํ•œ๋‹ค โ€” ์ƒˆ ์ฝ”๋“œ ๋ถˆํ•„์š”). + +### D4. ์ง€์—ฐ ์•Œ๋ฆผ (P2B-02) โ€” ๋ ˆ์ฝ”๋“œ ์•ˆ outbox + +`state-transitions.md` ยง5๊ฐ€ **๋…๋ฆฝ Outbox DB ์‹ ์„ค์„ ๊ธˆ์ง€**ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ: + +- `engine.transition`์ด ์ปค๋ฐ‹ํ•  ๋•Œ ๊ฐ™์€ ๋ฝ ์•ˆ์—์„œ `pending_notification: {event, seq, timestamp, signature}`๋ฅผ ๋ ˆ์ฝ”๋“œ์— **ํ•จ๊ป˜** ๊ธฐ๋กํ•œ๋‹ค(์ถ”๊ฐ€ I/O ์—†์Œ โ€” ์ด๋ฏธ ์“ฐ๋Š” ๋ ˆ์ฝ”๋“œ์— ํ•„๋“œ ํ•˜๋‚˜). +- ๋ฝ ํ•ด์ œ ํ›„ `notifier.flush(job_id)`๊ฐ€ MQTT ๋ฐœํ–‰ โ†’ ์„ฑ๊ณต ์‹œ `pending_notification`์„ ์ง€์šฐ๋Š” **๋ณ„๋„ CAS ์ „์ด**(`request="notification_settled"`, ์ƒํƒœ ๋ถˆ๋ณ€). +- **์žฌ์‹œ์ž‘ ๋ณต๊ตฌ**: ๊ธฐ๋™ ์‹œ `pending_notification`์ด ๋‚จ์€ ๋ ˆ์ฝ”๋“œ๋ฅผ ์Šค์บ”ํ•ด ์žฌ๋ฐœํ–‰. ๋ฉฑ๋“ฑํ•˜๋‹ค โ€” ๊ตฌ๋…์ž๊ฐ€ `seq`๋กœ ์ค‘๋ณต์„ ๊ฑฐ๋ฅธ๋‹ค(๊ธฐ์กด replay ๋ฐฉ์–ด). +- **๋ฝ ๊ทœ์œจ ์œ ์ง€**: ๋ฐœํ–‰์€ ์ ˆ๋Œ€ ๋ฝ ์•ˆ์—์„œ ํ•˜์ง€ ์•Š๋Š”๋‹ค(Phase 2a G5 ํ…Œ์ŠคํŠธ๊ฐ€ ๊ณ„์† ๊ฐ์‹œ). + +### D5. ๋ถ€์ˆ˜ํšจ๊ณผ ์ฆ๊ฑฐ (P2B-01 ํ›„๋ฐ˜) + +- ๋ ˆ์ฝ”๋“œ์˜ ๊ธฐ์กด `expected_artifacts`(์ด๋ฏธ ์กด์žฌ, `--artifact`๋กœ ์„ค์ •)๋ฅผ **์„ ์–ธ๋œ ๋ถ€์ˆ˜ํšจ๊ณผ ๋ชฉ๋ก**์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ์ƒˆ ํ•„๋“œ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. +- `expected_artifacts`๊ฐ€ ๋น„์–ด ์žˆ์ง€ ์•Š์€ job์€ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์˜ `side_effects`๊ฐ€ **๋ชจ๋“  ํ•ญ๋ชฉ์„ ๋ฎ์–ด์•ผ** ์ž๋™์™„๋ฃŒ ํ›„๋ณด๊ฐ€ ๋œ๋‹ค. ํ•˜๋‚˜๋ผ๋„ ์ฆ๊ฑฐ๊ฐ€ ์—†์œผ๋ฉด `hold`. +- ์ฆ๊ฑฐ ํ˜•์‹์€ `{kind, reference, verified_at}`์ด๋ฉฐ, Phase 2b๋Š” **์กด์žฌ์™€ ๋Œ€์‘๋งŒ** ๊ฒ€์ฆํ•œ๋‹ค(์›๊ฒฉ ํ™•์ธ์€ ๋น„๋ฒ”์œ„ โ€” ์ •์งํ•˜๊ฒŒ ๋ช…์‹œ). + +### D6. ํ”Œ๋ž˜๊ทธ์™€ ๊ฐ์‚ฌ ํ‘œ์‹ (P2B-03) + +| ์Šค์œ„์น˜ | ๊ธฐ๋ณธ | ๋ฒ”์œ„ | +|---|---|---| +| `MAM_AUTO_COMPLETE` | **`0` (OFF)** | ์ „์—ญ ํ‚ฌ ์Šค์œ„์น˜. `1`์ด์–ด์•ผ ์ž๋™์™„๋ฃŒ๊ฐ€ ๊ฐ€๋Šฅ | +| ๋ฐฑ์—”๋“œ ํ—ˆ์šฉ๋ชฉ๋ก | `claude,grok,codex` | ยง1-5 ์‹ค์ธก ๊ทผ๊ฑฐ. `MAM_AUTO_COMPLETE_BACKENDS`๋กœ ์ขํž ์ˆ˜๋งŒ ์žˆ๊ฒŒ(๋„“ํžˆ๋ ค๋ฉด ์ฆ๊ฑฐ ํ•„์š”) | +| ์žก๋ณ„ opt-in | ๋ ˆ์ฝ”๋“œ `auto_complete: true` | ์…‹ ๋‹ค ์ฐธ์ผ ๋•Œ๋งŒ ์ปค๋ฐ‹ | + +**๊ฐ์‚ฌ ํ‘œ์‹ ํ•„์ˆ˜**: ์ž๋™์™„๋ฃŒ๋กœ ๋“ค์–ด๊ฐ„ terminal์€ `completed_by: "auto_complete"`์™€ ํŒ์ • ๊ทผ๊ฑฐ(๋งค๋‹ˆํŽ˜์ŠคํŠธ ํ•ด์‹œ, ๊ด€์ธก seq)๋ฅผ ๊ฐ์‚ฌ ๋กœ๊ทธ์— ๋‚จ๊ธด๋‹ค. ยง1-4์—์„œ ํ™•์ธํ–ˆ๋“ฏ ๋ž˜ํผ๋Š” ์ž๋™/๋ช…์‹œ๋ฅผ ๊ตฌ๋ณ„ํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ, **์‚ฌํ›„์— ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ์ˆ˜๋‹จ**์ด๋‹ค. + +### D7. ๋ฌดํŒ์ • fail-closed + +์ •์ฑ…์ด ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ฑฐ๋‚˜ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฒฐ์ •์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด `hold`๋กœ ์ทจ๊ธ‰ํ•œ๋‹ค. ์ž๋™์™„๋ฃŒ ๊ฒฝ๋กœ ์ „์ฒด๋ฅผ `try/except`๋กœ ๊ฐ์‹ธ **์–ด๋–ค ์‹คํŒจ๋„ ์ปค๋ฐ‹์œผ๋กœ ์ด์–ด์ง€์ง€ ์•Š๊ฒŒ** ํ•œ๋‹ค. "ํŒ์ • ๋ชป ํ•จ"์€ ์ ˆ๋Œ€ "์™„๋ฃŒ"๊ฐ€ ์•„๋‹ˆ๋‹ค. + +### D8. ํŠธ๋ฆฌยท๊ถŒํ•œ ๊ทœ์น™ ์Šน๊ณ„ + +- `lib_py.loop`๋Š” `lib_py.delegation` ์™ธ ํ˜•์ œ ์ž„ํฌํŠธ ๊ธˆ์ง€(Phase 2a G9). +- ์ž๋™์™„๋ฃŒ์˜ `source`๋Š” **`auto_complete`** ์ด๋ฉฐ ์šด์˜์ž ๊ถŒํ•œ์ด **์•„๋‹ˆ๋‹ค** โ€” terminal์„ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋‹ค. +- `preexec_fn`: ์•Œ๋ฆผ ์žฌ๋ฐœํ–‰๊ธฐ๊ฐ€ ์Šค๋ ˆ๋“œ๋ฅผ ์“ฐ๋ฉด Phase 1 ์ธ๊ณ„ ๊ฒŒ์ดํŠธ์— ๊ฑธ๋ฆฐ๋‹ค. **2b๋Š” ๋ฐ๋ชฌ์„ ์Šคํฐํ•˜์ง€ ์•Š๊ณ ** ๊ธฐ์กด ๊ตฌ๋…์ž ํ”„๋กœ์„ธ์Šค ์•ˆ์—์„œ flushํ•œ๋‹ค. ์Šคํฐ์ด ํ•„์š”ํ•ด์ง€๋ฉด ๊ทธ๋•Œ `preexec_fn`์„ ๊ต์ฒดํ•œ๋‹ค. + +--- + +## 3. ์ž‘์—… ๋ถ„ํ•ด (WBS) โ€” ์ปค๋ฐ‹ 5๊ฐœ + +### P2B-00 ํŠน์„ฑํ™” ์„ ํ–‰ (์ปค๋ฐ‹ 1: `test(issue-5): phase 2b pre-conditions and fail-closed characterization`) +- ํ˜„ํ–‰ ๊ณ ์ •: ๋งค๋‹ˆํŽ˜์ŠคํŠธ ์—†๋Š” job์€ `hold`(์ด๋ฏธ ์ฐธ), `MAM_AUTO_COMPLETE` ๋ถ€์žฌ ์‹œ ์ž๋™์™„๋ฃŒ 0๊ฑด, `attempt_id` ๋ถ€์žฌ ์‹œ `expected_attempt`๊ฐ€ ๊ฑฐ๋ถ€, ๋ฏธ์ง€์› ๋ฐฑ์—”๋“œ ๋ชฉ๋ก. +- **์™„๋ฃŒ ๊ธฐ์ค€**: ๋ ˆ๊ฑฐ์‹œ์—์„œ ์ „๋ถ€ ํ†ต๊ณผ, 664 + ์‹ ๊ทœ ํ†ต๊ณผ. + +### P2B-01 ํ™•์ • ๊ณ„์•ฝ (์ปค๋ฐ‹ 2: `feat(issue-5): report finalization manifest and side-effect evidence`) +- **ํŒŒ์ผ**: `lib_py/loop/finalize.py`(๋งค๋‹ˆํŽ˜์ŠคํŠธ ์ž‘์„ฑยท๊ฒ€์ฆ), `publish_event.py`์— `--finalize-report`/`--side-effect`, `create_session.sh:483`ยท๋ž˜ํผ `:202/:319` ์ง€์‹œ๋ฌธ 1์ค„ ์ถ”๊ฐ€. +- **ํ…Œ์ŠคํŠธ**: tmpโ†’rename ์›์ž์„ฑ, ํ•ด์‹œ ์žฌ๊ณ„์‚ฐ ๋Œ€์กฐ(์œ„์กฐ ๋งค๋‹ˆํŽ˜์ŠคํŠธ ๊ฑฐ๋ถ€), ๋ฆฌํฌํŠธ ์žฌ์ž‘์„ฑ ์‹œ ๋ฌดํšจํ™”, `expected_artifacts` ๋ฏธ์ถฉ์กฑ ์‹œ `hold`, ๋งค๋‹ˆํŽ˜์ŠคํŠธ ๋ถ€์žฌ ์‹œ `hold`(๋ถˆ๋ณ€). + +### P2B-02 CAS ์—ฐ๋™ + ์ง€์—ฐ ์•Œ๋ฆผ (์ปค๋ฐ‹ 3: `feat(issue-5): auto-complete gate and durable pending notification`) +- **ํŒŒ์ผ**: `lib_py/loop/autocomplete.py`, `lib_py/delegation/notifier.py`, `engine`์— `pending_notification` ๊ธฐ๋ก ๋ฐ `notification_settled` ์š”์ฒญ. +- **ํ…Œ์ŠคํŠธ**: ๊ฒŒ์ดํŠธ 3์ค‘ ํ™•์ธ, ๋Šฆ์€ conflict ๊ฑฐ๋ถ€(์—”์ง„ ๊ธฐ์กด ๋™์ž‘), ํฌ๋ž˜์‹œ ํ›„ ์žฌ์‹œ์ž‘ ์žฌ๋ฐœํ–‰, ์ค‘๋ณต ๋ฐœํ–‰ ๋ฉฑ๋“ฑ, **๋ฝ ์•ˆ ๋ฐœํ–‰ 0๊ฑด**(Phase 2a G5 ์žฌ์‚ฌ์šฉ), `attempt_id` ๋ถˆ์ผ์น˜ ์‹œ ๊ฑฐ๋ถ€. + +### P2B-03 ํ”Œ๋ž˜๊ทธยทํ—ˆ์šฉ๋ชฉ๋ก (์ปค๋ฐ‹ 4: `feat(issue-5): default-off auto-complete flag with an evidence-backed backend allowlist`) +- **ํŒŒ์ผ**: ํ”Œ๋ž˜๊ทธ 3์ข…, `completed_by` ๊ฐ์‚ฌ ํ‘œ์‹. +- **ํ…Œ์ŠคํŠธ**: ๊ธฐ๋ณธ OFF์—์„œ ์ž๋™์™„๋ฃŒ 0๊ฑด์ด๋ฉฐ ๋ช…์‹œ ๊ฒฝ๋กœ ๊ฒฐ๊ณผ๊ฐ€ **๋ฐ”์ดํŠธ ๋™์ผ**, ํ—ˆ์šฉ๋ชฉ๋ก ๋ฐ– ๋ฐฑ์—”๋“œ๋Š” ํ”Œ๋ž˜๊ทธ ON์—์„œ๋„ ๋ฏธ์ปค๋ฐ‹, ์žก๋ณ„ opt-in ๋ฏธ์„ค์ • ์‹œ ๋ฏธ์ปค๋ฐ‹, ์ž๋™์™„๋ฃŒ terminal์— `completed_by` ์กด์žฌ. + +### P2B-04 ์‹œ๋‚˜๋ฆฌ์˜คยท๋ฌธ์„œ (์ปค๋ฐ‹ 5: `docs(issue-5): phase 2b contracts, scenarios and task checklist`) +- ๊ฒฝ์Ÿ/์žฌ์‹œ์ž‘/๋ถ€๋ถ„ ๋ฆฌํฌํŠธ/๋ถ€์ˆ˜ํšจ๊ณผ ๋ฏธ์ถฉ์กฑ/๋ฌดํŒ์ • ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ Phase 0 ์Šคํ‚ค๋งˆ๋กœ ์ถ”๊ฐ€(๊ธฐ์กด `validate_watcher_scenario` ํ†ต๊ณผ ํ•„์ˆ˜). +- `state-transitions.md` ยง4ยทยง5 ๊ตฌํ˜„ ๋งคํ•‘, `task.md` P2B-01~03 ์ฆ๊ฑฐ, ์šด์˜ ๋…ธํŠธ(`phase2b-autocomplete.md`). + +--- + +## 4. ๊ฒŒ์ดํŠธ + +| # | ์กฐ๊ฑด | ์ธก์ • | +|---|---|---| +| G1 | ๊ธฐ์กด **664** ๋ฌดํšŒ๊ท€ + ์‹ ๊ทœ ํ†ต๊ณผ, skipped 0 | `pytest tests/ -q` | +| G2 | ์…ธ ๋ฌธ๋ฒ•ยท๊ณต๋ฐฑ | `bash -n`, **๋ฒ”์œ„** `git diff --check` | +| G3 | Python 3.9 ์ปดํŒŒ์ผ | `compileall` | +| **G4** | **๊ธฐ๋ณธ OFF์—์„œ ์ž๋™์™„๋ฃŒ 0๊ฑด** | ํ”Œ๋ž˜๊ทธ ๋ฏธ์„ค์ • ์‹œ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ „ํ›„ ํ•ด์‹œ ๋™์ผ, ๋ช…์‹œ ๊ฒฝ๋กœ ๊ฒฐ๊ณผ ๋ฐ”์ดํŠธ ๋™์ผ | +| **G5** | **๋งค๋‹ˆํŽ˜์ŠคํŠธ ์œ„์กฐ ๊ฑฐ๋ถ€** | ํ•ด์‹œ ๋ถˆ์ผ์น˜ยทattempt ๋ถˆ์ผ์น˜ยท๋ถ€์žฌ ์ „๋ถ€ `hold` | +| G6 | ๋ฝ ์•ˆ I/O 0๊ฑด | Phase 2a G5 ๊ฐ์‹œ ํ…Œ์ŠคํŠธ ํ™•์žฅ(๋ฐœํ–‰ ํฌํ•จ) | +| G7 | ์žฌ์‹œ์ž‘ ๋ณต๊ตฌ | ํฌ๋ž˜์‹œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ํ›„ `pending_notification` ์žฌ๋ฐœํ–‰ ๋ฐ ๋ฉฑ๋“ฑ | +| G8 | ๋กค๋ฐฑ | `MAM_AUTO_COMPLETE=0` ์ฆ‰์‹œ ์ค‘๋‹จ, ์ด๋ฏธ ํ™•์ •๋œ terminal์€ ๋ณด์กด | +| G9 | ํ—ˆ์šฉ๋ชฉ๋ก | ์ฆ๊ฑฐ ์—†๋Š” ๋ฐฑ์—”๋“œ๋Š” ํ”Œ๋ž˜๊ทธ ON์—์„œ๋„ ๋ฏธ์ปค๋ฐ‹ | + +--- + +## 5. ๋กค๋ฐฑ + +1. **์ฆ‰์‹œ**: `MAM_AUTO_COMPLETE=0`. ์‹ ๊ทœ ์ž๋™์™„๋ฃŒ๋งŒ ์ค‘๋‹จ๋˜๊ณ  **์ด๋ฏธ ํ™•์ •๋œ terminal ๋ ˆ์ฝ”๋“œ๋Š” ๋ณด์กด**ํ•œ๋‹ค(`implementation_plan.md` Phase 2b ๋กค๋ฐฑ ์กฐ๊ฑด ๊ทธ๋Œ€๋กœ). +2. `MAM_ENGINE_CAS=0`์€ Phase 2a ๊ฒฝ๋กœ๊นŒ์ง€ ํ•จ๊ป˜ ๋˜๋Œ๋ฆฐ๋‹ค(์ž๋™์™„๋ฃŒ๋Š” CAS ์œ„์— ์žˆ์œผ๋ฏ€๋กœ ์ž๋™์œผ๋กœ ๋ฌด๋ ฅํ™”). +3. ์ปค๋ฐ‹ ๋‹จ์œ„ revert๋Š” 5โ†’2 ์ˆœ. ์ปค๋ฐ‹ 1(ํŠน์„ฑํ™”)์€ ๋‚จ๊ฒจ๋„ ๋ฌดํ•ด. + +--- + +## 6. ๋ฆฌ์Šคํฌ + +| # | ๋ฆฌ์Šคํฌ | ์™„ํ™” | +|---|---|---| +| R1 | ์›Œ์ฒ˜๊ฐ€ ์Šค์Šค๋กœ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด ์ž๊ธฐ ์ฆ๋ช…์ด ๋จ | D1: ์ž‘์„ฑ ์ฃผ์ฒด๋ฅผ ์—์ด์ „ํŠธ๋กœ ๊ณ ์ •, ์›Œ์ฒ˜๋Š” ํ•ด์‹œ ์žฌ๊ณ„์‚ฐ ๋Œ€์กฐ๋งŒ | +| R2 | `attempt_id`/`iteration` ํ˜ผ๋™์œผ๋กœ CAS ๊ฐ€๋“œ ๋ฌด๋ ฅํ™” | D2: ์Šน๊ฒฉ + ๋Œ€์ฒด ์ œ๊ฑฐ. **๋ฆฌ๋ทฐ์–ด ์Šน์ธ ์ „ ์ฐฉ์ˆ˜ ๊ธˆ์ง€** | +| R3 | ์ž˜๋ชป๋œ ์ž๋™์™„๋ฃŒ๋ฅผ ๋ž˜ํผ๊ฐ€ ๊ตฌ๋ณ„ ๋ชป ํ•จ | D6 `completed_by` ๊ฐ์‚ฌ ํ‘œ์‹ + ๊ธฐ๋ณธ OFF + ํ—ˆ์šฉ๋ชฉ๋ก | +| R4 | ์ง€์—ฐ ์•Œ๋ฆผ์ด ์ƒˆ ์ €์žฅ์†Œ๋ฅผ ๋งŒ๋“ค์–ด ๊ณ„์•ฝ ์œ„๋ฐ˜ | D4: ๋ ˆ์ฝ”๋“œ ๋‚ด ํ•„๋“œ, ยง5 ๋ช…์‹œ ์ค€์ˆ˜ | +| R5 | ์•Œ๋ฆผ ์žฌ๋ฐœํ–‰๊ธฐ๊ฐ€ ์Šค๋ ˆ๋“œ๋ฅผ ์จ `preexec_fn` ๊ฒŒ์ดํŠธ์— ๊ฑธ๋ฆผ | D8: 2b๋Š” ์Šคํฐ ์—†์Œ | +| R6 | ๋ถ€์ˆ˜ํšจ๊ณผ "๊ฒ€์ฆ"์„ ๊ณผ๋Œ€ ์ฃผ์žฅ | D5: ์กด์žฌยท๋Œ€์‘๋งŒ ๊ฒ€์ฆํ•œ๋‹ค๊ณ  ๋ฌธ์„œ์— ๋ช…์‹œ, ์›๊ฒฉ ํ™•์ธ์€ ๋น„๋ฒ”์œ„ | + +--- + +## 7. ๋ช…์‹œ์  ๋น„๋ฒ”์œ„ + +- `run_loop.sh` ํŒŒ์ด์ฌํ™” โ€” Phase 3. +- ๋ถ€์ˆ˜ํšจ๊ณผ์˜ **์›๊ฒฉ** ํ™•์ธ(์‹ค์ œ git push ๋„๋‹ฌ ์—ฌ๋ถ€ ๋“ฑ) โ€” ์ฆ๊ฑฐ ๋Œ€์‘๊นŒ์ง€๋งŒ. +- `agy`/`hermes`/`opencode` ์ž๋™์™„๋ฃŒ โ€” ์ •์ง€ ์ƒํƒœ ์‹ค์ธก ์ฆ๊ฑฐ ํ™•๋ณด ํ›„ ๋ณ„๋„ ํŒ๋‹จ. +- `preexec_fn` ๊ต์ฒด โ€” ์Šคํฐ์ด ํ•„์š”ํ•ด์ง€๋Š” ์‹œ์ . +- ์Šคํ‚ค๋งˆ v2 โ€” `attempt_id` ์ถ”๊ฐ€๋Š” v1 ์œ ์ง€(์ถ”๊ฐ€ ํ•„๋“œ). + +--- + +## 8. Worker ์ธ์ˆ˜์ธ๊ณ„ ์ฒดํฌ๋ฆฌ์ŠคํŠธ + +- [ ] **์ฐฉ์ˆ˜ ์ „**: ยงD2(`attempt_id` ์Šน๊ฒฉ = Phase 1 ์Šคํ‚ค๋งˆ ๊ณ„์•ฝ ๋ณ€๊ฒฝ)์— ๋Œ€ํ•œ **๋ฆฌ๋ทฐ์–ด ์Šน์ธ**์„ ๋ฐ›์„ ๊ฒƒ +- [ ] P2B-00 ์ปค๋ฐ‹ 1: ํ˜„ํ–‰ fail-closed ํŠน์„ฑํ™”๊ฐ€ ๋ ˆ๊ฑฐ์‹œ์—์„œ ํ†ต๊ณผ +- [ ] P2B-01 ์ปค๋ฐ‹ 2: ๋งค๋‹ˆํŽ˜์ŠคํŠธ ์ž‘์„ฑ/๊ฒ€์ฆ, ํ•ด์‹œ ์žฌ๊ณ„์‚ฐ, ๋ถ€์ˆ˜ํšจ๊ณผ ๋Œ€์‘ +- [ ] P2B-02 ์ปค๋ฐ‹ 3: ๊ฒŒ์ดํŠธ ์—ฐ๋™ + `pending_notification` + ์žฌ์‹œ์ž‘ ๋ณต๊ตฌ +- [ ] P2B-03 ์ปค๋ฐ‹ 4: ํ”Œ๋ž˜๊ทธ 3์ข… + ํ—ˆ์šฉ๋ชฉ๋ก + `completed_by` +- [ ] P2B-04 ์ปค๋ฐ‹ 5: ์‹œ๋‚˜๋ฆฌ์˜คยท๋ฌธ์„œ +- [ ] ๋ณด๊ณ ์„œ: G1 ์›๋ฌธ, G4 ํ•ด์‹œ ๋น„๊ต, G7 ๋ณต๊ตฌ ๋กœ๊ทธ, `grep -n` ์ธ์šฉ +- [ ] ์ปค๋ฐ‹ ์ ‘๋‘: `test/feat/feat/feat/docs(issue-5)` + +--- + +## ๋ถ€๋ก A. ๊ณ„ํš ์ˆ˜๋ฆฝ ์‹œ ์‹คํ–‰ํ•œ ๊ฒ€์ฆ ๋ช…๋ น + +```bash +git log --oneline -1 # 4a5916a +.venv/bin/python -m pytest tests/ --collect-only -q | tail -1 # 664 +find .mam/jobs -name "*manifest*" # ์—†์Œ +grep -rn "pending_notification\|MAM_AUTO_COMPLETE" --include='*.py' .agents # 0๊ฑด +grep -n 'record.get("attempt_id", record.get("iteration"))' .../engine.py # ๋Œ€์ฒด ํ™•์ธ +python -c "... transition(completed, expected_attempt=1|2) ..." # iteration ๋Œ€์ฒด ์‹ค์ฆ +python -c "... herdr/manifest.json observed slots ..." # ๋ฐฑ์—”๋“œ ์ฆ๊ฑฐ ํ–‰๋ ฌ +grep -n "publish_event.py --job" create_session.sh ๋ž˜ํผ # ์ง€์‹œ๋ฌธ ์ฃผ์ž… 2๊ณณ +```