# ๐Ÿš€ MAM ๋ฉ”์‹œ์ง• ๋ฐฑํ”Œ๋ ˆ์ธ ์ „ํ™˜ ์‹คํ–‰ ๋กœ๋“œ๋งต (`implementation_plan.md`) - **๋ฌธ์„œ ๋ฒ„์ „**: v1.0.0 (`8c651798` / `28bb7340`) - **์ž‘์„ฑ/๊ด€๋ฆฌ ์ฃผ์ฒด**: Multi-Agent Orchestration Team (`claude`, `agy`, `cline`) - **๊ธฐ์ค€ ์ปค๋ฐ‹**: `a9934ad` (276/276 baseline tests passing) - **๋ฌธ์„œ ๋ชฉ์ **: MAM์˜ ๋ฉ”์‹œ์ง• ์ธํ”„๋ผ๋ฅผ ๊ณต๊ฐœ HiveMQ ๋ธŒ๋กœ์ปค์—์„œ `nats-server` ์ „์šฉ ์‚ฌ์„ค ๋ธŒ๋กœ์ปค๋กœ ๋ฌด์ค‘๋‹จ ์ „ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ 4๊ฐœ ํŠธ๋ž™(Track 0~3)๊ณผ 5๋‹จ๊ณ„ ๋งˆ์ผ์Šคํ†ค(M0~M4)์˜ ๊ตฌ์ฒด์  ์‹คํ–‰ ์ง€์นจ ๋ฐ ์ง„ํ–‰ ์ƒํ™ฉ ์ถ”์ . - **์—ฐ๊ณ„ ๋ฌธ์„œ**: [`NATS_REPORT.md`](nats-docker/NATS_REPORT.md), [`PRIVATE_SERVER.md`](nats-docker/PRIVATE_SERVER.md), [`IMPROVEMENTS.md`](IMPROVEMENTS.md) --- ## 1. ๊ฐœ์š” ๋ฐ 4๊ฐœ ํŠธ๋ž™ ๊ตฌ์กฐ ``` [M0: ๋ฌธ์„œ ์ •ํ•ฉ์„ฑ] โ”€โ”€> [M1: ๋‚ด๊ฒฐํ•จ์„ฑ ํ™•๋ณด] โ”€โ”€> [M2: ๋ธŒ๋กœ์ปค ์‹ค์ฆ] โ”€โ”€> [M3: ๋ณด์•ˆ ์ข…๊ฒฐ] โ”€โ”€> [M4: ๋™๊ธฐํ™” ์™„๋ฃŒ] (E-1~E-4 ๊ต์ •, (Track 0: B-14,B-15, (Track 1: O-5 (Track 2: A-2, (Track 3: ๋ฌธ์„œ, G-D1~G-D4 ๊ฐ€๋“œ) G-1~G-10 ๊ฐ€๋“œ) S-1~S-9 ์ŠคํŒŒ์ดํฌ) ์ง€๋ฌธ ํ† ํ”ฝ, G-11) ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ) ``` | ํŠธ๋ž™ | ๋Œ€์ƒ ๊ณผ์ œ | ํ•ต์‹ฌ ๋ชฉํ‘œ | ์ฝ”๋“œ ๋ณ€๊ฒฝ ์ง€์  | |---|---|---|---| | **Track 0** | `B-14`, `B-15` (P1) | ๋ธŒ๋กœ์ปค ๋‹ค์šด ์‹œ 65๋ถ„ ์ •์ง€(Hang) ๋ฐ ์˜คํŒ์ • ๋ฐฉ์ง€ (๋กœ์ปฌ ๋””์Šคํฌ ๋‚ด๊ฒฐํ•จ์„ฑ) | `publish_event.py`, `job_subscriber.py`, `multi-agent-mux-delegate-job` | | **Track 1** | `O-5` (P2) | `nats-server` MQTT 3.1.1 ์–ด๋Œ‘ํ„ฐ ํ˜ธํ™˜์„ฑ ๋ฐ Retained ๋ฉ”์‹œ์ง€ ์‹ค์ธก ๊ฒ€์ฆ | ๊ฒฉ๋ฆฌ ํด๋ก  (`$SCRATCH/nats-spike`) | | **Track 1R** | ์›๊ฒฉ ํ”„๋กœ๋•์…˜ (P1) | VPS/ํ™ˆ๋žฉ `nats-server` Docker ์ƒ์‹œ ๊ฐ€๋™ ๋ฐ MAM ์›๊ฒฉ ๋ฐฑํ”Œ๋ ˆ์ธ ์ „ํ™˜ | `PRIVATE_SERVER.md` ยง9, ์„œ๋ฒ„์ธก `docker-compose.yml`/`nats.conf`, `.mam.env` | | **Track 2** | `A-2`, `B-16` (P2) | ์›Œํฌ์ŠคํŽ˜์ด์Šค ์ง€๋ฌธ ํ† ํ”ฝ ๊ฒฉ๋ฆฌ ๋ฐ `auth_token` ๋ฌด์กฐ๊ฑด ๋ฐœ๊ธ‰ ๊ฐ•์ œ | `mqtt_common.py`, `registry.py`, `reconcile.sh` | | **Track 3** | ๋ฌธ์„œ/์„ค์ • ๋™๊ธฐํ™” | ๊ณต์‹ ๊ฐ€์ด๋“œ, ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ, ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ…œํ”Œ๋ฆฟ ์ผ์›ํ™” | `MESSAGING.md`, `IMPROVEMENTS.md`, `VERSIONS.md`, `deploy/*` | --- ## 2. ๋‹จ๊ณ„๋ณ„ ๋งˆ์ผ์Šคํ†ค (Milestones M0 ~ M4) ๊ฐ ๋งˆ์ผ์Šคํ†ค์€ ์™„๋ฃŒ ์ •์˜(DoD)์™€ ์—„๊ฒฉํ•œ ๊ฒŒ์ดํŠธ(Gate)๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๊ฒŒ์ดํŠธ ์กฐ๊ฑด์„ ์ถฉ์กฑํ•˜์ง€ ๋ชปํ•˜๋ฉด ๋‹ค์Œ ๋งˆ์ผ์Šคํ†ค์œผ๋กœ ์ง„์ž…ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ``` M0 (๋ฌธ์„œ ์ •ํ•ฉ์„ฑ) โ”€โ”€> M1 (Track 0 ๋‚ด๊ฒฐํ•จ์„ฑ) โ”€โ”€> M2a (๋กœ์ปฌ ์ŠคํŒŒ์ดํฌ) โ”€โ”€> M2b (์›๊ฒฉ ๋ฐฐํฌ) โ”€โ”€> M3 (Track 2 ๋ณด์•ˆ) โ”€โ”€> M4 (Track 3 ์™„๊ฒฐ) ``` | ๋งˆ์ผ์Šคํ†ค | ์ด๋ฆ„ | ์™„๋ฃŒ ์ •์˜ (Definition of Done) | ํ†ต๊ณผ ๊ฒŒ์ดํŠธ (Gate Condition) | |---|---|---|---| | **M0** | ๋ฌธ์„œ ์ •ํ•ฉ์„ฑ ํ™•๋ณด | `PRIVATE_SERVER.md` E-1~E-4 ๊ต์ •, ๋‹ค๋Šฅ์„ฑ ์ ˆ ์ถ”๊ฐ€, ๋ณธ ๋กœ๋“œ๋งต ์ž‘์„ฑ | **G-D1 ~ G-D4 ๊ฐ€๋“œ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ** (276 -> 280) | | **M1** | ๋‚ด๊ฒฐํ•จ์„ฑ ํ™•๋ณด (Track 0) | `B-14`, `B-15` ์ฝ”๋“œ ํŒจ์น˜ ์™„๋ฃŒ | **G-1 ~ G-10 ๊ฐ€๋“œ ํ†ต๊ณผ + mutation ์ „๊ฑด FAIL ํ™•์ธ** (280 -> 290) | | **M2a** | ๋กœ์ปฌ ์ŠคํŒŒ์ดํฌ (Track 1) | ๊ฒฉ๋ฆฌ ํด๋ก ์—์„œ S-1 ~ S-9 ์ŠคํŒŒ์ดํฌ ์™„์ˆ˜ | **S-3(Retained Terminal Event) ํ†ต๊ณผ** (์‹คํŒจ ์‹œ mosquitto๋กœ ๋ถ„๊ธฐ) | | **M2b** | ์›๊ฒฉ ํ”„๋กœ๋•์…˜ ์ „ํ™˜ (Track 1R) | D-1~D-5 ๊ต์ • + ยง9 ์›๊ฒฉ ๋ฐฐํฌ + ยง9.5 ์ „ํ™˜ | **R-3(๋…ธ์ถœ0) ยท R-5(retained) ยท R-6(์‹ ์›) ยท R-9(๊ณ„์ •๊ฒฉ๋ฆฌ) ๋™์‹œ ํ†ต๊ณผ** | | **M3** | ๋ณด์•ˆ ์ข…๊ฒฐ (Track 2) | A-2 ์ง€๋ฌธ ํ† ํ”ฝ ์ „ํ™˜, G-11 ๋ฌด์กฐ๊ฑด ํ† ํฐ ๋ฐœ๊ธ‰ | ์ง€๋ฌธ ํ† ํ”ฝ ๋™์ž‘ ํ™•์ธ **ํ›„** legacy ๊ตฌ๋… ์ œ๊ฑฐ | | **M4** | ๋™๊ธฐํ™” ์™„๋ฃŒ (Track 3) | `MESSAGING.md`, `IMPROVEMENTS.md`, `VERSIONS.md`, `deploy/*` ์ •ํ•ฉ | ์ „์ฒด ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ 100% Green | --- ## 3. Track 0: ๊ฐ€์šฉ์„ฑ ๋ฐ ๋กœ์ปฌ ๋‚ด๊ฒฐํ•จ์„ฑ ๊ต์ • (`B-14`, `B-15`) > **ํ•ต์‹ฌ ์›์น™**: ๋ธŒ๋กœ์ปค ์„ ํƒ๊ณผ ์™„์ „ํžˆ ๋…๋ฆฝ์ ์ธ ์„ ํ–‰ ๊ณผ์ œ์ด๋ฉฐ, **Step 1 -> Step 2 -> Step 3์˜ ์—„๊ฒฉํ•œ ์ˆœ์„œ ์˜์กด์„ฑ**์„ ๊ฐ–์Šต๋‹ˆ๋‹ค. Step 2๋ฅผ ๋จผ์ € ๊ตฌํ˜„ํ•˜๋ฉด ๋””์Šคํฌ์— ํ„ฐ๋ฏธ๋„ ์ƒํƒœ๊ฐ€ ๊ธฐ๋ก๋˜์ง€ ์•Š์•„ ํด๋ฐฑ ํšจ๊ณผ๊ฐ€ 0์ด ๋ฉ๋‹ˆ๋‹ค. ``` [Step 1: publish_event.py] โ”€โ”€> [Step 2: job_subscriber.py] โ”€โ”€> [Step 3: delegate-job rc ๋งคํ•‘] ๋””์Šคํฌ ์ƒํƒœ ๋™๊ธฐํ™” ์„ ํ–‰ ๋กœ์ปฌ ๋””์Šคํฌ ํด๋ฐฑ ๊ฐ์ง€ ์ธํ”„๋ผ ์—๋Ÿฌ(rc=3) ๋ถ„๋ฆฌ ``` ### 3.1 Step 1 โ€” `publish_event.py` ์‹คํŒจ ์ฒ˜๋ฆฌ ์ˆœ์„œ ์žฌ๊ตฌ์„ฑ (`B-14` / `F-1`) 1. `publish(...)` ํ•จ์ˆ˜๋ฅผ `try-except`๋กœ ๊ฐ์‹ธ๋˜, ๋„คํŠธ์›Œํฌ ์‹คํŒจ ์‹œ ์ฆ‰์‹œ `return 2` ํ•˜์ง€ ์•Š๊ณ  `publish_ok = False`๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. 2. `mqtt_common.append_event` ๊ฐ์‚ฌ ๋กœ๊ทธ ์ž‘์„ฑ ๋ฐ `mqtt_common.update_job_status(status=new_status)` ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ƒํƒœ ๋™๊ธฐํ™”๋ฅผ **๋ฐœํ–‰ ์„ฑ๊ณต ์—ฌ๋ถ€์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ํ•ญ์ƒ ์ˆ˜ํ–‰**ํ•ฉ๋‹ˆ๋‹ค. 3. ๊ฐ์‚ฌ ๋กœ๊ทธ ๋ ˆ์ฝ”๋“œ์— `"published": publish_ok` ๋ฐ `"publish_error": str(exc)` ํ•„๋“œ๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. 4. ๋ชจ๋“  ๋กœ์ปฌ ๋””์Šคํฌ ๋™๊ธฐํ™”๊ฐ€ ์™„๋ฃŒ๋œ ํ›„, ๋„คํŠธ์›Œํฌ ๋ฐœํ–‰์ด ์‹คํŒจํ–ˆ๋‹ค๋ฉด ๊ธฐ์กด ํ˜ธ์ถœ๋ถ€ ๊ณ„์•ฝ ์œ ์ง€๋ฅผ ์œ„ํ•ด `return 2`๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ### 3.2 Step 2 โ€” `job_subscriber.py` ๋กœ์ปฌ ๋””์Šคํฌ ํด๋ฐฑ ๋„์ž… (`B-15` / `C1`) 1. ๋Œ€๊ธฐ ๋ฃจํ”„์˜ `queue.Empty` ๋ถ„๊ธฐ(`job_subscriber.py:233`)์—์„œ, 3์ดˆ ๊ฐ„๊ฒฉ ์Šค๋กœํ‹€๋กœ ๊ฐ์‹œ ์ค‘์ธ ์žก์˜ ๋””์Šคํฌ ํ„ฐ๋ฏธ๋„ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค (`registry.load_job` -> `mqtt_common.read_logged_status`). 2. ๋””์Šคํฌ์—์„œ ํ„ฐ๋ฏธ๋„ ์ƒํƒœ(`completed` ๋˜๋Š” `error`)๊ฐ€ ๊ฐ์ง€๋˜๋ฉด, `source: disk-fallback` ํ•ฉ์„ฑ ์ด๋ฒคํŠธ๋ฅผ ํ‘œ์ค€ ์ถœ๋ ฅ์— ๊ธฐ๋กํ•˜๊ณ  ์ฆ‰์‹œ ์ •์ƒ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. 3. ์ข…๋ฃŒ ์ฝ”๋“œ ๋งคํ•‘: ๋””์Šคํฌ ์ƒํƒœ๊ฐ€ `completed`์ด๋ฉด `return 0`, `error`์ด๋ฉด `return 1`์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ### 3.3 Step 3 โ€” `multi-agent-mux-delegate-job` ์ธํ”„๋ผ ์˜ˆ์™ธ ๋ถ„๋ฆฌ (`B-15` / `F-4`) 1. `job_subscriber.py`์˜ ๋ฏธํฌ์ฐฉ ๋ธŒ๋กœ์ปค ์ ‘์† ์˜ˆ์™ธ์— ์ „์šฉ ์ข…๋ฃŒ ์ฝ”๋“œ `rc=3`์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค. 2. `multi-agent-mux-delegate-job:331-341`์˜ `sub_rc` ๋งคํ•‘์— `rc=3` ๋ถ„๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ `job_status="broker_unavailable"`๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ , `wait_for_job`๊ณผ ๋™์ผํ•˜๊ฒŒ ๋””์Šคํฌ ์ƒํƒœ๋ฅผ ์žฌํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ### 3.4 Track 0 ํšŒ๊ท€ ๊ฐ€๋“œ ๋งคํŠธ๋ฆญ์Šค (10์ข… ์‹ ์„ค โ€” G-1 ~ G-10) | ID | ๊ฐ€๋“œ ๋‚ด์šฉ | ๋ณ€์ด ๊ฒ€์ถœ ๊ธฐ์ค€ (Mutation) | |---|---|---| | **G-1** | ๋ธŒ๋กœ์ปค ๋„๋‹ฌ ๋ถˆ๊ฐ€ ์‹œ `publish_event.py`๊ฐ€ `rc=2`์ด๋ฉด์„œ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ `status=completed` ๊ธฐ๋ก | `return 2`๋ฅผ ์ƒํƒœ ๋™๊ธฐํ™” ์•ž์œผ๋กœ ์ด๋™ ์‹œ FAIL | | **G-2** | ๋™์ผ ์ƒํ™ฉ ๊ฐ์‚ฌ ๋กœ๊ทธ์— `published: false` ๋ฐ `publish_error` ๋ ˆ์ฝ”๋“œ ์กด์žฌ | `append_event`๋ฅผ ์„ฑ๊ณต ๊ฒฝ๋กœ๋กœ๋งŒ ํ•œ์ • ์‹œ FAIL | | **G-3** | ๋ธŒ๋กœ์ปค ์ •์ƒ ์‹œ `rc=0` + `status=completed` + `published: true` ๋ฌดํšŒ๊ท€ ๊ฒ€์ฆ | โ€” | | **G-4** | ๋ฐœํ–‰ ์‹คํŒจ ํ›„ `last_seq`๊ฐ€ 1 ์ฆ๊ฐ€ํ•˜๊ณ  ํ›„์† ๋ฐœํ–‰์ด ๋” ํฐ seq ์‚ฌ์šฉ | seq ๋กค๋ฐฑ ๋„์ž… ์‹œ FAIL | | **G-5** | ๋””์Šคํฌ `status=completed` ์„ ์ž‘์„ฑ ์‹œ ๋ธŒ๋กœ์ปค ๋‹ค์šด ์ƒํƒœ์—์„œ๋„ `job_subscriber.py`๊ฐ€ 3์ดˆ ๋‚ด `rc=0` ์ข…๋ฃŒ | ๋””์Šคํฌ ํด๋ฐฑ ์ œ๊ฑฐ ์‹œ FAIL | | **G-6** | ๋™์ผ ์กฐ๊ฑด์—์„œ stdout ํ•ฉ์„ฑ ๋ผ์ธ์— `disk-fallback` ํ‘œ๊ธฐ ํ™•์ธ | ํ‘œ๊ธฐ ๋ˆ„๋ฝ ์‹œ FAIL | | **G-7** | ๋””์Šคํฌ `status=error` ์‹œ ํด๋ฐฑ `rc=1` ๋ฐ˜ํ™˜ ํ™•์ธ | ๋งคํ•‘ ๋ฐ˜์ „ ์‹œ FAIL | | **G-8** | ๋‹ค์ค‘ ์žก ๊ฐ์‹œ ์‹œ ์ „์ฒด ์™„๋ฃŒ ์ „๊นŒ์ง€ ์กฐ๊ธฐ ์ข…๋ฃŒ ๋ฐฉ์ง€ | ๋ถ€๋ถ„ ์ข…๋ฃŒ ๋„์ž… ์‹œ FAIL | | **G-9** | ๋””์Šคํฌ ํ„ฐ๋ฏธ๋„ ๋ถ€์žฌ + ๋ธŒ๋กœ์ปค ์‹คํŒจ ์‹œ `rc=3` ๋ฐ˜ํ™˜ ํ™•์ธ | `rc=1`๋กœ ๋˜๋Œ๋ฆด ์‹œ FAIL | | **G-10** | `loop` ์œ„์ž„ ๊ฒฝ๋กœ์—์„œ `rc=3` ์ˆ˜์‹  ์‹œ `job_status`๊ฐ€ `"error"`๋กœ ์˜คํŒ๋˜์ง€ ์•Š์Œ ํ™•์ธ | 3๋ถ„๊ธฐ ๋งคํ•‘ ๋ณต์› ์‹œ FAIL | --- ## 4. Track 1: `nats-server` ์ŠคํŒŒ์ดํฌ ๊ฒ€์ฆ (`O-5`) > **์‹คํ–‰ ์›์น™**: ๋ฉ”์ธ ์ €์žฅ์†Œ ์ž‘์—… ํŠธ๋ฆฌ๋ฅผ ์˜ค์—ผ์‹œํ‚ค์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๊ฒฉ๋ฆฌ ํด๋ก (`git clone --local --no-hardlinks . "$SCRATCH/nats-spike"`)์—์„œ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ข…๋ฃŒ ํ›„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค. | ID | ๊ฒ€์ฆ ํ•ญ๋ชฉ | ๊ฒ€์ฆ ๋ฐฉ๋ฒ• | ํ†ต๊ณผ ๊ธฐ์ค€ | |---|---|---|---| | **S-1** | `nats-server` MQTT ๋ฆฌ์Šค๋„ˆ ๊ธฐ๋ณธ ์ˆ˜์šฉ | `nats-server -c nats.conf` ๊ธฐ๋™ ํ›„ `started/progress/completed` 3์—ฐ์† ๋ฐœํ–‰ | `rc=0`, ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ `status=completed` | | **S-2** | paho 2.x `CallbackAPIVersion.VERSION2` ํ˜ธํ™˜ | `on_connect` CONNACK reason code ์ˆ˜์‹  ํ™•์ธ | `reason_code == 0` | | **S-3** | **Retained Terminal Event ์ „๋‹ฌ** (ํ•ต์‹ฌ ๊ด€๋ฌธ) | `--event completed` ๋ฐœํ–‰ ํ›„ ์‹ ๊ทœ `job_subscriber.py` ๊ธฐ๋™ | **์ฆ‰์‹œ ์ตœ์ข… ์ด๋ฒคํŠธ ์ˆ˜์‹ ** (*์‹คํŒจ ์‹œ mosquitto๋กœ ํšŒ๊ท€*) | | **S-4** | QoS 1 ๋ฐœํ–‰ ACK | `info.wait_for_publish()` ๋Œ€๊ธฐ | `is_published() == True` | | **S-5** | ์™€์ผ๋“œ์นด๋“œ ํ† ํ”ฝ ๋ผ์šฐํŒ… | `mam//jobs/+/events` ๊ตฌ๋… ํ›„ ์ด๋ฒคํŠธ ์ˆ˜์‹  | `SUBSCRIBED` ์ถœ๋ ฅ ๋ฐ ํŽ˜์ด๋กœ๋“œ ์ˆ˜์‹  | | **S-6** | ์ธ์ฆ ๋ฐ TLS ์•”ํ˜ธํ™” | user/pass ๋ฐ TLS ๊ตฌ์„ฑ ํ›„ ์ ‘์† ํ…Œ์ŠคํŠธ | ์ž๊ฒฉ์ฆ๋ช… ๋ˆ„๋ฝ ์‹œ ๊ฑฐ๋ถ€, ์œ ํšจ ์‹œ ์„ฑ๊ณต | | **S-7** | Subject ๋‹จ์œ„ ๊ถŒํ•œ ๊ฒฉ๋ฆฌ | Publisher write-only / Subscriber read-only ์„ค์ • | ๋น„์ธ๊ฐ€ ์ž‘์—… ์‹œ ์—ฐ๊ฒฐ ๊ฑฐ๋ถ€ | | **S-8** | ์ „์ฒด ํšŒ๊ท€ ํ…Œ์ŠคํŠธ | `pytest tests/ -q` | **์ „๊ฑด PASS (0 failed)** | | **S-9** | Track 0 ๋‚ด๊ฒฐํ•จ์„ฑ ํ†ตํ•ฉ ๊ฒ€์ฆ | `nats-server` ๊ฐ•์ œ ์ข…๋ฃŒ ์ƒํƒœ์—์„œ ์œ„์ž„ ์žก ์™„์ฃผ ํ…Œ์ŠคํŠธ | `wait_for_job` 3์ดˆ ๋‚ด ๋ฐ˜ํ™˜ | --- ## 5. Track 1R: ์›๊ฒฉ ํ”„๋กœ๋•์…˜ ์ „ํ™˜ ๋กœ๋“œ๋งต (M2b ์ƒ์„ธ) ``` [P0 ๊ต์ •] D-1~D-5 ๋ฌธ์„œ ๊ต์ • + ยง5.2 ๊ฒฝ๊ณ„ ๋ช…๋ฌธํ™” + ์‹ ๊ทœ ๊ฐ€๋“œ 7์ข… โ–ผ [P0.5 ์ž์‚ฐํ™”] docker/ 4๋Œ€ ์ž์‚ฐ ์ •๋ณธํ™” + D-22~D-30 ํšŒ๊ท€ ๊ฐ€๋“œ (297 -> 306) โ–ผ [P1 ์„œ๋ฒ„ ์ค€๋น„] VPS/ํ™ˆ๋žฉ ํ”„๋กœ๋น„์ €๋‹, Docker, Tailscale ๊ฐ€์ž… โ–ผ [P2 ๋ฐฐํฌ] nats.conf + compose ๊ธฐ๋™, healthcheck healthy ํ™•์ธ โ–ผ [P3 ์ž ๊ธˆ] ๋ฐ”์ธ๋“œ ์ฃผ์†Œ ํ•œ์ • + UFW + ss/nmap ๋กœ ๋…ธ์ถœ ๋ฉด์  0 ๋‹จ์–ธ (R-3) โ–ผ [P4 ์ „ํ™˜] ๋“œ๋ ˆ์ธ โ†’ ์ž”์—ฌ ์Šค์บ” โ†’ .mam.env ๊ต์ฒด (ยง9.5) โ–ผ [P5 ๊ฒ€์ฆ] R-1 ~ R-13. R-5(retained) / R-9(๊ณ„์ • ๊ฒฝ๊ณ„) / R-13(MQTT-over-WS) โ–ผ [P6 ์ƒ์‹œํ™”] ๋กœ๊ทธ ๋กœํ…Œ์ด์…˜, JetStream ๋ณผ๋ฅจ ๋ฐฑ์—…, healthcheck ์•Œ๋ฆผ ``` --- ## 6. Track 2: A-2 ๋ณด์•ˆ ๊ฒฐํ•จ ๋ฐ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๊ฒฉ๋ฆฌ ํ•ด์†Œ (`A-2`, `B-16`) 1. **`auth_token` ๋ฌด์กฐ๊ฑด ๋ฐœ๊ธ‰ (`F-3` / `G-11`)**: `registry.register_job()`์—์„œ ๋ธŒ๋กœ์ปค ์„ค์ •๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ํ•ญ์ƒ `secrets.token_urlsafe(32)` ๊ธฐ๋ฐ˜ ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•˜์—ฌ ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค ํ™˜๊ฒฝ์—์„œ๋„ HMAC ๊ฒ€์ฆ์ด ๋ฌด๋ ฅํ™”๋˜์ง€ ์•Š๋„๋ก ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. 2. **์›Œํฌ์ŠคํŽ˜์ด์Šค ์ง€๋ฌธ ํ† ํ”ฝ 3๋‹จ๊ณ„ ์ „ํ™˜ (`F-2`)**: - Step 1: ๋ฐœํ–‰์ž ๊ธฐ๋ณธ ํ† ํ”ฝ์„ `mam//jobs//events`๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. - Step 2: ์‹คํ™˜๊ฒฝ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ ์ด๋ฒคํŠธ ์ˆ˜์‹ ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. - Step 3: `reconcile.sh:237`์˜ ๋ ˆ๊ฑฐ์‹œ ์ „์—ญ ํ† ํ”ฝ(`python/mqtt/jobs/...`) ๊ตฌ๋…์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. --- ## 7. Track 3: ๋ฌธ์„œ ๋ฐ ๋ฐฐํฌ ์„ค์ • ๋™๊ธฐํ™” | ๋Œ€์ƒ ํŒŒ์ผ | ๊ฐฑ์‹  ๋‚ด์šฉ | |---|---| | [`MESSAGING.md`](MESSAGING.md) | ๋ธŒ๋กœ์ปค ํ‘œ์ค€์„ `nats-server`๋กœ ๊ฐฑ์‹ , F-1/C1 ํ•ด์†Œ ๊ธฐ๋ก, F-5 ์˜์† ์„ธ์…˜ ์„œ์ˆ  ์ •์ • | | [`IMPROVEMENTS.md`](IMPROVEMENTS.md) | A-2 ์™„๋ฃŒ ์ „ํ™˜, B-14/B-15/B-16/O-5 ํ•ด๊ฒฐ ์ƒํƒœ ๊ฐฑ์‹ , B-17/B-18 ์‹ ์„ค ๋“ฑ๋ก | | [`VERSIONS.md`](VERSIONS.md) | `v2.0.0` ๋ฆด๋ฆฌ์Šค ๋…ธํŠธ์— ๋ฉ”์‹œ์ง• ๋ฐฑํ”Œ๋ ˆ์ธ ๊ณ ๋„ํ™” ๋ฐ ๋‚ด๊ฒฐํ•จ์„ฑ ํŒจ์น˜ ๊ธฐ๋ก | | [`PRIVATE_SERVER.md`](nats-docker/PRIVATE_SERVER.md) | ์ŠคํŒŒ์ดํฌ ๊ฒฐ๊ณผ ๋ฐ˜์˜ ๋ฐ ์ตœ์ข… ๊ฐ€์ด๋“œ ํ™•์ • | | [`deploy/install.sh`](deploy/install.sh) | `requirements.txt` ํ™•์ธ (paho ์œ ์ง€) ๋ฐ ๊ฐœ์ธ ๋ธŒ๋กœ์ปค ์•ˆ๋‚ด ์ถ”๊ฐ€ | | [`.mam.env`](.mam.env) | `MQTT_BROKER`, `MQTT_PORT`, `MQTT_TLS` ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ ํ™•์ • | --- ## 8. ์ง„ํ–‰ ์ถ”์  ์ฒดํฌ๋ฆฌ์ŠคํŠธ ### M0: ๋ฌธ์„œ ์ •ํ•ฉ์„ฑ ํ™•๋ณด - [x] `PRIVATE_SERVER.md` E-1~E-4 ๊ต์ • ๋ฐ ๋‹ค๋Šฅ์„ฑ ์ ˆ(ยง5) ์ถ”๊ฐ€ - [x] `implementation_plan.md` 4๊ฐœ ํŠธ๋ž™ ๋ฐ ๋งˆ์ผ์Šคํ†ค ์ˆ˜๋ฆฝ - [x] `tests/test_deploy_freshness.py` ๋‚ด G-D1 ~ G-D4 ๋ฌธ์„œ ๋“œ๋ฆฌํ”„ํŠธ ๊ฐ€๋“œ ๊ตฌํ˜„ ### M1: Track 0 ๋‚ด๊ฒฐํ•จ์„ฑ ํ™•๋ณด (`B-14`, `B-15`) - [x] Step 1: `publish_event.py` ์ƒํƒœ ๋™๊ธฐํ™” ์„ ํ–‰ ์ฒ˜๋ฆฌ (`B-14` / G-1~G-4) - [x] Step 2: `job_subscriber.py` ๋กœ์ปฌ ๋””์Šคํฌ ํด๋ฐฑ ๋„์ž… (`B-15` / G-5~G-8) - [x] Step 3: `multi-agent-mux-delegate-job` ์ธํ”„๋ผ `rc=3` ์—๋Ÿฌ ๋ถ„๋ฆฌ (`F-4` / G-9~G-10) - [x] M1 ํ†ตํ•ฉ ๊ฒ€์ฆ (๋ธŒ๋กœ์ปค ๋‹ค์šด ์ƒํƒœ ์œ„์ž„ 3์ดˆ ์™„์ฃผ) ### M2a: Track 1 `nats-server` ๋กœ์ปฌ ์‹ค์ฆ (`O-5`) - [ ] ๊ฒฉ๋ฆฌ ํด๋ก  ์ƒ์„ฑ (`$SCRATCH/nats-spike`) - [ ] S-1 ~ S-9 ์ŠคํŒŒ์ดํฌ ๋งคํŠธ๋ฆญ์Šค ๊ฒ€์ฆ ์ˆ˜ํ–‰ - [ ] S-3 Retained ๋ฉ”์‹œ์ง€ ๊ฒŒ์ดํŠธ ํ†ต๊ณผ ํ™•์ธ ### M2b: Track 1R ์›๊ฒฉ ํ”„๋กœ๋•์…˜ ์ „ํ™˜ - [x] D-1 `store_dir` ์ ˆ๋Œ€๊ฒฝ๋กœ ๊ต์ • + ๋น„์ธ์šฉ heredoc (`PRIVATE_SERVER.md:73`, `:146`) - [x] D-2 ์ด๋ฏธ์ง€ ํ•€ `nats:2.12-alpine` + `/healthz` healthcheck - [x] D-3 8222/8080 ๋ฐ”์ธ๋“œ ์ฃผ์†Œ ํ•œ์ • - [x] D-4 TLS ์ ˆ์˜ IP ์˜ˆ์‹œ ์ œ๊ฑฐ ๋ฐ DNS/SAN ์š”๊ฑด ๋ช…์‹œ - [x] D-5 `.mam.env.example` ์ •ํ•ฉ (`MQTT_KEEPALIVE` ์ถ”๊ฐ€) - [x] N-1 ยง5.2 ์— retained=MQTT ์ „์šฉ ๊ฒฝ๊ณ„ ๋ช…๋ฌธํ™” + ยง9.1 `mam_observer` ์ถ”๊ฐ€ - [x] ์‹ ๊ทœ ๊ฐ€๋“œ G-D5 ~ G-D9, G-R1, G-R2 ๊ตฌํ˜„ ๋ฐ ๊ฒ€์ฆ (290 -> 297) - [x] P0.5: `docker/` ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์ž์‚ฐ ์ •๋ณธํ™” ๋ฐ D-22 ~ D-30 ํšŒ๊ท€ ๊ฐ€๋“œ (297 -> 306) - [ ] ์„œ๋ฒ„ ๋ฐฐํฌ ๋ฐ R-3 ๋…ธ์ถœ ๋ฉด์  0 ๋‹จ์–ธ - [ ] ยง9.5 ๋“œ๋ ˆ์ธยท์ž”์—ฌ ์Šค์บ” ํ›„ `.mam.env` ์ „ํ™˜ - [ ] R-1 ~ R-13 ์ „๊ฑด ํ†ต๊ณผ (R-5 / R-9 / R-13 ์ตœ์ข… ๊ด€๋ฌธ) ### M3: Track 2 ๋ณด์•ˆ ๋ฐ ํ† ํ”ฝ ๊ฒฉ๋ฆฌ (`A-2`, `B-16`) - [ ] G-11 ๋ฌด์กฐ๊ฑด `auth_token` ๋ฐœ๊ธ‰ ์ ์šฉ - [ ] ์›Œํฌ์ŠคํŽ˜์ด์Šค ์ง€๋ฌธ ํ† ํ”ฝ ๋ฐœํ–‰ ์ „ํ™˜ ๋ฐ ๋ ˆ๊ฑฐ์‹œ ๊ตฌ๋… ์ œ๊ฑฐ ### M4: Track 3 ๋ฌธ์„œ ๋ฐ ๋ฐฐํฌ ๋™๊ธฐํ™” - [ ] `MESSAGING.md`, `IMPROVEMENTS.md`, `VERSIONS.md`, `deploy/*` ์ตœ์ข… ๊ฐฑ์‹