# Milestone: Node provider 상태와 Capacity Queue 기반

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/inference-provider-extension/PHASE.md`

## 목표

Ollama, Lemonade, vLLM, SGLang 같은 provider별 상태 확인 방식 차이를 Node adapter 내부로 숨기고, Edge가 동일한 형태로 provider 가용성과 부하 상태를 확인할 수 있게 만든다.
초기 상태 모델은 `unknown`, `available`, `unavailable`만 사용하고, provider별 capacity를 넘는 요청은 Node가 소유한 FIFO queue에서 대기시키는 기준선을 만든다.
현재 제품 정렬에서는 NomadCode 지원을 위한 OpenAI workspace agent 실행 계약이 먼저이며, 이 마일스톤은 그 뒤 provider 운영 품질을 높이는 후속 기반으로 둔다.

## 상태

[완료]

## 승격 조건

- 없음

## 구현 잠금

- 상태: 해제
- 결정 필요: 없음

## 범위

- Node provider adapter가 provider별 endpoint probe를 구현하되, Edge-visible 상태 결과는 동일한 최소 형태로 제공한다.
- 공통 provider 상태는 `unknown`, `available`, `unavailable`만 먼저 사용한다.
- provider별 configured capacity, current in-flight count, queued count, max queue, queue timeout을 Node가 소유하고 추적한다.
- capacity를 초과한 요청은 provider별 FIFO queue에 넣고, 실행 중 요청이 완료, 실패, 취소되면 가장 먼저 들어온 대기 요청을 실행 슬롯으로 올린다.
- Edge는 provider-specific endpoint를 알지 않고 Node가 제공하는 provider availability/load snapshot만 읽을 수 있게 한다.

## 기능

### Epic: [provider-state] Provider Availability Snapshot

Provider별 probe 차이를 Node 내부로 감추고 Edge가 라우팅 입력으로 사용할 수 있는 최소 상태 snapshot을 정의한다.

- [x] [status-model] Node가 Edge에 노출하는 provider 상태 모델은 `unknown`, `available`, `unavailable`로 제한하고, provider가 직접 보장하지 않는 `degraded`, `starting`, `draining` 같은 상태는 넣지 않는다.
- [x] [probe-contract] Ollama, Lemonade, vLLM, SGLang 등 provider adapter가 endpoint 생존 여부와 target/model 사용 가능 여부를 확인하는 공통 probe 인터페이스를 구현한다.
- [x] [edge-snapshot] Edge가 provider별 구현 세부를 모르고 Node의 provider 상태, capacity, in-flight, queued 값을 조회하거나 이벤트로 받을 수 있는 계약을 정리한다.

### Epic: [capacity-queue] Capacity Gate and FIFO Queue

Provider별 동시 처리 한도를 IOP가 소유하고, 한도를 넘는 요청을 예측 가능한 FIFO queue로 관리한다.

- [x] [capacity-config] provider target별 `capacity`, `max_queue`, `queue_timeout`, `request_timeout` 설정 기준을 정리하고 기본 config 예시에 반영한다.
- [x] [admission-gate] Node provider executor가 `in_flight < capacity`일 때만 provider 호출을 시작하고, capacity가 찬 요청은 provider별 FIFO queue에 넣는다.
- [x] [queue-release] 실행 중 요청이 응답, 실패, 취소, timeout으로 종료되면 `in_flight`를 줄이고 queue의 첫 요청을 실행 슬롯으로 승격한다.
- [x] [queue-reject] `max_queue` 초과 또는 `queue_timeout` 초과 요청은 명확한 error/rejection reason으로 종료한다.
- [x] [queue-observe] Edge가 보는 snapshot에 `capacity`, `in_flight`, `queued`가 포함되어 이후 capacity-aware routing의 입력으로 사용할 수 있다.

## 완료 리뷰

- 상태: 승인됨
- 요청일: 2026-06-15
- 완료 근거: 모든 기능 Task가 PASS 완료되었고, 마지막 `queue-observe`는 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/05+04_queue_observe_snapshot/complete.log`에서 완료 근거와 검증을 남겼다.
- 리뷰 필요:
  - [x] 사용자가 완료 결과를 확인했다
  - [x] archive 이동을 승인했다
