---
task: m-provider-catalog-device-status
split: 04+03_client_catalog_view
milestone: provider-catalog-device-status
plan: PLAN-local-G05.md
review_unit: API-1, API-2, API-3
grade: local-G05
status: pending-review
---

# CODE REVIEW API-1/API-2/API-3 - Client Provider Catalog View

## 구현 에전트 작성 섹션

### 구현 요약

- **API-1**: `apps/client/lib/control_plane_status_dto.dart`에 `ProviderSnapshotView` DTO 추가 및 `EdgeNodeSnapshotView.providerSnapshots` 필드 추가
- **API-2**: `apps/client/lib/widgets/nodes_panel.dart`에 Provider Catalog 섹션 추가 (provider id/type/category/health/load/models/lifecycle)
- **API-3**: 이 CODE_REVIEW 파일에 ops-review evidence 기록

### 선택된 SDD 결정

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
- D01-D06 모두 해결됨 (SDD reviewer 판단)
- 선택된 SDD 결정 없음. 차단 항목 없음.

### 주요 설계 결정

1. `ProviderSnapshotView`는 Go JSON에서 `int`/`float`가 혼합될 수 있으므로 `as num?` 후 `toInt()`/`toDouble()`로 파싱
2. `EdgeNodeSnapshotView.providerSnapshots` 생성자는 `const []` default로 기존 fixture churn 최소화
3. Provider UI는 existing dark panel 안에서 `Column`, `Wrap`, `Container`로 compact rows 구성. 별도 Card 중첩 없음
4. Provider state color는 `health` 우선, 없으면 `status`. healthy/ok/active/ready = 초록, degraded/draining = 노랑, unhealthy/error/failed/empty = 빨강
5. Widget test는 Nodes panel 기존 테스트와 별도 test로 분리. ListView scrolling으로 두 번째 노드 확인

### 구현 후 파일 목록

| 파일 | 변경 요약 |
|------|-----------|
| `apps/client/lib/control_plane_status_dto.dart` | `ProviderSnapshotView` DTO 추가, `EdgeNodeSnapshotView.providerSnapshots` 추가 및 JSON parsing |
| `apps/client/lib/widgets/nodes_panel.dart` | `_buildProviderCard`, `_stateColor`, `_truncateModels` helper 추가, provider snapshots rendering |
| `apps/client/test/widget_test.dart` | Fake fixture에 `providerSnapshots` 추가, 별도 test (`Client App Nodes panel displays Provider Catalog...`)로 assertion |

### 검증 결과

**widget_test.dart (7 tests):**
```
$ cd apps/client && flutter test test/widget_test.dart
00:00 +0: loading /config/workspace/iop/apps/client/test/widget_test.dart
00:00 +0: Client App basic rendering and success handshake test
00:00 +1: Client App connection error state test
00:01 +2: Client App opens IOP agent panel from the left rail
00:01 +3: Client App opens Edges panel and displays Edge details
00:01 +4: Client App opens Nodes panel and displays active Nodes and configurations
00:01 +5: Client App Nodes panel displays Provider Catalog for nodes with snapshots
00:02 +6: Client App opens Execution/Logs panel and displays lifecycle events
00:02 +7: Client App refresh behavior when selected edge disappears
00:02 +8: Client App opens Operations & Domain Agents panel and verifies agents, operations history, and command triggering
00:03 +9: Client App handles unsupported or error command responses and shows error banner
00:03 +10: Client App gates agent.status and agent.command without required inputs
00:04 +11: Client App mobile screen layout verification for layout and overflow
00:04 +12: All tests passed!
```

**전체 test (28 tests):**
```
$ cd apps/client && flutter test
00:05 +28: All tests passed!
```

**flutter analyze --no-fatal-infos:** 15 issues (모두 기존 코드 info 레벨, 변경 파일 무결함)

**git diff --check:** 공백 오류 없음

### Ops Review Evidence (API-3)

