# Milestone: Node Provider-First Config Surface

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`

## 목표

Node 설정 표면을 `providers[]` resource list 중심으로 재정렬한다.
운영자는 한 Node가 제공하는 CLI, Ollama, OpenAI-compatible, vLLM/Lemonade/SGLang 계열 resource를 `providers[]`에 나열하고, `type`과 provider별 필드만으로 실행 방식을 선언한다.
내부 adapter registry와 `adapter + target` 실행 계약은 유지하되, 사용자 config에서 `adapters`와 `providers`를 동시에 맞춰야 하는 중복 source of truth를 제거한다.

## 상태

[완료]

## 구현 잠금

- 상태: 해제
- SDD: 필요
- SDD 문서: `agent-roadmap/sdd/operational-observability-provider-management/node-provider-first-config-surface/SDD.md`
- SDD 사유: Edge config schema, provider/resource source of truth, Edge-to-Node config payload normalization, dispatch/status/config refresh, dev-runtime smoke 기준이 함께 바뀌는 설정 표면 변경이다.
- 잠금 해제 조건: 아래 체크리스트
  - [x] SDD 잠금이 해제되어 있다.
  - [x] SDD 사용자 리뷰가 없거나 승인/해결되었다.
  - [x] Acceptance Scenario가 Milestone 기능 Task와 연결되어 있다.
  - [x] Evidence Map이 완료 시 `Roadmap Completion`과 최종 검증 evidence로 검증 가능하게 연결되어 있다.
- 결정 필요: 없음

## 범위

- `nodes[].providers[]`를 Node가 제공하는 가용 resource의 사용자-facing source of truth로 만든다.
- provider `type`별 필드로 실행 방식을 선언한다. 예: `openai_compat`/`ollama`/`cli`와 endpoint, base URL, command, args, headers, timeout, capacity, queue, model alias/served model mapping.
- loader/normalizer가 provider-first config를 내부 adapter registry와 `NodeConfigPayload`로 컴파일한다.
- 기존 `nodes[].adapters`와 기존 top-level model/provider mapping은 legacy/compat 입력 또는 optional display/policy 입력으로 낮추고, 새 문서와 예시는 provider-first를 기본으로 둔다.
- Edge provider-pool dispatch, direct CLI route, status snapshot, config refresh classification이 provider-first source of truth와 일관되게 동작한다.
- dev-runtime Mac node 예시를 `providers[]` 하나에 Codex/OpenCode CLI resource와 MLX vLLM provider resource가 함께 나열되는 형태로 정리한다.

## 기능

### Epic: [provider-first-config] Provider-First Node Config

운영자가 한 Node의 가용 실행 resource를 한 곳에서 읽고 관리하도록 config schema, runtime normalization, 검증 evidence를 정렬한다.

- [x] [schema-source] provider-first Node config schema가 `providers[]`를 source of truth로 정의하고, `type`별 필드와 model alias/served model mapping을 한 resource 안에 표현한다. 검증: config loader/validation tests가 provider-first happy path와 invalid path를 모두 검증한다.
- [x] [normalize-compile] provider-first config가 내부 adapter registry와 `NodeConfigPayload`로 컴파일되어 기존 Node runtime의 `adapter + target` 실행 계약을 재사용한다. 검증: mapper/config_set/router tests가 provider id를 adapter instance key로 사용할 수 있음을 확인한다.
- [x] [legacy-compat] 기존 `nodes[].adapters` 기반 설정은 legacy/compat로 유지하되, provider-first와 충돌하면 명확한 validation error 또는 우선순위 규칙을 제공한다. 검증: legacy config와 mixed config 회귀 tests가 통과한다.
- [x] [routing-status-refresh] Edge dispatch, status snapshot, config refresh가 provider-first source of truth를 기준으로 provider capacity, queue, health, model alias, served target을 처리한다. 검증: service/openai/status/configrefresh tests가 provider-first route와 zero/disabled provider edge case를 검증한다.
- [x] [priority-routing] Provider-pool dispatch가 dispatch 가능한 후보 중 가장 낮은 `in_flight` provider를 우선하고, `in_flight`가 같은 후보에서만 낮은 숫자의 `priority`를 선택 기준으로 사용한다. `priority` 기본값은 0이고 음수는 validation error이며, `in_flight`와 `priority`가 모두 같으면 기존 순환을 유지한다. 검증: config contract 갱신, config validation, service/model queue tests, config refresh classification tests가 priority 기본값/음수 거부/선택 순서를 검증한다.
- [x] [dev-runtime-docs] dev-runtime inventory, local/dev test rules, `configs/edge.yaml`, 운영 guide가 provider-first 예시로 정리되어 `adapters`와 `providers` 중복 작성을 기본 경로로 안내하지 않는다. 검증: `rg` stale-reference check와 config check evidence가 남아 있다.
- [x] [full-cycle-smoke] dev-runtime provider pool에서 provider-first config로 3 connected nodes, 3 provider candidates, total capacity 10 smoke가 유지된다. 검증: config check, refresh dry-run/apply 또는 restart-required 판정, `/v1/models`, `/v1/responses`, `/v1/chat/completions` capacity smoke evidence가 남아 있다.

## 완료 리뷰

- 상태: 통과
- 완료 근거: `agent-task/archive/2026/06/m-node-provider-first-config-surface/**/complete.log`와 `agent-task/archive/2026/07/m-node-provider-first-config-surface/07_priority_routing/complete.log` 기준으로 `schema-source`, `normalize-compile`, `legacy-compat`, `routing-status-refresh`, `priority-routing`, `dev-runtime-docs`, `full-cycle-smoke`가 PASS 처리되었다.
- SDD Evidence Map S01-S08이 각 `Roadmap Completion`과 최종 검증 evidence로 충족되었고, dev-runtime provider pool capacity smoke는 3 connected nodes, 3 provider candidates, total capacity 10 기준으로 PASS했다.
- 종료 전 코드레벨 검토에서 provider priority config/validation, provider-pool dispatch, config refresh classification, OpenAI catalog refresh 경로를 재확인했고, priority 의미를 `agent-contract/inner/edge-config-runtime-refresh.md`에 보강했다.
- 남은 차단 항목: 없음

## 범위 제외

- Node 내부 adapter abstraction 자체를 즉시 제거하는 대수술
- Control Plane을 Edge-local runtime/provider registry의 canonical store로 만드는 변경
- provider/device/model qualification report와 lifecycle 정책 구현
- billing, chargeback, 조직 IAM, 장기 audit retention
- 외부 public API endpoint 추가

## 작업 컨텍스트

- 관련 경로: `packages/go/config`, `configs/edge.yaml`, `apps/edge/internal/edgevalidate`, `apps/edge/internal/node/mapper.go`, `apps/edge/internal/service`, `apps/edge/internal/openai`, `apps/edge/internal/configrefresh`, `apps/node/internal/adapters`, `apps/node/internal/router`, `proto/iop/runtime.proto`, `agent-contract/inner/edge-config-runtime-refresh.md`, `agent-contract/inner/edge-node-runtime-wire.md`, `agent-test/dev`, `agent-test/local`, `docs/edge-local-dev-guide.md`
- 표준선(선택): 사용자-facing config는 provider/resource-first이고, 내부 adapter registry는 provider config를 컴파일한 실행 IR로 유지한다.
- 표준선(선택): `providers[].id`는 기본 internal adapter instance key가 되며, provider `type`이 내부 driver를 선택한다.
- 표준선(선택): CLI는 provider/resource로 표현하되 IOP-level concurrency 제한은 두지 않는다. provider-pool capacity는 provider별 `capacity`가 소유한다.
- 표준선(선택): provider-pool 선택은 `in_flight < capacity` 후보만 대상으로 하며, `in_flight` 오름차순을 최우선으로 보고 같은 `in_flight` 안에서만 `priority` 오름차순을 적용한다. `priority` 기본값은 0이며 음수는 허용하지 않는다.
- 선행 작업: Node Resource Model Unification
- 후속 작업: 사용량, 토큰, 로그 운영 추적 MVP, 요청 실행 로그와 Usage Ledger 기반
- 확인 필요: 없음
