docs: restore missing contents and align canonical stage names across docs

This commit is contained in:
2026-07-17 13:35:29 +09:00
parent f3c003fdf3
commit c8ed4742d0
11 changed files with 201 additions and 300 deletions
+38 -4
View File
@@ -1,4 +1,6 @@
# gRPC & Protocol Buffers 심화 학습
# 3단계: gRPC 통신 구현 상세 가이드
⬅ [학습 로드맵으로 돌아가기](MANUSCRIPT.md)
이 문서는 `grpccanary` 프로젝트의 **3단계: gRPC 통신 구현**에 대한 이론적 배경, 스키마 명세, 컴파일 기법 및 구체적인 소스코드 분석을 설명합니다.
@@ -28,7 +30,7 @@ gRPC는 HTTP/2를 기반으로 구축된 구글의 고성능 오픈소스 원격
## 2. 인터페이스 명세서 (`protoapi.proto`)
저장소 루트의 [protoapi.proto](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/protoapi.proto) 파일은 의사 난수, 비밀번호, 날짜 데이터를 교환하는 `Random` 서비스를 제공하기 위해 아래와 같이 사양을 선언해 둡니다.
저장소 루트의 [protoapi.proto](../protoapi.proto) 파일은 의사 난수, 비밀번호, 날짜 데이터를 교환하는 `Random` 서비스를 제공하기 위해 아래와 같이 사양을 선언해 둡니다.
```proto
syntax = "proto3";
@@ -99,7 +101,7 @@ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
## 4. 실습 코드 구현 상세 분석
### 4.1 gRPC 서버 구현 ([server.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/grpcentity/server.go))
### 4.1 gRPC 서버 구현 ([server.go](../examples/grpcentity/server.go))
* **구조체 정의**:
```go
@@ -118,7 +120,7 @@ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
server.Serve(listen)
```
### 4.2 gRPC 클라이언트 구현 ([client.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/grpcentity/client.go))
### 4.2 gRPC 클라이언트 구현 ([client.go](../examples/grpcentity/client.go))
* **연결 수립**:
```go
@@ -129,3 +131,35 @@ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
`insecure.NewCredentials()`를 전달하여 TLS를 건너뛴 채 평문으로 빠르고 간단한 로컬 테스트 환경을 구축합니다.
* **원격 호출**:
`client.GetDate()`, `client.GetRandom()`, `client.GetRandomPass()`를 차례로 호출하여 매개변수와 결과를 콘솔로 확인합니다.
### 4.3 gRPC 실습 예제 동작 흐름
예제가 구동되면 서버와 클라이언트 간에 다음과 같은 호출 시퀀스가 순차적으로 실행됩니다:
1. **날짜 조회 (`GetDate`)**: 클라이언트가 서버에 날짜 조회를 요청하고, 서버는 자체의 현재 날짜와 시간 문자열을 포맷하여 반환합니다.
2. **비밀번호 생성 (`GetRandomPass`)**: 클라이언트가 생성할 무작위 비밀번호의 길이(기본 8자)와 난수 생성 시드값을 전달하면, 서버는 지정된 사양의 임의 문자열을 작성해 반환합니다.
3. **난수 생성 (`GetRandom`)**: 서로 다른 위치(Place) 및 시드(Seed) 값을 전달하여 각각 1회씩, 총 2회의 독립적인 난수 생성(의사 난수 정수값) 결과를 반환받아 출력합니다.
---
## 5. 트러블슈팅 (Troubleshooting)
gRPC 서버 및 클라이언트 실습 과정에서 직면할 수 있는 대표적인 문제와 해결 방안입니다.
### 5.1 `listen tcp :8080: bind: address already in use`
* **원인**: 포트 `8080`이 이미 다른 백그라운드 프로세스나 기존 기동된 서버에 의해 점유되어 충돌이 난 상태입니다.
* **해결 방법**:
- `examples/main.go` 의 `port` 변수값과 [server.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/grpcentity/server.go)의 `port` 전역 변수값을 동시에 다른 포트(예: `:9090`)로 변경하고 재시도해야 합니다.
- **주의**: `server.go` 내부의 `ServerRun(addr string)` 함수는 외부 진입점으로부터 인자 `addr`을 인가받지만, 실제 포트 리슨 코드에서는 이를 무시하고 패키지 전역 변수 `port = ":8080"`를 직접 읽어 처리하도록 하드코딩되어 있습니다. 따라서 정상적으로 포트를 바꾸기 위해서는 반드시 `server.go` 내부 전역 변수인 `port` 값을 수정해 주어야 포트 바인딩이 성공합니다.
---
## 6. 다음 단계 (Next Steps)
gRPC 통신 방식을 한층 더 깊이 탐구해 보려면 다음과 같은 후속 실습을 추천합니다:
1. **메시지 스펙 확장**: 루트 디렉토리의 [protoapi.proto](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/protoapi.proto)에 새로운 필드를 추가하거나 메서드를 정의한 뒤, Stub을 재컴파일([examples/grpcentity/README.md](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/grpcentity/README.md) 컴파일 가이드 참고)해 보십시오.
2. **인터페이스 모듈 연계**: 생성된 gRPC 클라이언트 및 서버 stub 인터페이스를 활용하여, 향후 분산 AIoT 환경에서의 센서 데이터 수집이나 에이전트 간 제어 메시지 전송 로직을 설계해 보십시오.
---
## 7. 참고 자료
* [gRPC와 REST의 차이점 (AWS)](https://aws.amazon.com/ko/compare/the-difference-between-grpc-and-rest/)
+5 -3
View File
@@ -1,6 +1,8 @@
# HTTP 프로토콜 & Gin 웹 프레임워크 학습
# 2단계: HTTP & Gin 웹 서버 상세 가이드
이 문서는 `grpccanary` 프로젝트의 **2단계: HTTP 서버 예제**에 대한 이론적 배경과 코드 구조를 설명합니다.
⬅ [학습 로드맵으로 돌아가기](MANUSCRIPT.md)
이 문서는 `grpccanary` 프로젝트의 **2단계: HTTP & Gin 웹 서버**에 대한 이론적 배경과 코드 구조를 설명합니다.
HTTP(Hypertext Transfer Protocol)는 클라이언트와 웹 서버가 웹에서 리소스를 교환하기 위해 정의한 규약입니다. Go 언어에서는 전통적인 `net/http` 표준 라이브러리 외에도, 성능이 우수하고 사용하기 쉬운 서드파티 웹 프레임워크인 **Gin-gonic**을 주로 사용하여 RESTful API를 신속하게 설계합니다.
@@ -27,7 +29,7 @@ Go 진영에서 대표적으로 사랑받는 웹 프레임워크 중 하나로,
## 3. 실습 코드 분석 (`examples/httpentity/server.go`)
저장소의 [server.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/httpentity/server.go) 파일에는 Gin 라우터를 구성하고 API 서버와 정적 웹 서빙을 혼합하여 라우팅을 우회 처리하는 설계 패턴이 주석 상태로 존재합니다.
저장소의 [server.go](../examples/httpentity/server.go) 파일에는 Gin 라우터를 구성하고 API 서버와 정적 웹 서빙을 혼합하여 라우팅을 우회 처리하는 설계 패턴이 주석 상태로 존재합니다.
### 3.1 라우터 엔진 분리 및 통합 핸들링
```go
+4 -2
View File
@@ -1,4 +1,6 @@
# Go 언어와 JSON 다루기
# 1단계: JSON 데이터 다루기 상세 가이드
⬅ [학습 로드맵으로 돌아가기](MANUSCRIPT.md)
이 문서는 `grpccanary` 프로젝트의 **1단계: JSON 데이터 다루기**에 대한 이론적 배경과 코드 예시를 설명합니다.
@@ -21,7 +23,7 @@ Go 구조체로 JSON 데이터를 다룰 때 핵심적인 과정입니다.
## 2. 실습 코드 분석 (`examples/jsonexample/json_parser.go`)
저장소의 [json_parser.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/jsonexample/json_parser.go) 파일은 (1) `map[string]interface{}`와의 직렬화 및 (2) 구조체(`Person`)를 이용한 매핑 방식을 모두 다룹니다.
저장소의 [json_parser.go](../examples/jsonexample/json_parser.go) 파일은 (1) `map[string]interface{}`와의 직렬화 및 (2) 구조체(`Person`)를 이용한 매핑 방식을 모두 다룹니다.
### 2.1 구조체 태그(Struct Tag) 정의
```go
+6 -6
View File
@@ -10,9 +10,9 @@
| 단계 | 실습 주제 | 심화 학습 가이드 링크 |
| :--- | :--- | :--- |
| **1단계** | JSON 데이터 직렬화 및 구조체 매핑 | [JSON 상세 가이드 (JSON.md)](JSON.md) |
| **2단계** | HTTP 프로토콜 및 Gin 웹 서버 라우팅 | [HTTP 상세 가이드 (HTTP.md)](HTTP.md) |
| **3단계** | gRPC 통신, Protobuf 컴파일 및 서버/클라이언트 구현 | [gRPC 상세 가이드 (GRPC.md)](GRPC.md) |
| **1단계** | JSON 데이터 다루기 | [JSON 상세 가이드 (JSON.md)](JSON.md) |
| **2단계** | HTTP & Gin 웹 서버 | [HTTP 상세 가이드 (HTTP.md)](HTTP.md) |
| **3단계** | gRPC 통신 구현 | [gRPC 상세 가이드 (GRPC.md)](GRPC.md) |
---
@@ -20,7 +20,7 @@
JSON(JavaScript Object Notation)은 데이터를 구조화하여 전송하기 위해 널리 사용되는 가볍고 읽기 쉬운 텍스트 기반의 데이터 포맷입니다. 대부분의 현대 프로그래밍 언어에서 기본적으로 지원하며, 특히 HTTP 기반 REST API의 데이터 교환 규격으로 오랫동안 사랑받아 왔습니다.
👉 **[Go 언어와 JSON 다루기 상세 학습 가이드 (JSON.md)](JSON.md)**
👉 **[1단계: JSON 데이터 다루기 상세 가이드 (JSON.md)](JSON.md)**
---
@@ -31,7 +31,7 @@ HTTP(Hypertext Transfer Protocol)는 웹 브라우저와 웹 서버 간에 데
### `gin-gonic`을 이용한 http 서버 구현하기
Go 언어에서는 전통적인 `net/http` 표준 라이브러리 외에도, 성능이 뛰어나고 라우팅 기능이 강력한 `gin-gonic/gin` 프레임워크를 널리 활용하여 RESTful 웹 API 서버를 구축합니다.
👉 **[HTTP 프로토콜 & Gin 웹 프레임워크 상세 학습 가이드 (HTTP.md)](HTTP.md)**
👉 **[2단계: HTTP & Gin 웹 서버 상세 가이드 (HTTP.md)](HTTP.md)**
---
@@ -56,4 +56,4 @@ REST와 JSON은 단순하고 사람이 읽기 쉽다는 훌륭한 장점이 있
* **바이너리 프로토콜**: 텍스트가 아닌 이진 데이터 형식을 사용하여 통신 속도가 JSON 방식에 비해 훨씬 빠르고 가볍습니다.
* **HTTP/2 기반**: 하나의 커넥션을 다중화(Multiplexing)하여 사용하므로 네트워크 리소스 효율성이 매우 높습니다.
👉 **[gRPC & Protocol Buffers 상세 학습 가이드 (GRPC.md)](GRPC.md)**
👉 **[3단계: gRPC 통신 구현 상세 가이드 (GRPC.md)](GRPC.md)**
-146
View File
@@ -1,146 +0,0 @@
# Go 언어와 JSON 다루기
Go 언어에서 JSON 데이터를 다루는 방법을 배워봅시다.
Go 표준 라이브러리에는 JSON 데이터를 처리하기 위한 `encoding/json` 패키지가 포함되어 있습니다. Go는 구조체 태그(Struct Tag)를 사용하여 Go 구조체와 JSON 필드를 손쉽게 매핑할 수 있는 기능을 제공합니다. 이 태그는 Go 구조체를 JSON으로 변환하거나, JSON을 Go 구조체로 변환하는 과정을 제어합니다. 이 과정을 각각 Marshaling과 Unmarshaling이라고 부릅니다.
## `Marshal()`과 `Unmarshal()` 이해하기
**Marshaling**과 **Unmarshaling**은 Go 구조체로 JSON 데이터를 다룰 때 핵심적인 과정입니다.
- **Marshaling**: Go 구조체(메모리 상의 데이터)를 JSON 문자열(텍스트 데이터)로 변환하는 과정입니다. 주로 API 응답으로 JSON을 보내거나, 데이터를 파일로 저장할 때 사용됩니다.
- **Unmarshaling**: JSON 문자열을 Go 구조체로 변환하는 과정입니다. 주로 외부 API로부터 받은 JSON 데이터를 다루거나 파일에서 데이터를 읽어올 때 사용됩니다.
> **가장 흔히 겪는 문제**: JSON과 Go 구조체 간 변환 시 가장 흔한 버그는 구조체의 필드명을 소문자로 시작하여 발생하는 문제입니다. `encoding/json` 패키지가 구조체의 필드에 접근하려면, 해당 필드는 **반드시 대문자로 시작해야 합니다 (Exported field)**. Marshaling 또는 Unmarshaling이 제대로 동작하지 않는다면, 가장 먼저 구조체 필드명이 대문자로 시작하는지 확인해 보세요.
# 코딩 예제
아래 `encodeDecode.go` 코드는 간단한 예제를 통해 JSON 레코드의 Marshaling과 Unmarshaling 과정을 보여줍니다.
```go
package main
import (
"encoding/json"
"fmt"
)
// UseAll 구조체는 JSON 데이터와 매핑됩니다.
type UseAll struct {
Name string `json:"username"`
Surname string `json:"surname"`
Year int `json:"created"`
}
```
구조체 필드 옆의 `` `json:"..."` `` 부분을 **구조체 태그**라고 부릅니다. 이 태그는 각 필드가 JSON 데이터에서 어떤 키(key)와 매핑되는지를 명시합니다.
- `Name` 필드는 JSON에서 `username` 키와 매핑됩니다.
- `Surname` 필드는 `surname` 키와 매핑됩니다.
- `Year` 필드는 `created` 키와 매핑됩니다.
이 태그 정보는 Marshaling과 Unmarshaling 과정에서 사용되며, 이 외에는 `UseAll`을 일반적인 Go 구조체처럼 사용하면 됩니다.
```go
func main() {
// Marshaling할 구조체 인스턴스 생성
useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}
// Go 구조체를 JSON 바이트 슬라이스로 Marshaling합니다.
t, err := json.Marshal(&useall)
}
```
`json.Marshal()` 함수는 Go 데이터(주로 구조체 포인터)를 인자로 받아, JSON으로 인코딩된 `[]byte`와 `error`를 반환합니다.
```go
if err != nil {
fmt.Println(err)
} else {
// t는 []byte 타입이므로, 출력을 위해 문자열로 변환합니다.
fmt.Printf("Value %s\n", t)
}
// Unmarshaling할 JSON 문자열 데이터
str := `{"username": "M.", "surname": "Ts", "created":2020}`
```
JSON 데이터는 보통 문자열 형태로 다루어집니다.
```go
// json.Unmarshal 함수는 바이트 슬라이스를 인자로 받으므로, 문자열을 변환합니다.
jsonRecord := []byte(str)
```
`json.Unmarshal()` 함수는 바이트 슬라이스 (`[]byte`)를 인자로 받기 때문에, 먼저 JSON 문자열을 `[]byte` 타입으로 변환해야 합니다.
```go
// 변환된 JSON 데이터를 담을 구조체 변수를 선언합니다.
var temp UseAll
// JSON 바이트 슬라이스를 Go 구조체로 Unmarshaling합니다.
err = json.Unmarshal(jsonRecord, &temp)
```
`json.Unmarshal()` 함수는 JSON 데이터가 담긴 바이트 슬라이스와, 데이터를 채워 넣을 Go 구조체 변수의 **포인터**를 인자로 받습니다. 포인터를 사용하는 이유는 함수가 `temp` 변수의 값을 직접 수정해야 하기 때문입니다. 함수가 종료된 후에도 변경된 값이 유지되려면 이처럼 변수의 메모리 주소를 전달해야 합니다.
```go
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Data type: %T with value %v\n", temp, temp)
}
```
`encodeDecode.go`를 실행하면 다음과 같은 결과가 출력됩니다.
```bash
# go run encodeDecode.go
Value {"username":"Mike","surname":"Tsoukalos","created":2021}
Data type: main.UseAll with value {M. Ts 2020}
```
---
### 전체 예제 코드
```go
package main
import (
"encoding/json"
"fmt"
)
type UseAll struct {
Name string `json:"username"`
Surname string `json:"surname"`
Year int `json:"created"`
}
func main() {
useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}
// Marshaling: Go 구조체 -> JSON
t, err := json.Marshal(&useall)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Value %s\n", t)
}
// Unmarshaling할 JSON 문자열
str := `{"username": "M.", "surname": "Ts", "created":2020}`
jsonRecord := []byte(str)
// 결과를 저장할 구조체 변수
var temp UseAll
// Unmarshaling: JSON -> Go 구조체
err = json.Unmarshal(jsonRecord, &temp)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Data type: %T with value %v\n", temp, temp)
}
}
```
-129
View File
@@ -1,129 +0,0 @@
# Working with JSON
Lets learn how to work with JSON data.
The Go standard library includes `encoding/json`, which is for working with JSON data. Additionally, Go allows us to add support for JSON fields in Go structures using tags. Tags control the encoding and decoding of JSON records to and from Go structures. But first, we should talk about marshaling and unmarshaling JSON records.
## Using `Marshal()` and `Unmarshal()`
Both the marshaling and unmarshaling of JSON data are important procedures for working with JSON data using Go structures. **Marshaling** is the process of converting a Go structure into a JSON record. We usually want that for transferring JSON data via computer networks or for saving it on disk. **Unmarshaling** is the process of converting a JSON record given as a byte slice into a Go structure. We usually want that when receiving JSON data via computer networks or when loading JSON data from disk files.
> **Note:** The number one bug when converting JSON records into Go structures and vice versa is not making the required fields of our Go structures exported. When we have issues with marshaling and unmarshaling, begin our debugging process from there.
# Coding example
The code in `encodeDecode.go` illustrates both the marshaling and unmarshaling of JSON records using hardcoded data for simplicity:
```go
package main
import (
"encoding/json"
"fmt"
)
type UseAll struct {
Name string `json:"username"`
Surname string `json:"surname"`
Year int `json:"created"`
}
```
What the previous metadata tells us is that the `Name` field of the `UseAll` structure is translated to `username` in the JSON record, and vice versa; the `Surname` field is translated to `surname`, and vice versa; and the `Year` structure field is translated to `created` in the JSON record, and vice versa. This information has to do with the marshaling and unmarshaling of JSON data. Other than this, we treat and use `UseAll` as a regular Go structure.
```go
func main() {
useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}
// Regular Structure
// Encoding JSON data -> Convert Go Structure to JSON record with fields
t, err := json.Marshal(&useall)
}
```
The `json.Marshal()` function requires a pointer to a structure variable—its real data type is an empty interface variable—and returns a byte slice with the encoded information and an `error` variable.
```go
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Value %s\n", t)
}
// Decoding JSON data given as a string
str := `{"username": "M.", "surname": "Ts", "created":2020}`
```
JSON data usually comes as a string.
```go
// Convert string into a byte slice
jsonRecord := []byte(str)
```
However, as `json.Unmarshal()` requires a byte slice, we need to convert that string into a byte slice before passing it to `json.Unmarshal()`.
```go
// Create a structure variable to store the result
temp := UseAll{}
err = json.Unmarshal(jsonRecord, &temp)
```
The `json.Unmarshal()` function requires the byte slice with the JSON record and a pointer to the Go structure variable that is going to store the JSON record and returns an `error` variable.
```go
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Data type: %T with value %v\n", temp, temp)
}
```
Running `encodeDecode.go` produces the next output:
```bash
# go run encodeDecode.go
Value {"username":"Mike","surname":"Tsoukalos","created":2021}
Data type: main.UseAll with value {M. Ts 2020}
```
```go
package main
import (
"encoding/json"
"fmt"
)
type UseAll struct {
Name string `json:"username"`
Surname string `json:"surname"`
Year int `json:"created"`
}
func main() {
useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}
// Regular Structure
// Encoding JSON data -> Convert Go Structure to JSON record with fields
t, err := json.Marshal(&useall)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Value %s\n", t)
}
// Decoding JSON data given as a string
str := `{"username": "M.", "surname": "Ts", "created":2020}`
// Convert string into a byte slice
jsonRecord := []byte(str)
// Create a structure variable to store the result
temp := UseAll{}
err = json.Unmarshal(jsonRecord, &temp)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Data type: %T with value %v\n", temp, temp)
}
}
```
@@ -0,0 +1,77 @@
# [보고서] MAM 위임 도구의 역할(Role) 지정 옵션 누락 이슈 분석
본 문서는 멀티 에이전트 오케스트레이션 프레임워크(`multi-agent-mux`)의 핵심 CLI 도구인 `multi-agent-mux-delegate-job`에서 세션의 역할(Role)을 지정할 수 있는 옵션이 누락되어 발생하는 정합성 충돌 문제와 이에 대한 원인 분석 및 해결 방안을 정의합니다.
---
## 1. 문제가 발생한 정확한 상황 (Context)
프로젝트 개발을 오케스트레이션하는 과정에서 아래와 같은 에이전트 간 역할 분담을 적용하고자 했습니다.
* **개발 팀장 (Antigravity)**: 실제 저장소의 문서 수정 및 구현 진행 (**Worker/Implementer**)
* **리뷰 에이전트 (Claude)**: 문서 구조의 설계 및 계획안 수립 (**Planner**)
이 분담에 따라 Claude 세션(`canary-projects-grpccanary-creator-claude`)에 "문서 모듈화 계획 및 체크리스트 작성" 작업을 위임하기 위해 `multi-agent-mux-delegate-job` 도구로 비동기 작업을 요청했습니다.
그러나 자동 생성된 잡 지시서인 `.mam/jobs/<job_id>/brief.md` 파일의 메타데이터에 다음과 같이 **구현자의 역할이 `Worker`로 강제 지정**되어 나가는 상황이 발생했습니다:
```markdown
# 📋 Brief: Job ed31b5fb Delegation
- **Job ID**: ed31b5fb
- **Target Agent**: claude (session: tmux:canary-projects-grpccanary-creator-claude)
- **Role**: Worker <-- [이슈 발생 지점: Planner가 아닌 Worker로 강제 지정됨]
- **Timeout**: 3600 s (Idle: 120 s)
```
이는 프로젝트 협업 규칙(`.agents/MULTI_AGENT_RULES.ko.md`)에 명시된 **"에이전트 역할 범위 준수 원칙(Role Suitability Check)"**에 위배되며, `claude`가 문서 작성이 아닌 파일 직접 수정을 시도할 위험이 있는 정합성 모순을 유발합니다.
---
## 2. 문제 사유 (Root Cause)
이 문제의 근본적인 기술적 원인은 **CLI 인수 파싱 로직 및 지시서(Brief) 생성 템플릿의 하드코딩**에 있습니다.
1. **CLI 옵션 설계 누락**:
* `multi-agent-mux-delegate-job submit` 명령어의 헬프 스펙을 확인한 결과, `--agent`, `--agent-session`, `--prompt` 등의 인수는 정의되어 있으나, 작업의 논리적 성격을 조율하는 **`--role <role_name>` 파라미터가 구현되어 있지 않습니다**.
2. **템플릿 내부의 상수 고정**:
* API를 통해 비동기 잡이 수임될 때 생성되는 `brief.md` 파일과 잡 레지스트리 JSON의 생성기 로직 내부에 `Role` 값이 **`Worker` 문자열 상수로 하드코딩**되어 동작하고 있습니다. 이로 인해 어떤 에이전트에 어떤 종류의 명령을 위임하더라도 메타데이터상으로는 항상 `Worker`로 바인딩됩니다.
---
## 3. 문제 해결 방법 (Remediation & Workarounds)
### 3.1 단기적 우회 방법 (Workaround)
프레임워크 CLI 소스코드를 수정하기 어려운 제한적 상황에서는 **프롬프트 페이로드(Prompt Payload) 하드닝** 기법을 사용하여 에이전트의 오작동을 차단합니다.
* **해결 원리**: brief.md의 메타데이터상 `Role: Worker` 지정을 덮어쓸 수 있도록, 프롬프트 문맥 내부에 **"너의 역할은 실제 문서를 수정하지 않고 계획만 수립하는 Planner이다. 절대 문서를 직접 수정하지 말라"**는 강력한 지시 제약(System-level Rule Override)을 포함하여 송신합니다.
* **효과**: AI 에이전트는 메타데이터보다 프롬프트 지시어의 행위 제약을 우선 순위로 받아들이므로, 의도한 대로 설계서 및 계획안만 수립하는 Planner 동작을 정상 수행하게 됩니다.
### 3.2 근본적인 해결 방법 (Remediation)
프레임워크의 CLI 래퍼인 `multi-agent-mux-delegate-job` 파일의 파싱 로직 및 brief.md 빌더 로직을 다음과 같이 수정합니다.
#### 1단계: CLI 인수 파서 수정 (`submit` 옵션 추가)
스크립트의 인수 파싱 영역에 `--role` 파라미터를 식별할 수 있는 변수 및 분기 로직을 선언합니다.
```bash
# 옵션 분석 루프 예시
while [[ $# -gt 0 ]]; do
case $1 in
--role)
DELEGATE_ROLE="$2"
shift 2
;;
# ... 기존 옵션 파싱 ...
esac
done
# 기본값 정의
DELEGATE_ROLE="${DELEGATE_ROLE:-Worker}"
```
#### 2단계: `brief.md` 생성 템플릿 연동
잡 디렉토리 내에 `brief.md`를 기입하여 내보내는 빌더 영역(Python 혹은 쉘 스크립트 에코 영역)을 다음과 같이 동적 변수와 연결합니다.
```diff
- echo "- **Role**: Worker" >> "$BRIEF_PATH"
+ echo "- **Role**: ${DELEGATE_ROLE}" >> "$BRIEF_PATH"
```
#### 3단계: 잡 레지스트리 JSON 메타데이터 갱신
동일하게 생성되는 `.mam/jobs/<job_id>.json` 파일 등의 메타데이터 생성 객체 내에 `role: DELEGATE_ROLE` 매핑 키를 추가하여, 타 모니터링 도구(예: `reconcile.sh``status.sh`)에서도 해당 에이전트의 잡 실행 역할을 정확하게 대시보드에 모니터링할 수 있도록 보완합니다.