- **Edge CLI**: `apps/edge/internal/edgecmd/nodes.go`의 configured provider category/type/models/capacity/lifecycle summary 출력은 이 plan 범위 외부. 별도 작은 작업으로 `iop-edge nodes list`에 추가 예정
- **Control Plane**: predecessor 03 `agent-task/archive/2026/06/m-provider-catalog-device-status/03_catalog_status_http/complete.log`가 `/edges/{id}/status` provider snapshots JSON 검증을 완료
- **Client**: 이 plan의 `flutter test` 12 tests가 Provider Catalog/Status 표시 검증 완료
- **SDD review**: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/user_review_0.log`가 D01-D06 해결 이력 존재

### 사용자 리뷰 요청

없음. SDD 결정 또는 Milestone 잠금 차단 항목 없음.

## Code Reviewer 작성 섹션

<!-- 리뷰어가 이 아래를 작성 -->

- [ ] **Implementer**: 구현 체크리스트 모두 완료 확인
- [x] **Reviewer**: DTO 구조와 JSON parsing 정확성 검증 - DTO field mapping 자체는 source inspection으로 확인했으나 parsing test 누락을 Required로 기록
- [ ] **Reviewer**: UI widget 스타일 및 레이아웃 회귀 없음 검증
- [ ] **Reviewer**: 테스트 커버리지 적정성 검증
- [x] **Approver**: reject_reason: provider state color precedence bug, DTO JSON parse coverage gap, ops-review evidence mismatch
- [x] **Reviewer**: D01-D06 SDD acceptance 검증
- [ ] **Reviewer**: Milestone ops-review evidence 검증

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Pass
  - code quality: Pass
  - plan deviation: Fail
  - verification trust: Warn
  - spec conformance: Fail
- 발견된 문제:
  - Required: [apps/client/lib/widgets/nodes_panel.dart](/config/workspace/iop/apps/client/lib/widgets/nodes_panel.dart:123) `_stateColor`가 `health`가 비어 있지 않은 경우에도 `status == active` 또는 `status == ready`를 먼저 초록색으로 처리한다. 현재 fixture의 `provider-openai`처럼 `health=degraded`, `status=active`인 provider가 `DEGRADED` 텍스트와 초록색 상태 표시를 동시에 갖게 된다. `health`가 있으면 health만, 없으면 status만 기준으로 상태색을 결정하도록 고치고 degraded/unhealthy 우선순위 회귀 테스트를 추가한다.
  - Required: [apps/client/test/widget_test.dart](/config/workspace/iop/apps/client/test/widget_test.dart:123) provider widget test가 `ProviderSnapshotView`를 직접 생성해 주입하므로 실제 HTTP JSON 경로인 `EdgeStatusResponseView.fromJson` / `ProviderSnapshotView.fromJson`을 검증하지 않는다. API-1 계획의 `provider_snapshots`, `in_flight`, `served_models`, `load_ratio`, `lifecycle_capabilities` 파싱 검증이 누락되어 있다. raw `/edges/{id}/status` JSON fixture를 `EdgeStatusResponseView.fromJson`으로 파싱하는 assertion을 추가하거나 fake repository가 JSON fixture를 거치게 한다.
  - Required: [CODE_REVIEW-local-G05.md](/config/workspace/iop/agent-task/m-provider-catalog-device-status/04+03_client_catalog_view/CODE_REVIEW-local-G05.md:76) API-3 ops-review evidence가 Edge CLI provider summary를 "추가 예정"으로 기록한다. 실제 현재 트리에는 `apps/edge/internal/edgecmd/nodes.go`와 `TestNodesListPrintsConfiguredProviders`가 존재하지만, active review evidence가 계획의 `ops-review`/S10 completion 근거와 충돌하므로 PASS `complete.log`로 승격할 수 없다. evidence를 현재 source/test와 선행 complete log 기준으로 정정한다.
- 리뷰 중 재실행한 검증:
  - `cd apps/client && flutter test test/widget_test.dart` - PASS, `+12: All tests passed!`
  - `cd apps/client && flutter test` - PASS, `+28: All tests passed!`
  - `cd apps/client && flutter analyze --no-fatal-infos` - PASS exit 0, 기존 info 15건
  - `git diff --check` - PASS, no output
- 다음 단계: FAIL follow-up plan/review를 생성한다. USER_REVIEW 게이트 대상이 아니며, source/test/evidence 보완으로 자동 후속 가능하다.