- 리뷰 코멘트: 2026-06-15 코드 레벨 종료 검토와 smoke 검증에서 차단 이슈가 없어 archive 이동을 승인받고 완료 처리했다.

## 범위 제외

- 같은 로컬 모델을 묶는 model group alias 설계와 구현
- 여러 Node/Edge 후보 중 여유 자원으로 자동 분산하는 본격 capacity-aware routing
- cloud fallback, 품질 평가 feedback, 비용/속도/품질 기반 모델 선택
- `degraded`, `starting`, `draining` 같은 확장 상태 모델
- provider 내부 metric을 해석해 capacity를 자동 조정하는 기능
- 우선순위 queue, weighted queue, preemption 같은 고급 scheduling 정책

## 작업 컨텍스트

- 관련 경로: `apps/node`, `apps/edge`, `packages/go/config`, `proto/iop`, `configs`
- 표준선(선택): provider가 직접 제공하지 않는 상태를 IOP 공통 상태로 만들지 않고, 초기 상태는 `unknown`, `available`, `unavailable`만 사용한다.
- 표준선(선택): capacity와 queue는 provider 기능이 아니라 Node의 공통 provider execution wrapper 책임으로 둔다.
- 표준선(선택): Edge routing은 provider별 endpoint가 아니라 Node가 제공하는 availability/load snapshot을 입력으로 삼는다.
- 진행 근거: `status-model`은 `apps/node/internal/runtime/types.go`와 Node `CAPABILITIES` 응답에 제한 상태 모델을 추가했고 `go test ./apps/node/...`로 검증했다.
- 완료 근거: `probe-contract`는 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/01_probe_contract/complete.log`에서 PASS 완료되었고 `go test -count=1 ./apps/node/...`, `./scripts/e2e-smoke.sh`, `git diff --check`로 검증했다.
- 완료 근거: `edge-snapshot`은 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/complete.log`에서 PASS 완료되었고 `make proto`, `go test -count=1 ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/...`, `make test-control-plane-edge-wire`, `./scripts/e2e-smoke.sh`, `git diff --check`로 검증했다.
- 완료 근거: `capacity-config`는 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/03_queue_capability_contract/complete.log`에서 PASS 완료되었고 `go test -count=1 ./apps/node/internal/runtime ./apps/node/internal/adapters/ollama ./apps/node/internal/adapters/vllm ./apps/node/internal/adapters`로 검증했다.
- 완료 근거: `admission-gate`, `queue-release`, `queue-reject`는 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/04+03_fifo_admission_queue/complete.log`에서 PASS 완료되었고 `go test -count=1 ./apps/node/internal/node`, `go test -race -count=1 ./apps/node/internal/node`, `go test -count=1 ./apps/node/...`, `./scripts/e2e-smoke.sh`, `git diff --check`로 검증했다.
- 완료 근거: `queue-observe`는 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/05+04_queue_observe_snapshot/complete.log`에서 PASS 완료되었고 `go test -count=1 ./apps/node/internal/node -run 'TestOnCommandRequest_Capabilities'`, `go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/controlplane`, `go test -count=1 ./apps/node/internal/node`, `go test -count=1 ./apps/node/... ./apps/edge/...`, `./scripts/e2e-smoke.sh`, `git diff --check`로 검증했다.
- 선행 작업: Node 단일 통로 멀티 타겟 서빙 기반, OpenAI Workspace Agent Execution Contract
- 후속 작업: Lemonade provider 서빙 경로 추가, vLLM provider 서빙 경로 추가, SGLang provider 서빙 경로 추가, model group alias와 capacity-aware routing Milestone
- 확인 필요: 없음
