# SDD: Model route Queue 정책 정합화

## 위치

- Milestone: `agent-roadmap/archive/phase/inference-provider-extension/milestones/model-route-queue-policy-alignment.md`
- Phase: `agent-roadmap/phase/inference-provider-extension/PHASE.md`

## 상태

[승인됨]

## SDD 잠금

- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목:
  - 없음

## 문제 / 비목표

- 문제: Edge-owned model group queue는 OpenAI `model` alias를 group key로 쓰지만, 현재 queue policy의 설정 원천은 Node/provider adapter instance에 남아 있어 다중 Node/다중 provider pool에서 어느 값이 alias queue 정책인지 불명확하다.
- 비목표:
  - GPU/CPU/VRAM metric 기반 자동 capacity 산정
  - cross-Edge 라우팅, cloud fallback, 품질/비용 기반 모델 선택
  - provider별 target 후보 목록을 `model_routes` 하나에 직접 여러 개 선언하는 schema

## Source of Truth

| 영역 | 기준 | 메모 |
|------|------|------|
| Roadmap | `agent-roadmap/phase/inference-provider-extension/milestones/model-route-queue-policy-alignment.md` | Milestone 기능 Task와 완료 반영 기준 |
| Code | `packages/go/config/config.go`, `apps/edge/internal/openai`, `apps/edge/internal/service` | route schema, OpenAI dispatch, Edge queue policy 구현 원천 |
| External Provider | 없음 | provider endpoint 자체의 동작 변경은 범위 밖 |
| User Decision | 없음 | 사용자가 queue policy는 model alias, capacity는 Node/provider instance에 둔다는 방향을 확정했다 |

## State Machine

| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| route-resolved | OpenAI request `model`이 `model_routes[]` 또는 fallback route로 해석된다 | queue-admission | `resolveRouteDispatch` 결과와 `SubmitRunRequest.ModelGroupKey` |
| queue-admission | `ModelGroupKey`가 있고 Edge queue manager가 후보 Node와 policy를 받는다 | dispatched 또는 queued 또는 rejected | `modelQueueManager.admit` |
| queued | 모든 후보 Node의 `in_flight >= capacity`이고 group queue 여유가 있다 | dispatched 또는 timeout/cancelled | terminal run event, queue timeout, request context |
| dispatched | 후보 Node의 `in_flight < capacity` slot이 예약되어 Node로 run request를 보냈다 | released | run `complete`, `error`, `cancelled`, node disconnect |
| released | terminal event 또는 disconnect로 slot이 해제됐다 | queue-admission | 다음 queued item dispatch 시도 |

## Interface Contract

- 계약 원문: 없음
- 입력:
  - `openai.model_routes[].model`: OpenAI-compatible 외부 model alias이며 Edge model group queue key다.
  - `openai.model_routes[].adapter`: 내부 adapter 또는 adapter instance key다.
  - `openai.model_routes[].target`: 내부 provider/model target이다.
  - `openai.model_routes[].max_queue`: 해당 model alias queue의 최대 대기 요청 수다. 0이면 fallback/default를 쓴다.
  - `openai.model_routes[].queue_timeout_ms`: 해당 model alias queue의 대기 timeout이다. 0이면 fallback/default를 쓴다.
  - `nodes[].adapters.*_instances[].capacity`: 해당 Node/provider instance가 동시에 받을 수 있는 실행 slot 수다.
- 출력:
  - Edge queue admission은 route-level policy를 우선 적용하고, 없으면 기존 adapter instance queue policy 또는 기본값을 사용한다.
  - provider snapshot의 `capacity`, `in_flight`, `queued`는 Edge-owned queue 상태와 per-node capacity 기준으로 표현된다.
- 금지:
  - GPU utilization만으로 Edge queue admission 가능 여부를 판단하지 않는다.
  - route-level queue policy가 있는데 adapter instance `max_queue`가 이를 덮어쓰지 않는다.
  - `model_routes`의 중복 `model` alias를 허용하지 않는다.

## Acceptance Scenarios

| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `config-schema` | `openai.model_routes[]`에 `max_queue`, `queue_timeout_ms`가 있다 | Edge config를 load/validate한다 | route entry에 값이 보존되고 중복/빈 model 검증은 유지된다 |
| S02 | `dispatch-contract` | Chat/Responses 요청이 route catalog model alias로 들어온다 | OpenAI handler가 service request를 만든다 | `ModelGroupKey`와 route queue policy가 함께 전달된다 |
| S03 | `queue-policy` | route-level queue policy와 adapter fallback policy가 모두 있다 | Edge queue group을 생성한다 | route-level `max_queue`, `queue_timeout_ms`가 우선 적용된다 |
| S04 | `docs-example` | 사용자가 다중 Node/provider pool 예시를 본다 | `configs/edge.yaml`을 읽는다 | queue policy는 `model_routes`, capacity는 Node/provider instance에 둔다는 책임 경계가 드러난다 |

## Evidence Map

| Scenario | Required Evidence | `agent-task` 연결 | `Spec Completion` 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | `go test -count=1 ./packages/go/config ./apps/edge/internal/openai` | `agent-task/m-model-route-queue-policy-alignment/01_config_route_policy` | `config-schema` PASS와 S01 evidence |
| S02 | `go test -count=1 ./apps/edge/internal/openai` | `agent-task/m-model-route-queue-policy-alignment/01_config_route_policy` | `dispatch-contract` PASS와 S02 evidence |
| S03 | `go test -count=1 ./apps/edge/internal/service` | `agent-task/m-model-route-queue-policy-alignment/02+01_service_queue_policy` | `queue-policy` PASS와 S03 evidence |
| S04 | `git diff --check`와 `configs/edge.yaml` 예시 변경 리뷰 | `agent-task/m-model-route-queue-policy-alignment/01_config_route_policy` | `docs-example` PASS와 S04 evidence |

## Cross-repo Dependencies

- 없음

## Drift Check

- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- [x] Evidence Map이 plan/code-review/complete.log에서 검증 가능하다.
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- [x] 사용자 리뷰가 필요한 항목은 `USER_REVIEW.md`에만 남겼다.

## 사용자 리뷰 이력

- 없음

## 작업 컨텍스트

- 표준선: Edge queue owner 원칙, OpenAI `model` 값 기반 group key, 내부 `adapter + target` 실행 계약을 유지한다.
- 후속 SDD: 없음
