<!-- task=m-node-multi-target-serving-foundation/02+01_adapter_registry plan=0 tag=REFACTOR -->

# Plan - REFACTOR

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

구현 완료의 마지막 단계는 `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션 작성이다. 검증을 실행하고 실제 출력과 설계 결정을 기록한 뒤 active 파일을 유지하고 리뷰 준비를 보고한다. 사용자 전용 결정이나 외부 환경 prerequisite 없이는 진행할 수 없을 때만 `사용자 리뷰 요청`을 채우고 멈춘다.

## 배경

Node registry는 adapter name을 map key로 쓰기 때문에 같은 adapter type의 여러 engine/profile instance를 등록하면 덮어쓴다. 멀티 타겟 Node에서는 adapter type과 route target을 분리해 라우팅 가능한 namespace가 필요하다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 직접 질문하지 않으며, code-review가 user-review stop 여부를 판단한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
- Task ids:
  - `adapter-registry`: Node adapter registry가 동일 adapter type의 여러 engine/profile instance를 덮어쓰지 않고 라우팅 가능한 target namespace로 관리한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-test/local/rules.md`
- `agent-test/local/node-smoke.md`
- `apps/node/internal/adapters/registry.go`
- `apps/node/internal/adapters/factory.go`
- `apps/node/internal/adapters/adapters_blackbox_test.go`
- `apps/node/internal/router/router.go`
- `apps/node/internal/router/router_test.go`
- `apps/node/internal/runtime/types.go`
- `apps/node/internal/node/node.go`
- `apps/node/internal/node/node_test.go`
- `proto/iop/runtime.proto`

### 테스트 환경 규칙

- test_env: `local`
- `node-smoke` 기준 명령은 `go test ./apps/node/...`와 필요 시 edge-node command/run round-trip smoke다. 이 plan은 registry/router 단위 변경이므로 `go test -count=1 ./apps/node/...`를 최종 계약으로 둔다.

### 테스트 커버리지 공백

- registry는 `map[string]runtime.Adapter`로 name 중복을 덮어쓰는 현재 동작을 명시적으로 방어하는 테스트가 부족하다.
- factory는 payload 내 동일 type 여러 adapter instance 등록을 허용하지 않는 구조다.
- router는 `adapter` 단일 string으로 adapter instance와 adapter type을 구분하지 않는다.

### 심볼 참조

- rename/remove 예정 심볼: 없음. 단, `Registry.Get`, `Registry.Register`, `Router.ResolveAdapter` call site는 signature 변경 가능성이 있으므로 전체 grep 후 갱신한다.

### 분할 판단

- predecessor `01`: `01_config_topology`가 active plan으로 존재하며 아직 `complete.log`가 없다. 구현은 `agent-task/m-node-multi-target-serving-foundation/01_config_topology/complete.log`가 생긴 뒤 시작한다.
- 이 작업은 registry/router 내부 변경만 다루고 provider 실행/route catalog는 sibling task로 분리한다.

### 범위 결정 근거

- OpenAI route table과 engine profile별 endpoint 실행은 후속 task에서 처리한다.
- concurrency scheduling은 `07+02_runtime_concurrency`에 둔다.

### 빌드 등급

- build: `local-G08`, review: `cloud-G07`. 내부 refactor지만 Node routing contract라 review는 더 넓은 맥락을 본다.

## 의존 관계 및 구현 순서

- `02+01_adapter_registry`는 `01_config_topology` 완료 후 구현한다. 디렉터리 이름의 `+01` 외 추가 dependency를 만들지 않는다.

## 구현 체크리스트

- [ ] Registry가 adapter type과 instance key를 보존하고 동일 adapter type 여러 instance를 덮어쓰지 않는다.
- [ ] Factory가 `NodeConfigPayload`의 multi adapter instance를 registry namespace로 등록한다.
- [ ] Router가 request의 adapter/target을 새 namespace로 해석하고 legacy 단일 adapter 요청을 호환한다.
- [ ] registry/factory/router/node 단위 테스트를 추가 또는 갱신한다.
- [ ] 최종 검증 명령을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REFACTOR-1] Registry Namespace

- 문제: `Registry.Register`는 `a.Name()`만 key로 쓰고 기존 key를 덮어쓴다 (`apps/node/internal/adapters/registry.go:26`). `BuildFromPayload`도 `"ollama"` 하나만 등록한다 (`apps/node/internal/adapters/factory.go:29`). 이 상태에서는 `ollama@local`, `ollama@dgx`, `vllm@fast` 같은 여러 instance가 공존할 수 없다.
- 해결 방법: adapter registry entry에 stable instance id/type/targets를 둔다. `Get`/`All`/lifecycle 순서는 새 key 기준으로 유지하고, legacy `"ollama"` 단일 요청은 단일 후보일 때만 호환한다. ambiguous legacy 요청은 명확한 error를 반환한다.
- 수정 파일 및 체크리스트:
  - [ ] `apps/node/internal/adapters/registry.go`: key model과 duplicate policy 구현.
  - [ ] `apps/node/internal/adapters/factory.go`: payload instance id를 registry key로 등록.
  - [ ] `apps/node/internal/router/router.go`: request resolution 업데이트.
  - [ ] `apps/node/internal/runtime/types.go`: 필요한 routing metadata 최소 추가.
- 테스트 작성: 추가한다. `adapters_blackbox_test.go` 또는 신규 registry test에 duplicate type multi instance 등록, lifecycle ordering, ambiguous legacy lookup 테스트를 둔다. `router_test.go`에 adapter instance/target 해석 테스트를 둔다.
- 중간 검증:
  - `go test -count=1 ./apps/node/internal/adapters/... ./apps/node/internal/router/...`

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/node/internal/adapters/registry.go` | REFACTOR-1 |
| `apps/node/internal/adapters/factory.go` | REFACTOR-1 |
| `apps/node/internal/adapters/adapters_blackbox_test.go` | REFACTOR-1 |
| `apps/node/internal/router/router.go` | REFACTOR-1 |
| `apps/node/internal/router/router_test.go` | REFACTOR-1 |
| `apps/node/internal/runtime/types.go` | REFACTOR-1 |
| `apps/node/internal/node/node_test.go` | REFACTOR-1 |

## 최종 검증

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

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