<!-- task=m-node-provider-first-config-surface/04+02,03_routing_status_refresh plan=1 tag=REVIEW_ROUTE -->

# Plan - REVIEW_ROUTE

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

구현 완료는 `CODE_REVIEW-local-G07.md` 작성 후 review-ready 보고까지다. review/finalization은 code-review 전용이다.

직접 사용자에게 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 구현 중 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 `CODE_REVIEW-local-G07.md`의 `사용자 리뷰 요청` 섹션을 증거와 함께 채우고 중단한다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니라 검증 결과 또는 후속 리뷰 이슈로 처리한다.

## 배경

이 후속 루프는 이전 리뷰에서 확인된 config refresh 분류 누락만 처리한다. provider-first 실행 필드 변경은 계약상 restart-required인데, 현재 `Classify`의 provider diff key가 endpoint/base URL/command/header 같은 실행 필드를 포함하지 않아 type 변경 없이 실행 필드만 바뀌면 no-change 또는 mutable-only로 처리될 수 있다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/node-provider-first-config-surface.md`
- Task ids:
  - `routing-status-refresh`: provider-first dispatch, status snapshot, config refresh
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Archived plan: `agent-task/m-node-provider-first-config-surface/04+02,03_routing_status_refresh/plan_local_G07_0.log`
- Archived review: `agent-task/m-node-provider-first-config-surface/04+02,03_routing_status_refresh/code_review_local_G07_0.log`
- Verdict: FAIL
- Required summary:
  - `apps/edge/internal/configrefresh/classify.go`의 provider diff key가 provider-first 실행 필드를 누락해 endpoint/base_url/command/header 등 변경을 restart_required로 분류하지 못한다.
  - `apps/edge/internal/bootstrap/runtime.go`의 apply 경로는 변경 목록이 비어 있으면 Node config refresh push를 생략하므로, 누락된 실행 필드 변경은 Edge snapshot과 Node runtime config를 갈라놓을 수 있다.
- Affected files:
  - `apps/edge/internal/configrefresh/classify.go`
  - `apps/edge/internal/configrefresh/classify_test.go`
  - 필요한 경우 `apps/edge/internal/bootstrap/runtime_test.go`
- Verification evidence from previous loop:
  - `go test ./packages/go/config ./apps/edge/internal/edgevalidate ./apps/edge/internal/node ./apps/edge/internal/service ./apps/edge/internal/openai ./apps/edge/internal/configrefresh ./apps/edge/internal/bootstrap`
  - `go test ./apps/edge/... ./packages/go/config`
  - `go test ./...`
- Roadmap carryover:
  - Milestone task `routing-status-refresh`
  - SDD S04/S05, especially config refresh/status evidence for `agent-task/m-node-provider-first-config-surface/04+02,03_routing_status_refresh`
- Narrow reread allowed:
  - `agent-task/m-node-provider-first-config-surface/04+02,03_routing_status_refresh/plan_local_G07_0.log`
  - `agent-task/m-node-provider-first-config-surface/04+02,03_routing_status_refresh/code_review_local_G07_0.log`

## 범위 결정 근거

새 schema나 dispatch/status semantics를 다시 설계하지 않는다. 이미 구현된 `providers[].enabled`, provider-first dispatch key, disabled snapshot 경로는 유지하고, config refresh 분류가 계약상 restart-required 실행 필드를 빠짐없이 감지하도록 보완한다.

## 구현 체크리스트

- [ ] `apps/edge/internal/configrefresh/classify.go`에서 provider-first 실행 필드(`provider`, `endpoint`, `base_url`, `headers`, `command`, `args`, `env`, `mode`, `resume_args`, `output_format`, `context_size`, `request_timeout_ms`)를 provider diff key에 포함하고 변경 시 `StatusRestartRequired`로 분류한다.
- [ ] `apps/edge/internal/configrefresh/classify_test.go`에 type/category 변경 없이 endpoint만 변경되는 provider-first config가 `restart_required`와 `nodes[].providers["..."].endpoint` restart path를 내는 회귀 테스트를 추가한다.
- [ ] 필요한 경우 header/command/base_url 계열 중 최소 하나 이상의 representative execution field 회귀 테스트를 추가해 slice/map diff가 누락되지 않게 한다.
- [ ] 기존 `providers[].enabled` 토글은 계속 live-apply로 남고, capacity/max_queue/queue_timeout_ms는 계속 applied로 남는지 기존 테스트 또는 보강 테스트로 확인한다.
- [ ] `go test ./apps/edge/internal/configrefresh ./apps/edge/internal/bootstrap`를 실행한다.
- [ ] `go test ./packages/go/config ./apps/edge/internal/edgevalidate ./apps/edge/internal/node ./apps/edge/internal/service ./apps/edge/internal/openai ./apps/edge/internal/configrefresh ./apps/edge/internal/bootstrap`를 실행한다.
- [ ] `CODE_REVIEW-local-G07.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_ROUTE-1] Provider-First Execution Field Restart Classification

#### 문제

`apps/edge/internal/configrefresh/classify.go`는 provider restart-required 비교에서 `Type`, `Category`, `Adapter`, `Models`, `Health`, `LifecycleCapabilities`만 비교한다. provider-first compile에 영향을 주는 endpoint, base URL, CLI command/args, headers, timeout 같은 필드는 비교하지 않는다. 결과적으로 실제 Node adapter payload가 바뀌어야 하는 config가 no-change로 보일 수 있다.

#### 해결 방법

`providerKey`에 provider-first 실행 필드 스냅샷을 추가하고 `Classify`의 restart-required provider comparison에서 필드별 path로 비교한다. map/slice 필드는 deep comparison을 사용한다. 실행 필드 path는 `nodes[].providers["<id>"].endpoint`처럼 operator가 원인을 바로 읽을 수 있게 작성한다.

`Enabled`는 기존처럼 별도 live-apply 비교로 유지한다. capacity, max_queue, queue_timeout_ms도 기존 applied 분류를 유지한다.

#### 테스트 작성

작성한다. 최소한 type을 바꾸지 않고 endpoint만 바꾸는 provider-first `openai_compat` 또는 `vllm` 후보가 `StatusRestartRequired`가 되는 테스트를 추가한다. 가능하면 `headers` map 또는 CLI `command`/`args` 변경도 representative field로 확인한다.

## 최종 검증

```bash
go test ./apps/edge/internal/configrefresh ./apps/edge/internal/bootstrap
```

```bash
go test ./packages/go/config ./apps/edge/internal/edgevalidate ./apps/edge/internal/node ./apps/edge/internal/service ./apps/edge/internal/openai ./apps/edge/internal/configrefresh ./apps/edge/internal/bootstrap
```

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