<!-- task=m-multi-edge-operations/03+01,02_control_plane_fleet_api plan=0 tag=API -->
# Plan - Control Plane Fleet API

## 작업 지시

Control Plane에 multi-edge command API, command result/event relay, fleet status summary API를 구현한다. Control Plane은 attachable control layer이므로 Edge 원본 데이터 저장소를 만들지 않는다.

## 선행 의존성

- 필요: `agent-task/m-multi-edge-operations/01_native_contract/complete.log`
- 필요: `agent-task/m-multi-edge-operations/02+01_edge_fleet_ops/complete.log`
- 현재 상태: missing. 구현 시작 전 선행 plan 완료 로그를 확인한다.

## 분석 결과

현재 HTTP API는 Edge 목록, 상세, 이벤트, live status 조회만 제공한다.

```go
// apps/control-plane/cmd/control-plane/main.go:182
registryMux.HandleFunc("/edges", app.handleEdges)
registryMux.HandleFunc("/edges/", app.handleEdgeDetail)
```

Edge server는 active client에 status request만 보낼 수 있다.

```go
// apps/control-plane/internal/wire/edge_server.go:192
func (s *EdgeServer) RequestStatus(ctx context.Context, edgeID string) (*iopcontrolv1.EdgeStatusResponse, error) {
```

Registry는 연결 상태와 최근 node event만 보관한다.

```go
// apps/control-plane/internal/wire/edge_registry.go:12
type EdgeRegistry struct {
	mu     sync.RWMutex
	edges  map[string]EdgeConnection
	events map[string][]EdgeNodeEventRecord
```

## 구현 체크리스트

1. `EdgeServer`에 command request 전송과 response/event 수신 경로를 추가한다.
2. `EdgeRegistry`에는 connection view, command request/result, bounded event/audit만 저장한다.
3. HTTP API를 추가한다.
   - `GET /edges/{id}/status`
   - `GET /edges/{id}/operations`
   - `POST /edges/{id}/commands`
   - `GET /fleet/status`
   - 필요한 경우 `POST /fleet/commands`
4. API 응답은 Edge-owned source summary만 포함하고 설정/registry/runtime 원본 복제를 하지 않는다.
5. 여러 Edge가 연결된 상황에서 command routing이 target Edge로만 가는지 테스트한다.
6. audit/event 저장은 bounded로 유지한다.

## 세부 설계

### Command API

- HTTP request는 native command request로 변환한다.
- `edge_id`, `command_id`, `operation`, `status`, `summary`, `created_at`, `updated_at` 중심으로 응답한다.
- command detail payload는 요청/결과 요약까지만 보관한다.

### Fleet Status API

- fleet 전체 목록은 연결/health와 Edge-provided capability summary를 합친 view이다.
- Edge 상태 조회 실패는 해당 Edge의 degraded/error view로 표현한다.
- Control Plane 단독 판단으로 agent lifecycle 원본 상태를 만들지 않는다.

### Tests

- 두 Edge 연결 후 각 Edge로 command가 분리 routing된다.
- offline Edge command는 적절한 error를 반환한다.
- command result/event가 bounded audit로 저장된다.
- API 응답에 Edge config secret과 runtime detail 원본이 포함되지 않는다.
- 기존 `/edges`, `/edges/{id}/status`, `/edges/{id}/events` 회귀 테스트가 통과한다.

## 검증

- `go test ./apps/control-plane/internal/wire ./apps/control-plane/cmd/control-plane`
- 필요한 경우 `go test ./apps/control-plane/...`
- `make test-control-plane-edge-wire`가 존재하면 실행한다.

## 완료 조건

- Control Plane이 여러 Edge에 command/control 요청을 보낼 수 있다.
- Edge status와 command result/event가 Control Plane API로 조회된다.
- 저장 모델은 connection/control/audit view로 제한된다.
