 <!-- task=m-node-provider-first-config-surface/03+01_legacy_compat plan=0 tag=COMPAT -->

# Plan - COMPAT

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

구현 완료 전 `CODE_REVIEW-local-G05.md`를 반드시 채운다. 사용자 리뷰는 Milestone lock 결정만 파일에 기록한다.

## 배경

SDD S03은 기존 `nodes[].adapters` 설정을 회귀 없이 유지하면서 provider-first와 섞였을 때 명확한 validation error 또는 우선순위 규칙을 요구한다. 현재 legacy single-instance와 explicit instance conflict 검증은 있지만 provider-first field와 legacy adapter field의 충돌 규칙은 아직 없다.

## 사용자 리뷰 요청 흐름

직접 사용자에게 묻지 않는다. 에이전트가 표준선으로 정할 수 없는 Milestone 결정만 review stub에 기록한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/node-provider-first-config-surface.md`
- Task ids:
  - `legacy-compat`: legacy adapters config compatibility and mixed config rules
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/sdd/operational-observability-provider-management/node-provider-first-config-surface/SDD.md`
- `packages/go/config/config.go`
- `apps/edge/internal/edgevalidate/validate.go`
- `configs/edge.yaml`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`

### SDD 기준

대상 Acceptance Scenario는 S03이다. Evidence Map은 `legacy/mixed config compatibility tests`를 요구한다. legacy 설정은 유지하되 provider-first와 같은 provider id/adapter key가 서로 다른 endpoint/capacity/model을 선언하면 silent precedence 대신 validation error를 우선한다.

### 테스트 환경 규칙

`test_env=local`. 적용 명령은 `go test ./packages/go/config ./apps/edge/internal/edgevalidate`.

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

`TestLoadEdge_ProviderPoolLegacyCompatibility`는 legacy와 provider pool 동시 존재를 확인하지만 provider-first 실행 필드와 legacy adapter instance conflict를 다루지 않는다.

### 심볼 참조

rename/remove 없음.

### 분할 판단

`03+01_legacy_compat`은 schema source 완료 뒤 mixed config 규칙을 좁게 검증하는 별도 sibling task다. compile task와는 병렬 가능하지만 `01_schema_source` 완료가 선행 조건이다. routing/status 변경은 제외한다.

### 범위 결정 근거

legacy adapters 제거, migration command, public API 변경은 제외한다.

### 빌드 등급

`local-G05`: validation/test 중심의 bounded compatibility work다.

## 의존 관계 및 구현 순서

이 subtask는 디렉터리명 `03+01_legacy_compat`에 따라 같은 task group의 `01_schema_source`가 `complete.log`를 만든 뒤 구현한다.

## 구현 체크리스트

- [x] provider-first resource와 legacy adapter instance가 같은 key를 선언할 때 일치/불일치 규칙을 정의하고 구현한다.
- [x] legacy-only config가 계속 통과하는 회귀 테스트를 유지한다.
- [x] mixed conflict config가 명확한 error를 내는 테스트를 추가한다.
- [x] `go test ./packages/go/config ./apps/edge/internal/edgevalidate`를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [COMPAT-1] Mixed Config Compatibility Rules

#### 문제

`packages/go/config/config.go:565`는 legacy adapter fields와 explicit instance conflict만 검증한다. provider-first fields가 같은 provider id/key를 선언할 때 legacy adapter와 다른 endpoint/capacity를 가리키는지 판정하지 않는다.

#### 해결 방법

provider-first compile target과 legacy adapter instance key가 같으면 field equality를 확인한다. 동일하면 compat accepted, 다르면 `nodes[].providers[...] conflicts with adapters...` 형식의 error를 낸다. provider-first key와 legacy key가 다르면 둘 다 허용하되 docs에서 legacy path로 분리한다.

#### 수정 파일 및 체크리스트

- [x] `packages/go/config/config.go`: provider-first/legacy conflict helper 추가.
- [x] `packages/go/config/config_test.go`: legacy-only, identical mixed, conflicting mixed tests 추가.
- [x] `apps/edge/internal/edgevalidate/validate_test.go`: edgevalidate entrypoint에서도 같은 error 확인.

#### 테스트 작성

`TestLoadEdge_ProviderFirstLegacyIdenticalMixedAllowed`, `TestLoadEdge_ProviderFirstLegacyConflictRejected` 추가 완료. `TestLoadEdge_LegacyOnlyConfigRegression` 회귀 테스트 추가 완료.

#### 중간 검증

```bash
go test ./packages/go/config ./apps/edge/internal/edgevalidate
```

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `packages/go/config/config.go` | COMPAT-1 |
| `packages/go/config/config_test.go` | COMPAT-1 |
| `apps/edge/internal/edgevalidate/validate_test.go` | COMPAT-1 |

## 최종 검증

```bash
go test ./packages/go/config ./apps/edge/internal/edgevalidate
```

```bash
go test ./...
```

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