# ๐Ÿ“‘ Herdr Runtime Contract and Backend Capability Matrix - **Document Version**: Rev.5 (Evidence Source-Kind Dispatch & Identity Correspondence โ€” ยง3-1 ์‹ ์„ค) - **Issue**: [Gitea Issue #5](https://git.godopu.com/tmpl/multi-agent-mux/issues/5) - **Target Herdr Version**: `herdr 0.8.2` - **Scope**: Native Herdr API Contract, External Collector Specification, and Backend Matrix --- ## 1. Herdr 0.8.2 ๋„ค์ดํ‹ฐ๋ธŒ API ๊ณ„์•ฝ (Native API Contract) ### 1-1. ํ•ต์‹ฌ CLI ๋ช…๋ น์–ด ๋ฐ ๋ฐ˜ํ™˜ ํŽ˜์ด๋กœ๋“œ `herdr`๋Š” AI ์ฝ”๋”ฉ ์—์ด์ „ํŠธ ์ „์šฉ ํ„ฐ๋ฏธ๋„ ๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๋กœ์„œ, ์†Œ์ผ“ API๋ฅผ ํ†ตํ•ด ํ„ฐ๋ฏธ๋„ ์Šคํฌ๋ฆฐ ๊ฐ์‹œ ์ƒํƒœ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค: 1. **`herdr agent list`**: ํ˜„์žฌ ํ™œ์„ฑํ™”๋œ ๋ชจ๋“  ์—์ด์ „ํŠธ ๋ชฉ๋ก ๋ฐ ์ƒํƒœ ๋ฐ˜ํ™˜ (JSON stdout, exit 0). 2. **`herdr agent get `**: ํŠน์ • ์—์ด์ „ํŠธ์˜ ๋‹จ์ผ ์‹œ์  ์Šค๋ƒ…์ƒท ๋ฐ˜ํ™˜ (`agent_status`, `interactive_ready`, `pane_id`, `revision`, `state_change_seq` ๋“ฑ). 3. **`herdr agent wait [OPTIONS]`**: ์—์ด์ „ํŠธ๊ฐ€ ์ง€์ •๋œ ์ƒํƒœ์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€ ๋ธ”๋กœํ‚น ๋Œ€๊ธฐ. 4. **`herdr agent explain [OPTIONS] [target]`**: ๋‚ด๋ถ€ ์ •๊ทœ์‹ ๋งค์นญ ๊ทœ์น™(`matched_rule`, `evaluated_rules`) ํ‰๊ฐ€ ์„ธ๋ถ€์ •๋ณด ๋ฐ˜ํ™˜. ### 1-2. ๋„ค์ดํ‹ฐ๋ธŒ ์ƒํƒœ ์ง‘ํ•ฉ (`agent_status`) - **์ง€์› Enum**: `idle`, `working`, `blocked`, `done`, `unknown` (์ด 5์ข…). - **`interactive_ready` (boolean)**: - ํ„ฐ๋ฏธ๋„์ด ํ‚ค ์ž…๋ ฅ์„ ์ˆ˜์‹ ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ”Œ๋ž˜๊ทธ. - **์‹ค์ธก ๋ถˆ๋ณ€์‹**: ์—์ด์ „ํŠธ๊ฐ€ ์ฝ”๋”ฉ ์ž‘์—… ์ค‘(`agent_status: "working"`)์ผ ๋•Œ๋„ `interactive_ready: true`์ž„์ด ์ž…์ฆ๋จ. ๋”ฐ๋ผ์„œ `interactive_ready` ๋‹จ๋…์œผ๋กœ๋Š” ์ž‘์—… ์™„๋ฃŒ์˜ ์ถฉ๋ถ„์กฐ๊ฑด์ด ๋  ์ˆ˜ ์—†์œผ๋ฉฐ, ์˜ค์ง `agent_status in {"idle", "done"}` ์ƒํƒœ์™€ ๊ฒฐํ•ฉ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ### 1-3. `herdr agent wait` ํ˜ธ์ถœ ๊ทœ๊ฒฉ ๋ฐ ์ฑ„๋„ ๋ถ„๋ฆฌ ์‹ค์ธก ์‹ค์ œ `herdr 0.8.2` ์‹คํ–‰(`wait-contract.json` ๋ฐ `evidence/timeout_stderr.json`)์„ ํ†ตํ•ด ๊ฒ€์ฆ๋œ ์ž…์ถœ๋ ฅ ์ฑ„๋„ ๋ฐ ์ข…๋ฃŒ ์ฝ”๋“œ ๊ณ„์•ฝ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: | ํ˜ธ์ถœ ์กฐ๊ฑด | ๋ช…๋ น ์˜ˆ์‹œ | ์ข…๋ฃŒ ์ฝ”๋“œ | ์ถœ๋ ฅ ์ฑ„๋„ | ๋ฐ˜ํ™˜ JSON ํŽ˜์ด๋กœ๋“œ ๊ตฌ์กฐ | |---|---|---|---|---| | **์ƒํƒœ ๋„๋‹ฌ (์„ฑ๊ณต)** | `herdr agent wait creator-agy-01 --until working --timeout 1000` | `exit 0` | `stdout` | `{"id":"cli:agent:wait","result":{"agent":{...},"type":"agent_info"}}` | | **ํƒ€์ž„์•„์›ƒ (Timeout)** | `herdr agent wait creator-agy-01 --until done --timeout 50` | `exit 1` | **`stderr`** | `{"error":{"code":"timeout","message":"timed out waiting for agent status"},"id":"cli:agent:wait"}` | | **๋Œ€์ƒ ๋ถ€์žฌ (Unknown)** | `herdr agent wait non-existent --until idle --timeout 50` | `exit 1` | **`stderr`** | `{"error":{"code":"agent_not_found","message":"agent target non-existent not found"},"id":"cli:agent:wait"}` | | **๊ธฐ๋ณธ ๋Œ€๊ธฐ ์˜ต์…˜ ์ƒ๋žต** | `herdr agent wait ` | N/A | N/A | `--until` ์ƒ๋žต ์‹œ ๊ธฐ๋ณธ ๋งค์นญ ๋Œ€์ƒ์€ `idle, done, blocked`์ž„. `--timeout` ์ƒ๋žต ์‹œ ๋ฌด๊ธฐํ•œ ๋Œ€๊ธฐํ•จ | > ๐Ÿšจ **Critical Empirical Finding (์ˆ˜์ • ๋ฐ˜์˜)**: > `herdr agent wait`์—์„œ ์„ฑ๊ณต(์ƒํƒœ ์ผ์น˜) ์‹œ์—๋Š” ์ •์ƒ JSON์ด `stdout`์œผ๋กœ ์ถœ๋ ฅ๋˜์ง€๋งŒ, **ํƒ€์ž„์•„์›ƒ(`timeout`) ๋ฐ ๋Œ€์ƒ ๋ถ€์žฌ(`agent_not_found`) ๋“ฑ ๋ชจ๋“  ์—๋Ÿฌ ์‘๋‹ต์€ `stderr`๋กœ ์—๋Ÿฌ JSON์ด ์ถœ๋ ฅ๋˜๋ฉฐ ์ข…๋ฃŒ ์ฝ”๋“œ๋Š” `exit 1`**์ž…๋‹ˆ๋‹ค (`stdout`์€ ๋น„์–ด์žˆ์Œ). ๋”ฐ๋ผ์„œ Watcher ์—”์ง„์€ `exit 1` ์‹œ ๋ฐ˜๋“œ์‹œ `stderr` ์ŠคํŠธ๋ฆผ์—์„œ JSON ์—๋Ÿฌ ํŽ˜์ด๋กœ๋“œ๋ฅผ ํŒŒ์‹ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. --- ## 2. ์™ธ๋ถ€ ์‹œํ€€์Šค ์ˆ˜์ง‘๊ธฐ ๊ณ„์•ฝ (External Sequence Collector) ### 2-1. ๋‹จ์ผ `wait` ํ˜ธ์ถœ์˜ ๋ณธ์งˆ์  ํ•œ๊ณ„ - `herdr agent wait`๋Š” ํƒ€๊ฒŸ์ด ํŠน์ • ์ƒํƒœ์— ๋„๋‹ฌํ•œ ์‹œ์ ์— **๋‹จ 1๊ฐœ์˜ ์ตœ์ข… ์Šค๋ƒ…์ƒท JSON**๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋™๊ธฐ์  ์žฅ๋ฒฝ(Barrier)์ž…๋‹ˆ๋‹ค. - ํ˜ธ์ถœ ๋„์ค‘ ๊ฑฐ์ณ๊ฐ„ ์ค‘๊ฐ„ ์ „์ด ๊ณผ์ •(Transition Stream)์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. - ๋”ฐ๋ผ์„œ `working โž” idle`๊ณผ ๊ฐ™์€ ์ƒํƒœ ์ „์ด๋ฅผ ์ž…์ฆํ•˜๋ ค๋ฉด, ์™ธ๋ถ€ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ/์ˆ˜์ง‘๊ธฐ๊ฐ€ ์‚ฌ์ „ ์Šค๋ƒ…์ƒท(`before`)๊ณผ ์‚ฌํ›„ ์Šค๋ƒ…์ƒท(`after`)์„ ๋…๋ฆฝ์ ์œผ๋กœ ํš๋“ํ•˜์—ฌ ํƒ€์ž„์Šคํƒฌํ”„์™€ ํ•จ๊ป˜ ์—ฐ๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ### 2-2. ์ˆ˜์ง‘๊ธฐ(Collector) ๊ถŒ์žฅ ๊ทœ๊ฒฉ - **ํด๋ง ์ฃผ๊ธฐ**: 1~2์ดˆ ์ˆ˜์ค€์˜ ์œ ๊ณ„(Bounded) ํด๋ง (`agent get`). ๊ณ ์ •๋ฐ€ 10ms ์Šคํ•€ ํด๋ง์€ ๋ถˆํ•„์š”ํ•˜๋ฉฐ CPU ๋‚ญ๋น„๋ฅผ ์ดˆ๋ž˜ํ•ฉ๋‹ˆ๋‹ค. - **Envelope ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ**: ๋‹จ์กฐ ์‹œ๊ณ„ ์˜คํ”„์…‹(`monotonic_start`, `monotonic_end`), `collector_clock_id`, `capture_id`, `source_command`๋ฅผ ํ•„์ˆ˜๋กœ ๋ณด์กดํ•˜์—ฌ ์‹œ์  ๊ฐ„ ์™œ๊ณก์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. --- ## 3. ์ด๋ฒคํŠธ, ํŒŒ์ผ์‹œ์Šคํ…œ ๋ฐ ๋””์ŠคํŒจ์น˜ ์—ฐ๊ณ„ ๊ฒฝ๊ณ„ Herdr ๋„ค์ดํ‹ฐ๋ธŒ ํŽ˜์ด๋กœ๋“œ์™€ Multi-Agent-Mux ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์‚ฌ์ด์—๋Š” ์—„๊ฒฉํ•œ ์ฑ…์ž„ ๊ฒฉ๋ฆฌ๊ฐ€ ์œ ์ง€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Herdr Native API Boundary โ”‚ โ”‚ โ€ข agent_status (idle/working/blocked/done/unknown) โ”‚ โ”‚ โ€ข interactive_ready (true/false) โ”‚ โ”‚ โ€ข pane_id, state_change_seq, terminal_title โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ ๊ด€์ธก ์ฆ๊ฑฐ ์ œ๊ณต (์ฑ„๋„ 2) โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Multi-Agent-Mux Watcher Boundary โ”‚ โ”‚ โ€ข 4-Fold Attribution: โ”‚ โ”‚ (job_id, attempt_id, session_uuid, โ”‚ โ”‚ pane_process_generation) โ”‚ โ”‚ โ€ข File Evidence (report mtime debounce >=15s, hash) โ”‚ โ”‚ โ€ข Backplane Events (seq, HMAC signature, started/progress)โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` - **๋ถˆ๋ณ€์‹**: Herdr ์›๋ณธ ์‘๋‹ต ํŽ˜์ด๋กœ๋“œ์—๋Š” `job_id`, `attempt_id`, `report phase`, `expected_decision` ๋“ฑ์˜ MAM ๋‚ด๋ถ€ ํ•„๋“œ๋ฅผ ์ ˆ๋Œ€ ์ฃผ์ž…ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ### 3-1. Watcher ํƒ€์ž„๋ผ์ธ ์ฆ๊ฑฐ ์Šคํ‚ค๋งˆ ๋ฐ `source_kind` ๋ถ„๊ธฐ ๊ณ„์•ฝ (Rev.5) Watcher ๋ณตํ•ฉ ์‹œ๋‚˜๋ฆฌ์˜ค(`tests/fixtures/issue_5/watcher/*.json`)์˜ ๊ฐ ํƒ€์ž„๋ผ์ธ ํ•ญ๋ชฉ์€ ์•„๋ž˜ ๊ณ„์•ฝ์„ ๋”ฐ๋ฅด๋ฉฐ, `tests/test_issue5_phase0_contracts.py`์˜ `validate_watcher_scenario`๊ฐ€ ์ด๋ฅผ ๊ธฐ๊ณ„์ ์œผ๋กœ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. ์œ„๋ฐ˜ ์‹œ fixture ๊ฒ€์ฆ์€ **fail-closed(๊ฑฐ๋ถ€)** ์ž…๋‹ˆ๋‹ค. | ๊ทœ์น™ | ๋‚ด์šฉ | ๊ฐ•์ œ ์ง€์  | |---|---|---| | **์ง€์› source_kind ์ง‘ํ•ฉ** | `herdr`, `event`, `filesystem`, `harness` 4์ข…๋งŒ ํ—ˆ์šฉ. ๊ทธ ์™ธ ๊ฐ’์€ provenance์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ฑฐ๋ถ€ | `SUPPORTED_SOURCE_KINDS` | | **๊ด€์ธก(observed) ์ฆ๊ฑฐ ์Šคํ‚ค๋งˆ** | ๋…๋ฆฝ ์ฆ๊ฑฐ ์Šคํ‚ค๋งˆ๊ฐ€ ๊ตฌํ˜„๋œ kind๋งŒ `source_provenance: observed`๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Œ. **ํ˜„์žฌ `herdr`๋งŒ ํ•ด๋‹น.** `event`/`filesystem`/`harness`๋Š” ์Šคํ‚ค๋งˆ๊ฐ€ ์ƒ๊ธธ ๋•Œ๊นŒ์ง€ `simulated`(๋˜๋Š” `synthetic`)๋กœ๋งŒ ๊ธฐ๋กํ•˜๋ฉฐ, `observed`๋กœ ํ‘œ๊ธฐ ์‹œ `capture_ref`๊ฐ€ ์œ ํšจํ•˜๋”๋ผ๋„ ์ฆ‰์‹œ ๊ฑฐ๋ถ€ | `OBSERVED_EVIDENCE_SCHEMAS` | | **capture_ref ๊ฒฉ๋ฆฌ** | repo ์ƒ๋Œ€ ๊ฒฝ๋กœ์—ฌ์•ผ ํ•˜๋ฉฐ(์ ˆ๋Œ€ ๊ฒฝ๋กœ ๊ธˆ์ง€), ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ `tests/fixtures/issue_5/herdr/` ํ•˜์œ„์—ฌ์•ผ ํ•จ(`..` ์šฐํšŒ ๊ธˆ์ง€). `.json`์ด๊ณ  `schema_version 1.0.0`ยท`provenance observed`ยท`capture_id`๋ฅผ ๊ฐ–๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์บก์ฒ˜๋งŒ ์ฆ๊ฑฐ๋กœ ์ธ์ • | `_load_observed_capture_ref` | | **Herdr ํ•ญ๋ชฉ ๋Œ€์‘์„ฑ** | โ‘  ์ฐธ์กฐ ์บก์ฒ˜๋Š” Layer 1 ๋„ค์ดํ‹ฐ๋ธŒ ๊ณ„์•ฝ(`validate_native_capture`)์„ ์žฌํ†ต๊ณผ โ‘ก ํ•ญ๋ชฉ payload๋Š” ๋น„์–ด์žˆ์ง€ ์•Š์€ dict์ด๋ฉฐ `agent_status`๋ฅผ **ํ•„์ˆ˜ ์„ ์–ธ**(๋ˆ„๋ฝ ์‹œ ๋น„๊ต ์ƒ๋žต์ด ์•„๋‹Œ ๊ฑฐ๋ถ€) โ‘ข ์บก์ฒ˜๋Š” `result.agent` ๊ฐ์ฒด๋ฅผ ํฌํ•จํ•ด์•ผ ํ•จ(`wait` timeout ๋“ฑ nonzero-exit ์บก์ฒ˜๋Š” agent_status๋ฅผ ์ž…์ฆํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๊ฑฐ๋ถ€) โ‘ฃ ์„ ์–ธ๋œ **๋ชจ๋“ ** ํ•„๋“œ๊ฐ€ agent ๊ฐ์ฒด์˜ ๋„ค์ดํ‹ฐ๋ธŒ ํ•„๋“œ์— ์กด์žฌํ•˜๊ณ  ๊ฐ’์ด ์ •ํ™•ํžˆ ์ผ์น˜(๋น„๋„ค์ดํ‹ฐ๋ธŒ ํ•„๋“œ ์„ ์–ธ ์‹œ ๊ฑฐ๋ถ€) | `_validate_observed_herdr_item` | | **์ •์ฒด์„ฑ ๋Œ€์‘ (4์ค‘ ๊ท€์† ์—ฐ๋™)** | ์บก์ฒ˜์˜ `agent_session.value` == `dispatch_context.agent_session_uuid`, ์บก์ฒ˜์˜ `pane_id`๊ฐ€ `dispatch_context.pane_process_generation`(`::`)์˜ ์„ ๋‘ ๊ตฌ์„ฑ์š”์†Œ์—ฌ์•ผ ํ•จ. ์‹œ๋‚˜๋ฆฌ์˜ค `backend`๊ฐ€ `backend_agnostic`์ด ์•„๋‹ˆ๋ฉด ์บก์ฒ˜ `backend`์™€ ์ผ์น˜ํ•ด์•ผ ํ•จ | `_validate_observed_herdr_item` | | **1์บก์ฒ˜ = 1๊ด€์ธก** | ํ•˜๋‚˜์˜ `capture_id`๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค ๋‚ด ๊ด€์ธก ํ•ญ๋ชฉ ํ•˜๋‚˜๋งŒ ์ž…์ฆํ•  ์ˆ˜ ์žˆ์Œ(๋™์ผ ์Šค๋ƒ…์ƒท์˜ ์žฌ์ธ์šฉ ๊ธˆ์ง€) | `validate_watcher_scenario` | | **provenance ์ •์ง์„ฑ** | observedยทsimulated ํ˜ผ์žฌ ์‹œ ์ „์ฒด `provenance`๋Š” `mixed`, ์ „๋Ÿ‰ ๊ด€์ธก ์‹œ `observed_composite`, ์ „๋Ÿ‰ ๋ชจ์˜ ์‹œ `simulated`์—ฌ์•ผ ํ•จ (`synthetic` ํ•ญ๋ชฉ์€ ๋น„๊ด€์ธก์œผ๋กœ ์ง‘๊ณ„) | `validate_watcher_scenario` | | **๋งค๋‹ˆํŽ˜์ŠคํŠธ ์ •ํ•ฉ์„ฑ** | `watcher/manifest.json`์˜ `provenance`/`expected_decision`์€ ์‹œ๋‚˜๋ฆฌ์˜ค ํŒŒ์ผ๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•˜๋ฉฐ, `herdr/manifest.json`์˜ observed ์Šฌ๋กฏ์€ ๋™์ผ backend์˜ ์บก์ฒ˜๊ฐ€ ์Šฌ๋กฏ๋ช…๊ณผ ๊ฐ™์€ `agent_status`(๋˜๋Š” `wait_timeout` ์Šฌ๋กฏ์€ `error.code=timeout`)๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•จ. ๋””์Šคํฌ์˜ ๋ชจ๋“  ์บก์ฒ˜๋Š” ์ •ํ™•ํžˆ ํ•˜๋‚˜์˜ observed ์Šฌ๋กฏ์— ์ƒ‰์ธ๋˜์–ด์•ผ ํ•จ(๊ณ ์•„ ์บก์ฒ˜ ๊ธˆ์ง€) | `validate_watcher_manifest`, `validate_herdr_manifest` | > ๐Ÿ“Œ ์ด ๊ณ„์•ฝ์€ **fixture ๊ฒ€์ฆ ๊ณ„์•ฝ**์ด๋ฉฐ ํ”„๋กœ๋•์…˜ Watcher ๊ตฌํ˜„(Phase 2a)์ด ์•„๋‹™๋‹ˆ๋‹ค. Phase 2a์˜ `watcher.py`๋Š” ์ด ํ‘œ์˜ "๊ด€์ธก ์ฆ๊ฑฐ ์Šคํ‚ค๋งˆ" ์—ด์„ ํ™•์žฅ(event/filesystem ์Šคํ‚ค๋งˆ ์ถ”๊ฐ€)ํ•  ๋•Œ `OBSERVED_EVIDENCE_SCHEMAS`๋ฅผ ํ•จ๊ป˜ ๊ฐฑ์‹ ํ•ด์•ผ ํ•˜๋ฉฐ, ์Šคํ‚ค๋งˆ ์—†๋Š” kind๋ฅผ ๊ด€์ธก์œผ๋กœ ์Šน๊ฒฉํ•˜๋Š” ์ผ์€ ํ—ˆ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. --- ## 4. 6๊ฐœ ๋ฐฑ์—”๋“œ ๊ธฐ๋Šฅ ๋ฐ ์‹ค์ธก ์ฆ๊ฑฐ ํ–‰๋ ฌ (Capability Matrix) ๋ณธ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ง€์›ํ•˜๋Š” 6๊ฐœ ์—์ด์ „ํŠธ ๋ฐฑ์—”๋“œ(`claude`, `agy`, `hermes`, `grok`, `opencode`, `codex`)์˜ ํ˜„ ํ™˜๊ฒฝ ์‹ค์ธก ์ƒํƒœ ๋ฐ Gap ํ˜„ํ™ฉ์ž…๋‹ˆ๋‹ค (๋ชจ๋“  ์Šฌ๋กฏ์€ `tests/fixtures/issue_5/herdr/manifest.json`๊ณผ 100% ์ผ์น˜): | ๋ฐฑ์—”๋“œ | ์„ค์น˜ ๋ฐ”์ด๋„ˆ๋ฆฌ ๊ฒฝ๋กœ | ํ™œ์„ฑ ์„ธ์…˜๋ช… | ์‹ค์ธก ์บก์ฒ˜ ์Šฌ๋กฏ | ๋ฏธ๊ด€์ธก ์Šฌ๋กฏ (Gaps) ๋ฐ ์‚ฌ์œ  | Provenance | |---|---|---|---|---|---| | **`claude`** | `~/.local/bin/claude` | `planner-reviewer-claude-01` | `idle` (`claude/idle.json`) | `working`, `blocked`, `done`, `unknown` (์บก์ฒ˜ ์‹œ์ ์— idle ์ƒํƒœ์˜€์Œ) | `observed` | | **`agy`** | `~/.local/bin/agy` | `creator-agy-01` | `working` (`agy/working.json`), `wait_timeout` (`agy/wait_timeout.json`) | `idle`, `blocked`, `done`, `unknown` (์ž‘์—… ์ˆ˜ํ–‰ ์ค‘์ด๋ฉฐ ๊ถŒํ•œ ๊ฑด๋„ˆ๋›ฐ๊ธฐ ์ ์šฉ) | `observed` | | **`grok`** | `~/.grok/bin/grok` | `reviewer-creator-grok-01` | `idle` (`grok/idle.json`) | `working`, `blocked`, `done`, `unknown` (๋ฆฌ๋ทฐ ๋Œ€๊ธฐ ์ค‘ ์œ ํœด ์ƒํƒœ ์บก์ฒ˜) | `observed` | | **`codex`** | `~/.local/bin/codex` | `planner-reviewer-codex-01` | `done` (`codex/done.json`) | `idle`, `working`, `blocked`, `unknown` (์ด์ „ ํ”Œ๋ž˜๋„ˆ ์ž‘์—… ์™„๋ฃŒ ์งํ›„ ์บก์ฒ˜) | `observed` | | **`hermes`** | `~/.local/bin/hermes` | N/A (๋ฏธ๊ฐ€๋™) | ์—†์Œ | `idle`, `working`, `blocked`, `done`, `unknown` (Herdr ์„œ๋ฒ„์— ํ™œ์„ฑ ์„ธ์…˜ ๋ถ€์žฌ) | `unobserved` | | **`opencode`**| `~/.opencode/bin/opencode` | N/A (๋ฏธ๊ฐ€๋™) | ์—†์Œ | `idle`, `working`, `blocked`, `done`, `unknown` (Herdr ์„œ๋ฒ„์— ํ™œ์„ฑ ์„ธ์…˜ ๋ถ€์žฌ) | `unobserved` | > ๐Ÿ“Œ **์ •์งํ•œ ์ฆ๊ฑฐ ๋ณด๊ณ  ์›์น™ (D3, D6)**: > 1. ์˜ค์ง ์‹ค์ œ๋กœ ์‹คํ–‰ํ•˜์—ฌ ์บก์ฒ˜ํ•œ ์Šฌ๋กฏ๋งŒ `observed`๋กœ ์ธ์ •ํ•˜๋ฉฐ, ๋ชจ์˜ ์‹คํ–‰์ด๋‚˜ ๊ฐ€์ƒ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์„ ๋„ค์ดํ‹ฐ๋ธŒ ์บก์ฒ˜๋กœ ๋‘”๊ฐ‘์‹œํ‚ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. > 2. `hermes`์™€ `opencode`์˜ ๋ฏธ๊ฐ€๋™ ์Šฌ๋กฏ ๋ฐ ๊ธฐ์กด 4๊ฐœ ๋ฐฑ์—”๋“œ์˜ ๋ฏธ๊ด€์ธก ์ƒํƒœ๋Š” ๋ชจ๋‘ ์ •์งํ•˜๊ฒŒ `unobserved gap`์œผ๋กœ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. > 3. Grok ์„ธ์…˜ ๋“ฑ์€ ์บก์ฒ˜ ์‹œ์ ์— `idle`์ด์—ˆ์œผ๋‚˜ ๋ฆฌ๋ทฐ ์‹คํ–‰ ์‹œ `working`์œผ๋กœ ์ „์ด๋˜๋ฏ€๋กœ, ์ •์  ์บก์ฒ˜๊ฐ€ ์—์ด์ „ํŠธ์˜ ์˜๊ตฌ ๋ถˆ๋ณ€ ๋Šฅ๋ ฅ์„ ๋œปํ•˜์ง€ ์•Š์Œ์„ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. --- ## 5. ๋ณตํ•ฉ ์‹œ๋‚˜๋ฆฌ์˜ค ๊ธฐ๋Œ€ ์ •์ฑ… (Composite Expected Policy) Watcher๊ฐ€ ๋‹ค์ค‘ ์ฑ„๋„ ์ฆ๊ฑฐ๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ๋‚ด๋ฆฌ๋Š” ์ตœ์ข… ์™„๋ฃŒ ํŒ์ • ์ •์ฑ…: 1. **`initial_idle`**: ๋””์ŠคํŒจ์น˜ ๊ธฐ๋ก์€ ์žˆ์œผ๋‚˜ ํ•ด๋‹น ์‹œ๋„์˜ ์‹คํ–‰ ์•ต์ปค๊ฐ€ ์—†์œผ๋ฉด โž” **`hold`** (์™„๋ฃŒ ๊ฑฐ๋ถ€). 2. **`tool_running`**: Herdr๋Š” `working`์ด๋‚˜ ํ•˜๋„ค์Šค์—์„œ ๋„๊ตฌ ์‹คํ–‰ ์ค‘์ด๋ฉด โž” **`hold`** (๋Œ€๊ธฐ). 3. **`report_pause_15s`**: ํŒŒ์ผ์ด 15์ดˆ๊ฐ„ ๋ฉˆ์ถ”์—ˆ์œผ๋‚˜ ํ™•์ • ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ์—†์œผ๋ฉด โž” **`hold`** (๋Œ€๊ธฐ). 4. **`finalized_candidate`**: 4์ค‘ ๊ท€์† ์ผ์น˜ + Fresh `idle/done` + ์›์ž์  ๋งค๋‹ˆํŽ˜์ŠคํŠธ/ํ•ด์‹œ ์ผ์น˜ โž” **`candidate_approved`** (CAS ์ปค๋ฐ‹ ํ—ˆ์šฉ). 5. **`blocked_permission`**: Herdr๊ฐ€ `blocked`์ด๋ฉด โž” **`hold_blocked`** (์™„๋ฃŒ ๊ธˆ์ง€). 6. **`transport_disconnected`**: ์†Œ์ผ“ ํ†ต์‹  ์‹คํŒจ ์‹œ โž” **`hold_reconnect`** (์Šค๋ƒ…์ƒท ์žฌ๋™๊ธฐํ™”). 7. **`restarted_generation`**: PID/์„ธ์…˜ ์‹๋ณ„์ž๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ณผ๊ฑฐ ์‹œ๋„์˜ ์‹ ํ˜ธ โž” **`reject_stale`** (์ „์ด ๊ฑฐ๋ถ€).