<!-- task=m-provider-catalog-device-status/04+03_client_catalog_view plan=1 tag=REVIEW_API -->

# PLAN REVIEW_API - Client Provider Catalog View Follow-up

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

이 plan은 이전 리뷰의 FAIL 후속이다. 범위는 provider status color 우선순위 수정, HTTP JSON DTO parsing test 추가, ops-review evidence 정정으로 제한한다. 구현이 끝나도 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성까지 끝나야 완료다. 검증 명령의 실제 stdout/stderr를 review stub에 붙이고 active 파일을 그대로 둔 뒤 리뷰 준비를 보고한다.

구현 중 선택된 SDD 결정 또는 선택된 Milestone lock decision이 막으면 review stub의 `사용자 리뷰 요청` 섹션에 연결 대상, 차단 근거, 실행한 명령, 재개 조건을 기록하고 멈춘다. 직접 사용자에게 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 환경, secret, service 준비, 일반 범위 변경, 증거 공백은 사용자 리뷰 요청이 아니라 검증 결과 또는 후속 plan 대상으로 기록한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
- Task ids:
  - `ops-review`: Edge config와 Edge-owned read-only catalog/status를 기준으로 `iop-edge` CLI, Control Plane, Client가 어떤 정보를 소비하는지 확인할 수 있다.
- Completion mode: check-on-pass

## Spec Targets

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
- Acceptance scenarios:
  - `S10`: task=`ops-review`; evidence=`Edge CLI configured-provider summary, Control Plane provider status JSON, Client Nodes panel provider catalog/status rendering, and resolved user_review_0.log are present.`
- Completion mode: spec-check-on-pass

## Archive Evidence Snapshot

- Archived plan: `agent-task/m-provider-catalog-device-status/04+03_client_catalog_view/plan_local_G05_0.log`
- Archived review: `agent-task/m-provider-catalog-device-status/04+03_client_catalog_view/code_review_local_G05_0.log`
- Verdict: FAIL
- Required summary:
  - `apps/client/lib/widgets/nodes_panel.dart:123` returns green for `health=degraded` or `health=unhealthy` when `status=active/ready`; health must take precedence over status.
  - `apps/client/test/widget_test.dart` injects `ProviderSnapshotView` directly and has no `EdgeStatusResponseView.fromJson` or `ProviderSnapshotView.fromJson` coverage for `provider_snapshots`.
  - `code_review_local_G05_0.log` records Edge CLI provider summary as "추가 예정", but `apps/edge/internal/edgecmd/nodes.go` and `TestNodesListPrintsConfiguredProviders` already provide the required evidence and the review evidence must be corrected.
- Affected files:
  - `apps/client/lib/widgets/nodes_panel.dart`
  - `apps/client/test/widget_test.dart`
  - `agent-task/m-provider-catalog-device-status/04+03_client_catalog_view/CODE_REVIEW-local-G04.md`
- Verification evidence from failed loop:
  - `cd apps/client && flutter test test/widget_test.dart` passed locally with `+12: All tests passed!`, but did not catch the state-color precedence bug or DTO JSON parse gap.
  - `cd apps/client && flutter test` passed locally with `+28: All tests passed!`.
  - `cd apps/client && flutter analyze --no-fatal-infos` exited 0 with existing info-level issues.
  - `git diff --check` passed with no output.
- Narrow archive reread allowed:
  - `agent-task/m-provider-catalog-device-status/04+03_client_catalog_view/plan_local_G05_0.log`
  - `agent-task/m-provider-catalog-device-status/04+03_client_catalog_view/code_review_local_G05_0.log`
- Roadmap/spec carryover:
  - Keep `Roadmap Targets` and `Spec Targets` above. This follow-up still works toward `ops-review` and S10 completion.

## 범위 결정 근거

- 포함: `apps/client/lib/widgets/nodes_panel.dart`, `apps/client/test/widget_test.dart`, active `CODE_REVIEW-local-G04.md` implementation evidence.
- 제외: `apps/control-plane/**`; predecessor `03_catalog_status_http` already has archive complete log.
- 제외: `apps/edge/**`; Edge CLI provider summary source/test already exists and only evidence text needs correction.
- 제외: proto/generated Dart binding; this is HTTP JSON DTO/widget coverage only.

## 구현 체크리스트

