<!-- task=01_pool_schema plan=0 tag=API -->
# Plan - API

## 이 파일을 읽는 구현 에이전트에게

구현이 끝났다고 보고하기 전에 반드시 `CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채운다. 검증 명령을 실행하고 실제 stdout/stderr를 남기며, active plan/review 파일은 그대로 둔 상태에서 review-ready만 보고한다. 선택된 SDD 결정이나 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 구현을 차단할 때만 review stub의 `사용자 리뷰 요청` 섹션을 채우고 중단한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하거나 `USER_REVIEW.md`, `complete.log`, archive log를 작성하지 않는다. 환경/secret/서비스 준비, 일반 범위 조정, 검증 증거 공백은 그 자체로 사용자 리뷰 요청이 아니다.

## 배경

현재 Edge OpenAI route는 `openai.model_routes`의 단일 `adapter + target` mapping에 머물러 있다. Milestone의 `model-pool` epic은 외부 model key를 `models[].id`로 고정하고, provider id별 served model map과 `nodes[].providers[]` 후보 schema를 먼저 확정해야 이후 target rewrite와 load-ratio selection을 안전하게 구현할 수 있다. 이 계획은 런타임 selection을 바꾸기 전 config/proto/status 계약과 validation foundation을 만든다.

## 사용자 리뷰 요청 흐름

선택된 SDD 결정 또는 선택된 Milestone lock 결정이 실제 구현을 차단할 때만 `CODE_REVIEW-local-G06.md`의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 근거를 채운다. 구현 중 직접 사용자에게 묻거나 채팅 선택지를 만들지 않는다. code-review 단계가 사용자 리뷰 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
- Task ids:
  - `alias-contract`: `models[].id` canonical routing key와 `models[].providers` provider id -> served model map 검증
  - `candidate-schema`: `nodes[].providers[]` provider id/type/category/served models/health/capacity/in-flight/queued/coarse lifecycle 표현
- Completion mode: check-on-pass

## Spec Targets

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
- Acceptance Scenarios:
  - `S01`: model alias provider pool contract
  - `S02`: provider candidate schema
- Evidence Map:
  - `S01`, `S02`는 이 task directory의 `complete.log`에 Roadmap Completion과 Spec Completion 근거가 있어야 한다.

## 분석 결과

- 읽은 규칙/계약: `agent-ops/rules/project/rules.md`, `agent-ops/rules/common/rules-roadmap.md`, `agent-ops/skills/common/router.md`, `agent-ops/skills/common/plan/SKILL.md`, `agent-ops/skills/common/roadmap-sdd/SKILL.md`, `agent-ops/skills/common/update-roadmap/SKILL.md`, `agent-contract/index.md`, `agent-contract/provided/openai-compatible-api.md`.
- 읽은 Roadmap/SDD: `agent-roadmap/current.md`, active Phase, active Milestone, 승인된 SDD. SDD는 `[승인됨]`, SDD 잠금은 `해제`, active `USER_REVIEW.md`는 없다.
- 읽은 domain/test rules: edge, platform-common, testing, `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/platform-common-smoke.md`.
- 읽은 source: `packages/go/config/config.go`, `apps/edge/internal/openai/chat_handler.go`, `apps/edge/internal/openai/responses_handler.go`, `apps/edge/internal/openai/routes.go`, `apps/edge/internal/openai/server.go`, `apps/edge/internal/service/run_dispatch.go`, `apps/edge/internal/service/model_queue.go`, `apps/edge/internal/service/status_provider.go`, `proto/iop/runtime.proto`, `configs/edge.yaml`, `apps/edge/README.md`.
- 읽은 tests: `packages/go/config/config_test.go`, `apps/edge/internal/openai/server_test.go`, `apps/edge/internal/service/model_queue_test.go`, `apps/edge/internal/service/service_internal_test.go`, `apps/edge/internal/service/service_test.go`, `apps/edge/internal/service/status_provider_test.go`.
- 기존 커버리지: legacy `openai.model_routes`, `/v1/models` catalog, provider instance capacity/queue policy, model queue admission/release/status는 테스트가 있다. `models[]` provider map validation, `nodes[].providers[]` catalog schema, provider id reference validation, category/lifecycle/status field projection은 테스트가 없다.
- 분할 판단: `model-pool` epic은 config/proto/status 계약과 dispatch policy가 서로 의존하는 다중 단계 작업이다. 이 plan은 predecessor 없는 `01_pool_schema` foundation이다. 후속 `02+01_edge_dispatch`는 이 task가 `complete.log`로 PASS 된 뒤 구현해야 한다.
- 심볼 참조: 기존 symbol rename/remove는 계획하지 않는다. `OpenAIRouteEntry`, `EdgeOpenAIConf.Models`, `ProviderSnapshot`은 compatibility를 유지하면서 새 field/type을 추가한다.
- dependency manifest: 새 외부 package는 추가하지 않는다. protobuf 변경 시 repo 기존 `make proto` 흐름을 사용한다.
- pre-check compile: `EdgeOpenAIConf.Models []string`은 legacy openai catalog로 남기고, Milestone의 새 `models[]`는 top-level Edge config field로 추가해야 decode conflict를 피할 수 있다.
- build grade: local-G06. Go config/proto/status 계약과 다수 테스트를 건드리지만 외부 서비스, secret, 배포 상태 변경은 없다.

## 구현 체크리스트

- [ ] `packages/go/config/config.go`에 top-level `models[]` catalog type을 추가하고 `models[].id`, `models[].providers` provider id -> served model map validation을 구현한다.
- [ ] `packages/go/config/config.go`에 `nodes[].providers[]` config type과 provider id/type/category/served models/capacity/health/coarse lifecycle validation을 추가한다.
- [ ] `proto/iop/runtime.proto`와 생성 코드를 갱신해 provider catalog/status surface가 provider id, type/category, served models, health, capacity, in-flight, queued, load ratio, coarse lifecycle capability를 표현하게 한다.
- [ ] Edge status/provider snapshot 조립 코드에 새 provider catalog/status field를 채우되 legacy adapter snapshot 소비자를 깨지 않게 compatibility field를 유지한다.
- [ ] `configs/edge.yaml`와 `apps/edge/README.md`에 실제 지원되는 `models[]`/`nodes[].providers[]` 예시와 legacy `openai.model_routes` migration 문구를 갱신한다.
- [ ] config/status/openai/service 테스트에 provider pool schema happy path와 invalid duplicate/missing provider/missing served model/invalid category 회귀를 추가한다.
- [ ] 중간 및 최종 검증 명령을 실행하고 결과를 `CODE_REVIEW-local-G06.md`에 붙여 넣는다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## API-1 - `models[]` alias contract

### 문제

`packages/go/config/config.go`의 `EdgeOpenAIConf`는 legacy `models []string`과 `model_routes`만 갖고 있고, `OpenAIRouteEntry`도 외부 model id를 하나의 `adapter + target`으로만 연결한다. 따라서 `qwen3.6:35b` 같은 외부 key가 provider pool canonical key인지, provider id별 served model map이 유효한지 검증할 곳이 없다.

### 변경

Top-level Edge config에 `Models []ModelCatalogEntry`를 추가한다. `ModelCatalogEntry`는 최소 `ID string`, `Providers map[string]string`, 선택적 display/metadata field를 갖는다. `openai.models []string`은 기존 `/v1/models` compat field로 남겨 decode와 legacy tests를 유지한다.

Validation은 `LoadEdge` 흐름에 붙인다.

- `models[].id` trim 후 non-empty, unique.
- `models[].providers` non-empty.
- provider id와 served model name은 trim 후 non-empty.
- provider id는 `nodes[].providers[].id` 중 하나로 resolve되어야 한다.
- served model name은 해당 provider의 served model list에 포함되어야 한다.

### 중간 검증

```bash
go test ./packages/go/config -run 'TestLoadEdge_ModelProviderPool|TestLoadEdge_OpenAI' -count=1
```

기대 결과: 새 provider pool schema validation tests와 기존 OpenAI config tests가 모두 통과한다.

## API-2 - `nodes[].providers[]` candidate schema

### 문제

기존 Node adapter instance 설정은 `openai_compat_instances`, `vllm_instances`, `ollama_instances`처럼 adapter별 실행 설정에 묶여 있고 provider id/category/served model list/lifecycle capability를 공통 schema로 표현하지 않는다. `proto/iop/runtime.proto`의 `ProviderSnapshot`도 `adapter/status/capacity/in_flight/queued`만 있으므로 operations catalog가 provider 후보를 비교할 수 없다.

### 변경

`NodeDefinition`에 `Providers []NodeProviderConf`를 추가한다. `NodeProviderConf`는 최소 `ID`, `Type`, `Category`, `Adapter`, `Models`, `Health`, `Capacity`, `MaxQueue`, `QueueTimeout`, `LifecycleCapabilities`를 포함한다. Category MVP 값은 Milestone 기준대로 `api`, `cli`, `local_inference`만 허용한다. Runtime in-flight/queued/load ratio는 config 입력값이 아니라 Edge status에서 계산해 projection한다.

`ProviderSnapshot` protobuf에는 compatibility를 위해 기존 field를 유지하고, 새 field를 뒤에 추가한다. 생성 코드는 `make proto`로 갱신한다.

### 중간 검증

```bash
make proto
go test ./packages/go/config ./apps/edge/internal/service -run 'TestLoadEdge_ModelProviderPool|TestProvider|TestModelQueue' -count=1
```

기대 결과: protobuf 생성물이 최신이고 config/provider snapshot 관련 테스트가 통과한다.

## API-3 - Docs and compatibility examples

### 문제

`configs/edge.yaml`와 `apps/edge/README.md`는 현재 legacy route와 vLLM OpenAI-compatible provider setup만 설명한다. 실제 schema 구현 뒤에는 operator가 `models[]`/`nodes[].providers[]`를 어떻게 쓰고 legacy route가 어떤 migration input인지 확인할 수 있어야 한다.

### 변경

설정 예시는 실제 구현된 field 이름만 사용한다. 1:1 legacy route가 단일 provider pool entry로 이동하는 예시, provider id와 served model list의 관계, Edge target rewrite 책임, legacy `openai.model_routes` fallback 규칙을 함께 적는다.

### 중간 검증

```bash
rg --sort path -n 'models\\[\\]|nodes\\[\\]\\.providers|model_routes' apps/edge/README.md configs/edge.yaml
```

기대 결과: README와 예시 YAML이 새 contract와 legacy compatibility를 모두 언급한다.

## 최종 검증

```bash
make proto
go test ./packages/go/config ./apps/edge/internal/openai ./apps/edge/internal/service -count=1
git diff --check
```

기대 결과: protobuf 생성물이 최신이고, 관련 Go tests가 통과하며, whitespace error가 없다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
