<!-- task=m-node-resource-model-unification/03+01_edge_dispatch_status plan=1 tag=REVIEW_REFACTOR -->

# Follow-up Plan - REVIEW_REFACTOR

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

이 계획은 직전 code-review `FAIL` 판정을 닫기 위한 좁은 후속 작업이다. 사용자에게 직접 질문하지 않는다. 선택된 Milestone `구현 잠금 > 결정 필요`에 연결된 blocker가 아니라면 `사용자 리뷰 요청`을 채우지 말고 구현/검증 증거를 `CODE_REVIEW-local-G06.md`에 기록한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/node-resource-model-unification.md`
- Task ids:
  - `edge-routing-status`: Edge provider-only queue routing and resource-first status
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- 이전 plan: `agent-task/m-node-resource-model-unification/03+01_edge_dispatch_status/plan_cloud_G07_0.log`
- 이전 review: `agent-task/m-node-resource-model-unification/03+01_edge_dispatch_status/code_review_cloud_G07_0.log`
- 이전 판정: FAIL
- Required 요약: `apps/edge/internal/service/run_dispatch.go:493`의 `isProviderAdapterInstanceValid`가 unknown/custom adapter와 enabled instance가 0개인 type route를 fail-open으로 처리해, provider-pool candidate resolution에서 missing adapter provider를 후보로 남길 수 있다.
- 영향 파일: `apps/edge/internal/service/run_dispatch.go`, `apps/edge/internal/service/model_queue_test.go`, `apps/edge/internal/service/service_test.go`, 필요 시 인접 service test fixture.
- 검증 증거: 구현 에이전트는 `go test -count=1 ./apps/edge/...` 통과를 기록했다. 리뷰 에이전트도 같은 명령과, gofmt 후 `go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/openai`를 통과시켰다. 단, missing adapter negative case가 없어서 계약 위반이 테스트로 잡히지 않았다.
- 좁은 재읽기 허용: 구현 판단에 필요하면 위 이전 plan/review log 두 파일만 읽는다. `agent-task/archive/**`를 broad search하지 않는다.
- Roadmap carryover: `edge-routing-status` Task와 SDD S02/S05/S06 중 S05의 provider-only queue, S06의 resource catalog status evidence를 계속 닫는 작업이다.

## 범위 결정 근거

새 제품 결정은 없다. `agent-contract/inner/edge-config-runtime-refresh.md`와 `apps/edge/internal/edgevalidate/validate.go`에 이미 확정된 provider adapter reference 의미를 service runtime candidate resolution에 맞추는 수정이다.

## 구현 항목

### [REVIEW_REFACTOR-1] Strict Provider Adapter Candidate Validation

**문제:** provider-pool candidate resolution의 방어 체크가 missing/unknown adapter를 통과시킨다. config validation은 같은 케이스를 거부하지만, service runtime snapshot 경계는 `resolveProviderPoolCandidates`에서 동일 의미를 지켜야 한다.

**해결 방법:** `isProviderAdapterInstanceValid`를 `edgevalidate.validateProviderAdapterReferences`와 같은 의미로 정렬한다. 통과 조건은 다음 두 가지뿐이다.

- provider adapter가 같은 Node의 enabled exact instance key와 일치한다.
- provider adapter가 legacy type key(`ollama`, `vllm`, `openai_compat`, `cli`)이고 해당 type의 enabled instance/key가 정확히 1개다.

missing/unknown adapter, disabled exact instance, enabled instance 0개인 type route, ambiguous type route는 provider-pool candidate에서 제외한다. 기존 테스트에서 provider adapter로 `mock` 또는 임의 key를 쓰던 fixture는 계약상 유효한 enabled adapter instance를 추가하거나 실제 supported adapter key로 바꾼다.

**수정 파일 및 체크리스트:**

- [x] `apps/edge/internal/service/run_dispatch.go`
- [x] `apps/edge/internal/service/model_queue_test.go`
- [x] `apps/edge/internal/service/service_test.go`
- [x] 필요 시 `apps/edge/internal/service/service_internal_test.go`
- [x] `apps/edge/internal/bootstrap/runtime_test.go`

**테스트 작성:** missing adapter, zero-instance type route, disabled exact instance, ambiguous type route가 후보에서 제외되고 valid exact instance와 single enabled type route만 후보가 되는 regression을 추가한다. 기존 fail-open 기대(`custom-runtime` candidate 포함)는 제거한다.

**중간 검증:**

```bash
go test -count=1 ./apps/edge/internal/service -run 'TestResolveProviderPoolCandidates'
```

## 구현 체크리스트

- [ ] service provider-pool adapter validation을 enabled exact instance 또는 single enabled type route만 통과하도록 수정한다.
- [ ] missing/unknown adapter와 zero-instance type route가 provider-pool candidate에서 제외되는 regression test를 추가한다.
- [ ] 기존 provider-pool queue/status service tests의 provider adapter fixture를 계약상 유효한 enabled adapter instance로 정렬한다.
- [ ] `go test -count=1 ./apps/edge/internal/service -run 'TestResolveProviderPoolCandidates'` 중간 검증 출력을 기록한다.
- [ ] `go test -count=1 ./apps/edge/...` 최종 검증 출력을 기록한다.
- [ ] CODE_REVIEW-local-G06.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 리뷰어 체크포인트

- missing adapter가 더 이상 `resolveProviderPoolCandidates` 후보에 남지 않는지 확인한다.
- `edgevalidate`와 service defensive check의 의미가 어긋나지 않는지 확인한다.
- 테스트 fixture가 `mock`/unknown adapter fail-open에 의존하지 않는지 확인한다.

## 최종 검증

```bash
go test -count=1 ./apps/edge/...
```