- [x] `_stateColor`가 `health`가 있으면 health를, 없으면 status를 기준으로 색상을 정하도록 수정한다.
- [x] Provider Catalog widget test에 `health=degraded`, `status=active` provider가 yellow state text/color로 표시되는 회귀 assertion을 추가한다.
- [x] raw `/edges/{id}/status` 형태의 JSON fixture를 `EdgeStatusResponseView.fromJson`으로 파싱하는 DTO test를 추가하고 `providerSnapshots`의 id/type/category/status/health/capacity/inFlight/queued/loadRatio/servedModels/lifecycleCapabilities를 검증한다.
- [x] active `CODE_REVIEW-local-G04.md`의 Ops Review Evidence를 Edge CLI source/test, Control Plane predecessor complete log, Client test, SDD `user_review_0.log` 기준으로 정정한다.
- [x] `cd apps/client && flutter test test/widget_test.dart`를 실행했다 (14 tests, all passed).
- [x] `cd apps/client && flutter test`를 실행했다 (30 tests, all passed).
- [x] `cd apps/client && flutter analyze --no-fatal-infos`를 실행했다 (exit 0, pre-existing info issues).
- [x] `git diff --check`를 실행했다 (exit 0, no whitespace errors).
- [x] CODE_REVIEW-local-G04.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채웠다.

### [REVIEW_API-1] Provider state color precedence 수정

#### 문제

`_stateColor`는 `health`와 `status`를 동시에 검사하면서 `status == active` 또는 `status == ready`를 초록색으로 먼저 처리한다. 따라서 `health=degraded`, `status=active`인 provider가 degraded 텍스트를 보여도 상태 색상은 초록색이 된다.

#### 해결 방법

`health`가 비어 있지 않으면 health만 기준으로, health가 없으면 status만 기준으로 상태를 계산한다. `healthy`, `ok`, `active`, `ready`는 초록색, `degraded`, `draining`은 노란색, `unhealthy`, `error`, `failed`는 빨간색으로 둔다.

#### 중간 검증

```bash
cd apps/client && flutter test test/widget_test.dart
```

### [REVIEW_API-2] Provider HTTP DTO JSON parsing test 추가

#### 문제

현재 widget test는 `ProviderSnapshotView`를 직접 생성해 UI에 주입한다. 실제 `HttpControlPlaneStatusRepository.fetchEdgeStatus`가 사용하는 `EdgeStatusResponseView.fromJson` 경로는 검증하지 않는다.

#### 해결 방법

`apps/client/test/widget_test.dart` 또는 기존 client test 파일에 raw JSON map fixture를 만들고 `EdgeStatusResponseView.fromJson`으로 파싱한다. 최소 검증 필드는 `provider_snapshots`, `id`, `adapter`, `type`, `category`, `status`, `health`, `capacity`, `in_flight`, `queued`, `load_ratio`, `served_models`, `lifecycle_capabilities`다.

#### 중간 검증

```bash
cd apps/client && flutter test test/widget_test.dart
```

### [REVIEW_API-3] Ops-review evidence 정정

#### 문제

이전 review log는 Edge CLI provider summary를 "추가 예정"으로 기록했다. 현재 소스에는 `apps/edge/internal/edgecmd/nodes.go`의 configured provider summary와 `TestNodesListPrintsConfiguredProviders`가 존재하므로, evidence text가 실제 상태와 맞지 않는다.

#### 해결 방법

active review stub의 Ops Review Evidence에 다음을 기록한다.

- Edge CLI: `apps/edge/internal/edgecmd/nodes.go`가 configured provider id/type/category/models/capacity/lifecycle summary를 출력하고 `apps/edge/internal/edgecmd/edgecmd_test.go`의 `TestNodesListPrintsConfiguredProviders`가 이를 검증한다.
- Control Plane: `agent-task/archive/2026/06/m-provider-catalog-device-status/03_catalog_status_http/complete.log`가 `/edges/{id}/status` provider snapshots JSON 검증을 완료했다.
- Client: 이 follow-up의 Flutter tests가 Provider Catalog/Status 표시와 DTO parsing을 검증한다.
- SDD review: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/user_review_0.log`가 D01-D06 해결 이력으로 존재한다.

#### 중간 검증

```bash
test -f agent-task/archive/2026/06/m-provider-catalog-device-status/03_catalog_status_http/complete.log
test -f agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/user_review_0.log
rg -n "TestNodesListPrintsConfiguredProviders|providers:" apps/edge/internal/edgecmd
```

## 최종 검증

```bash
cd apps/client && flutter test test/widget_test.dart
cd apps/client && flutter test
cd apps/client && flutter analyze --no-fatal-infos
git diff --check
```

Client test 규칙상 원격 runner evidence가 필요한 환경이면 동기화된 runner에서 같은 명령을 실행한다. 현재 checkout에서만 실행했다면 active review의 검증 결과에 실행 host와 원격 미실행 사유를 명시한다.
