feat: provider catalog device status milestone completion and catalog view implementation
- Add provider catalog device status milestone tracking - Implement catalog status HTTP endpoints - Add client catalog view implementation - Update control-plane HTTP views and tests - Update edge cmd nodes and tests - Update roadmap and phase documentation
This commit is contained in:
parent
06f1d36fa3
commit
3f8da3354c
15 changed files with 1644 additions and 43 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# IOP 로드맵 현황 및 진행 계획
|
||||
|
||||
작성 기준: 2026-06-18
|
||||
작성 기준: 2026-06-20
|
||||
|
||||
# 1. 월별 로드맵
|
||||
|
||||
|
|
@ -21,11 +21,11 @@
|
|||
|
||||
| 구분 | 내용 |
|
||||
| --- | --- |
|
||||
| 목표 | 같은 Edge 안에서 동일 `model` 요청을 model group queue로 묶고, 여러 Node 후보에 순차 dispatch하는 구조를 정리합니다. Lemonade 이후 vLLM과 SGLang provider 검증을 이어가며 1차 KPI 범위의 속도/디바이스 효율 축을 닫습니다. |
|
||||
| 목표 | 같은 Edge 안에서 동일 `model` 요청을 model group queue로 묶고, 여러 Node 후보에 순차 dispatch하는 구조를 정리합니다. Lemonade 이후 vLLM과 SGLang provider 검증 결과를 반영해 1차 KPI 범위의 속도/디바이스 효율 축을 닫습니다. |
|
||||
| 예상 투입인원 | 1명 |
|
||||
| 현재 구현 상태 | Node-local admission/capacity 기반 완료<br>Edge model group queue scheduling과 같은 Edge 내부의 다중 Node 후보 순차 dispatch 기반 완료<br>OpenAI-compatible `model` route queue 정책 정합화 완료<br>vLLM provider serving validation 완료: provider boundary, DGX Spark vLLM container, config contract, 모델 조회/chat, streaming, split-host field smoke 검증 PASS 근거와 코드 레벨 완료 리뷰까지 정리하고 archive함<br>SGLang provider serving validation을 다음 활성 마일스톤으로 지정함. 현재 계획/잠금 상태이며 SDD 승인과 provider 경계·실테스트 endpoint/model/auth/streaming 결정이 필요함 |
|
||||
| 주요 작업 | 완료: OpenAI-compatible `model` 값을 queue group key로 사용하는 Edge-owned FIFO 정리<br>완료: Node 후보별 capacity, in-flight, queued snapshot을 기준으로 dispatch 흐름 정리<br>완료: Model route queue 정책 원천 정합화<br>완료: vLLM OpenAI-compatible provider의 config contract, 모델 조회, chat, streaming, split-host field smoke 검증<br>다음: SGLang provider SDD gate 해소와 최소 serving 경로 검증<br>계획: Provider별 adapter/config/target/model 매핑 기준을 vLLM/SGLang 검증 결과로 보강 |
|
||||
| 완성 예정 기능 | vLLM provider 최소 serving 검증<br>SGLang provider 최소 serving 검증<br>Provider별 adapter/config/target/model 매핑 기준 보강 |
|
||||
| 현재 구현 상태 | Node-local admission/capacity 기반 완료<br>Edge model group queue scheduling과 같은 Edge 내부의 다중 Node 후보 순차 dispatch 기반 완료<br>OpenAI-compatible `model` route queue 정책 정합화 완료<br>vLLM provider serving validation 완료: provider boundary, DGX Spark vLLM container, config contract, 모델 조회/chat, streaming, split-host field smoke 검증 PASS 근거와 코드 레벨 완료 리뷰까지 정리하고 archive함<br>SGLang provider serving validation은 GX10 + Qwen3.6 35B NVFP4 field validation 결과 NVIDIA checkpoint 직접 로딩 실패와 vLLM 대비 낮은 throughput/KV 여유가 확인되어 현 조건에서 provider 채택을 폐기함<br>다음 활성 작업은 운영 관측 Phase의 `Model Alias Provider Pool과 Provider Catalog` 계획 마일스톤으로 전환됨 |
|
||||
| 주요 작업 | 완료: OpenAI-compatible `model` 값을 queue group key로 사용하는 Edge-owned FIFO 정리<br>완료: Node 후보별 capacity, in-flight, queued snapshot을 기준으로 dispatch 흐름 정리<br>완료: Model route Queue 정책 원천 정합화<br>완료: vLLM OpenAI-compatible provider의 config contract, 모델 조회, chat, streaming, split-host field smoke 검증<br>정리: SGLang provider는 현 field 조건에서 채택하지 않고 Provider Catalog/Qualification 후속 판단 후보로 분리<br>다음: `models[]` 기반 provider pool과 read-only Provider Catalog 계약 구현 계획 |
|
||||
| 완성 예정 기능 | vLLM provider 최소 serving 검증 완료<br>SGLang provider field 판단 결과 정리<br>Provider별 adapter/config/target/model 매핑 기준을 Provider Catalog 계약으로 승계 |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -35,21 +35,21 @@
|
|||
| --- | --- |
|
||||
| 목표 | IOP를 여러 Edge, Node, CLI Agent, local inference provider와 함께 운영하기 위해 필요한 사용자/토큰/사용량/로그/provider 상태 관찰 기준을 1차 feature MVP 단위로 정리합니다. |
|
||||
| 예상 투입인원 | 1명 |
|
||||
| 현재 구현 상태 | Edge, Node, Control Plane, Client의 기본 상태 관찰 경로 구축<br>Provider availability와 capacity snapshot 기반 완료<br>사용자/토큰/사용량/로그 추적과 Provider Catalog는 스케치 상태이며, 사용자 검토 후 계획으로 승격 |
|
||||
| 주요 작업 | 사용자 단위 API/CLI/local inference 사용량 집계 범위 정리<br>Token, request, execution, provider usage 로그의 최소 수집 항목 정의<br>Control Plane/Client 또는 CLI에서 확인할 운영 상태 항목 정리<br>API, CLI, local inference provider를 구분하는 Provider Catalog 기준 정리<br>vLLM, vLLM-MLX, Lemonade, SGLang 등 로컬 디바이스 provider 상태 표현 정리 |
|
||||
| 완성 예정 기능 | 사용량, 토큰, 로그 운영 추적 MVP<br>Provider Catalog와 로컬 디바이스 상태 관리 MVP<br>운영자가 확인할 수 있는 provider/status/usage 최소 화면 또는 명령 표면 |
|
||||
| 현재 구현 상태 | Edge, Node, Control Plane, Client의 기본 상태 관찰 경로 구축<br>Provider availability와 capacity snapshot 기반 완료<br>`Model Alias Provider Pool과 Provider Catalog`가 현재 활성 계획 마일스톤이며 SDD는 승인됨/잠금 해제 상태<br>사용자/토큰/사용량/로그 추적과 Provider-Device-Model Qualification 리포트는 후속 스케치 상태 |
|
||||
| 주요 작업 | `models[]`를 OpenAI-compatible model key이자 provider pool canonical routing key로 정리<br>`nodes[].providers[]` 기반 provider 정의와 provider별 served model list 계약 정리<br>Edge-owned target rewrite와 `in_flight / capacity` load-ratio provider 선택 정책 구현 계획<br>API, CLI, local inference provider를 구분하는 read-only Provider Catalog 기준 정리<br>사용자 단위 API/CLI/local inference 사용량, token, request, execution 로그의 최소 수집 항목은 후속 MVP로 구체화 |
|
||||
| 완성 예정 기능 | Model Alias Provider Pool과 Provider Catalog MVP<br>Provider Catalog와 로컬 디바이스 상태 관리 MVP<br>사용량, 토큰, 로그 운영 추적 MVP<br>운영자가 확인할 수 있는 provider/status/usage 최소 화면 또는 명령 표면 |
|
||||
|
||||
---
|
||||
|
||||
## 8월 ~ 9월: CLI Agent 알림, 원격 작업 환경, 단계 호출 검증 MVP
|
||||
## 8월 ~ 9월: CLI Agent 알림, 원격 작업 환경, Update Plane, 단계 호출 검증 MVP
|
||||
|
||||
| 구분 | 내용 |
|
||||
| --- | --- |
|
||||
| 목표 | CLI Agent 사용량 limit 감지와 알림, 작업 자동 이어받기, workspace-bound execution 기반 원격 작업 환경을 feature MVP로 정리합니다. 동시에 planner/generator/verifier 형태의 단계 호출과 runtime schema 검증은 최소 실행 모드로 스케치합니다. |
|
||||
| 목표 | CLI Agent 사용량 limit 감지와 알림, 작업 자동 이어받기, workspace-bound execution 기반 원격 작업 환경을 feature MVP로 정리합니다. 동시에 Edge/Node 자체 업데이트 기반과 planner/generator/verifier 형태의 단계 호출/runtime schema 검증은 최소 실행 모드로 스케치합니다. |
|
||||
| 예상 투입인원 | 1명 |
|
||||
| 현재 구현 상태 | CLI runtime과 usage checker 계열 기반 마련<br>알림 자체는 `nexo`의 messaging/notification 기반을 활용할 수 있는 상태<br>socket/protocol 기반은 `proto-socket`의 안정화 결과를 활용할 수 있는 상태<br>CLI Agent 알림/자동 이어받기, 원격 작업 환경, 단계 호출 검증 MVP는 스케치 상태이며 사용자 검토 후 계획으로 승격 |
|
||||
| 주요 작업 | CLI Agent limit 도달 이벤트와 사용자 알림 trigger 기준 정리<br>자동 이어받기 허용 조건, 중단 조건, 사용자 승인 경계 정리<br>workspace-bound execution을 이용한 원격 코딩/유지보수 작업 환경의 최소 운영 흐름 정리<br>요청 의도 분석, 실제 작업, 검증/schema 강제, 오류 시 retry/fallback의 최소 단계 실행 흐름 정리<br>Nexo 알림 기반과 IOP 실행 이벤트의 연동 경계 정리 |
|
||||
| 완성 예정 기능 | CLI Agent 사용량 알림 MVP<br>CLI Agent 자동 이어받기 MVP<br>원격 코딩/유지보수 작업 환경 MVP<br>단계 호출과 runtime schema 검증 MVP |
|
||||
| 현재 구현 상태 | CLI runtime과 usage checker 계열 기반 마련<br>OpenAI Workspace Agent Execution Contract 완료<br>알림 자체는 `nexo`의 messaging/notification 기반을 활용할 수 있는 상태<br>socket/protocol 기반은 `proto-socket`의 안정화 결과를 활용할 수 있는 상태<br>CLI Agent 알림/자동 이어받기, 원격 작업 환경, Update Plane, 단계 호출 검증 MVP는 스케치/계획 상태이며 사용자 검토 후 구체화 |
|
||||
| 주요 작업 | CLI Agent limit 도달 이벤트와 사용자 알림 trigger 기준 정리<br>자동 이어받기 허용 조건, 중단 조건, 사용자 승인 경계 정리<br>workspace-bound execution을 이용한 원격 코딩/유지보수 작업 환경의 최소 운영 흐름 정리<br>Control Plane release view, Edge/Node update protocol, host-local manager, rollout/recovery 정책 스케치<br>요청 의도 분석, 실제 작업, 검증/schema 강제, 오류 시 retry/fallback의 최소 단계 실행 흐름 정리<br>Nexo 알림 기반과 IOP 실행 이벤트의 연동 경계 정리 |
|
||||
| 완성 예정 기능 | CLI Agent 사용량 알림 MVP<br>CLI Agent 자동 이어받기 MVP<br>원격 코딩/유지보수 작업 환경 MVP<br>Update Plane 안정 프로토콜과 자체 업데이트 기반 스케치<br>단계 호출과 runtime schema 검증 MVP |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -80,9 +80,9 @@
|
|||
| 구분 | 주요 산출물 |
|
||||
| ----------------| -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 5월 ~ 6월 중순 | Edge-Node 실행 기반, Ollama serving 안정화, Control Plane/Client 운영 기반, CLI Automation Runtime, Domain Agent registry/bootstrap, OpenAI Responses input surface, Workspace Agent Execution Contract, Workspace port/env 표준화, Lemonade provider 검증 결과 |
|
||||
| 6월 중순 ~ 7월 | Edge model group queue scheduling과 model route queue 정책 정합화 결과, vLLM/SGLang provider 검증 결과, provider별 adapter/config/target/model 매핑 기준 |
|
||||
| 7월 ~ 8월 | 사용량/토큰/로그 추적 MVP 정의, Provider Catalog 초안, 로컬 디바이스 provider 상태 관리 기준, 운영 확인 표면 초안 |
|
||||
| 8월 ~ 9월 | CLI Agent 사용량 알림/자동 이어받기 MVP, 원격 작업 환경 MVP, 단계 호출과 runtime schema 검증 MVP, Nexo 알림 연동 경계 |
|
||||
| 6월 중순 ~ 7월 | Edge model group queue scheduling과 model route queue 정책 정합화 결과, vLLM provider 검증 결과, SGLang provider field 판단 결과, provider별 adapter/config/target/model 매핑 기준 |
|
||||
| 7월 ~ 8월 | Model Alias Provider Pool과 Provider Catalog MVP, 사용량/토큰/로그 추적 MVP 정의, 로컬 디바이스 provider 상태 관리 기준, 운영 확인 표면 초안 |
|
||||
| 8월 ~ 9월 | CLI Agent 사용량 알림/자동 이어받기 MVP, 원격 작업 환경 MVP, Update Plane 안정 프로토콜/자체 업데이트 기반, 단계 호출과 runtime schema 검증 MVP, Nexo 알림 연동 경계 |
|
||||
| 9월 ~ 10월 | 1차 KPI 범위 feature MVP 통합 검증 결과, E2E smoke 기준, 운영 전환 체크리스트, known limitation, 2차 범위 분리 결과 |
|
||||
| 10월 이후 | 장기 기억/RAG, advisor/context hook, 원격 터널링, oto scheduler/CI-CD, cloud fallback/품질 평가 고도화 검토 결과 |
|
||||
|
||||
|
|
@ -92,7 +92,7 @@
|
|||
|
||||
| 리스크 및 고려사항 | 내용 |
|
||||
| --- | --- |
|
||||
| Provider 실장비 검증 | vLLM, SGLang, vLLM-MLX 등 local inference provider는 adapter 구현보다 실제 장비와 런타임별 동작 검증이 일정 변수입니다. 1차 범위에서는 provider별 전체 최적화보다 최소 serving 경로와 상태 표현을 우선합니다. |
|
||||
| Provider 실장비 검증 | vLLM, SGLang, vLLM-MLX 등 local inference provider는 adapter 구현보다 실제 장비와 런타임별 동작 검증이 일정 변수입니다. SGLang은 현 field 조건에서 채택하지 않기로 정리했으며, 1차 범위에서는 provider별 전체 최적화보다 최소 serving 경로와 상태 표현을 우선합니다. |
|
||||
| 사용량/토큰/로그 정책 결정 | 사용자 단위 usage, token, log 추적은 구현보다 보존 범위, 노출 범위, 제한 정책 결정이 중요합니다. Billing, chargeback, 조직 IAM, 장기 retention은 1차 범위에서 완성하지 않고 후속 구체화로 둡니다. |
|
||||
| CLI Agent 자동 이어받기 안전 경계 | 자동 이어받기는 편의성이 크지만, 무제한 재시도나 의도하지 않은 실행 지속으로 이어지면 위험합니다. 1차에서는 limit 감지, 알림, 명시된 조건에서의 이어받기만 MVP로 다룹니다. |
|
||||
| Nexo와 Proto Socket 기반 활용 | 알림 기반은 `nexo`, socket/protocol 기반은 `proto-socket`의 거의 완료된 기반을 활용하는 전제로 봅니다. IOP에서는 새 인프라를 다시 만들기보다 실행 이벤트, 알림 trigger, protocol compatibility, 운영 표면 고정에 집중합니다. |
|
||||
|
|
@ -104,10 +104,10 @@
|
|||
|
||||
IOP는 현재 Control Plane - Edge - Node 기반의 실행 오케스트레이션 구조와 OpenAI-compatible 실행 표면을 상당 부분 구축한 상태입니다. Edge-Node 실행 경로, Ollama serving, Control Plane/Client 운영 기반, CLI Automation Runtime, Domain Agent registry/bootstrap, OpenAI Responses input surface, workspace-bound agent execution contract, Workspace port/env 표준화, Node multi-target serving, provider availability/capacity queue, Lemonade provider 검증, Edge model group queue scheduling, model route queue 정책 정합화까지 완료되었습니다.
|
||||
|
||||
현재 vLLM provider serving validation은 완료 및 archive 처리되었습니다. Edge model group queue scheduling과 model route queue 정책 정합화는 완료된 기반으로 보고, 다음 활성 마일스톤은 SGLang provider serving validation입니다. SGLang은 현재 계획/잠금 상태이며, SDD 승인과 provider 경계·실테스트 endpoint/model/auth/streaming 결정이 다음 진입 조건입니다.
|
||||
현재 vLLM provider serving validation은 완료 및 archive 처리되었습니다. SGLang provider serving validation은 field validation 결과를 반영해 현 조건에서는 provider 채택을 폐기했습니다. Edge model group queue scheduling과 model route queue 정책 정합화는 완료된 기반으로 보고, 다음 활성 마일스톤은 운영 관측 Phase의 `Model Alias Provider Pool과 Provider Catalog`입니다.
|
||||
|
||||
이후 7월부터 8월까지는 사용자/토큰/사용량/로그 추적과 Provider Catalog, 로컬 디바이스 provider 상태 관리를 정리합니다. 이 단계는 완성된 billing이나 enterprise IAM이 아니라, 운영자가 IOP의 사용량과 provider 상태를 확인할 수 있는 feature MVP를 목표로 합니다.
|
||||
이후 7월부터 8월까지는 `models[]` 기반 provider pool, Edge-owned target rewrite, load-ratio provider 선택, read-only Provider Catalog, 로컬 디바이스 provider 상태 관리를 정리합니다. 이 단계는 완성된 billing이나 enterprise IAM이 아니라, 운영자가 IOP의 사용량과 provider 상태를 확인할 수 있는 feature MVP를 목표로 합니다. 사용자/토큰/사용량/로그 추적과 Provider-Device-Model Qualification 리포트는 후속 마일스톤으로 이어집니다.
|
||||
|
||||
8월부터 9월까지는 CLI Agent 사용량 limit 알림, 자동 이어받기, 원격 코딩/유지보수 작업 환경, 단계 호출과 runtime schema 검증 MVP를 구체화합니다. 알림 기반은 `nexo`, socket/protocol 기반은 `proto-socket`의 거의 완료된 기반을 활용하고, IOP는 실행 이벤트와 운영 정책, E2E 흐름 고정에 집중합니다.
|
||||
8월부터 9월까지는 CLI Agent 사용량 limit 알림, 자동 이어받기, 원격 코딩/유지보수 작업 환경, Update Plane 자체 업데이트 기반, 단계 호출과 runtime schema 검증 MVP를 구체화합니다. 알림 기반은 `nexo`, socket/protocol 기반은 `proto-socket`의 거의 완료된 기반을 활용하고, IOP는 실행 이벤트와 운영 정책, 업데이트 수렴, E2E 흐름 고정에 집중합니다.
|
||||
|
||||
9월부터 10월까지는 1차 KPI 범위에 포함된 feature MVP를 통합 검증하고 안정화합니다. 10월 이후에는 장기 기억/RAG, advisor, context hook, 원격 터널링, oto scheduler/CI-CD, cross-Edge/cloud fallback 같은 2차 고도화 후보를 별도로 검토합니다.
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ provider/device/model별 qualification report와 모델 lifecycle 관리는 prov
|
|||
- 경로: `agent-roadmap/phase/inference-provider-extension/PHASE.md`
|
||||
- 요약: 1차 MVP의 속도 향상 축으로, 같은 Edge 안의 model group queue와 여러 Node 후보 순차 dispatch를 기준으로 다중 디바이스 효율을 높이고 Lemonade/vLLM/SGLang 같은 provider 검증을 이어가는 단계다.
|
||||
|
||||
- [계획] 운영 관측과 Provider 관리
|
||||
- [진행중] 운영 관측과 Provider 관리
|
||||
- 경로: `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`
|
||||
- 요약: 사용자/토큰/사용량/로그 추적과 API/CLI/local inference provider catalog, 로컬 디바이스 provider 상태 관리, provider/device/model qualification report와 모델 lifecycle 관리 방향을 MVP 운영 축과 후속 심화 축으로 스케치한다.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## 상태
|
||||
|
||||
[계획]
|
||||
[진행중]
|
||||
|
||||
## 목표
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ provider 확장 Phase에서 검증한 Ollama, vLLM, SGLang, Lemonade 같은 추
|
|||
완료, 검토중, 진행중, 계획, 스케치 순서로 두어 아래로 갈수록 미래 작업에 가까워지게 정렬한다.
|
||||
스케치 Milestone은 아직 구현 가능한 계획이 아니므로 사용자 검토와 구체화 후 `[계획]`으로 승격한다.
|
||||
|
||||
- [계획] Model Alias Provider Pool과 Provider Catalog
|
||||
- [진행중] Model Alias Provider Pool과 Provider Catalog
|
||||
- 경로: `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
|
||||
- 요약: `models[]`를 외부 model key이자 운영 catalog model로 두고, Node 하위 provider와 served model list를 기준으로 Edge가 provider 선택, load-ratio routing, target rewrite, read-only catalog 상태를 소유하는 MVP 계약을 구현 계획 대상으로 정리한다.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ provider/device/model별 qualification report와 benchmark/품질 비교는 이
|
|||
|
||||
## 상태
|
||||
|
||||
[계획]
|
||||
[진행중]
|
||||
|
||||
## 승격 조건
|
||||
|
||||
|
|
@ -52,27 +52,27 @@ provider/device/model별 qualification report와 benchmark/품질 비교는 이
|
|||
|
||||
`models[]` catalog model을 중심으로 provider 후보를 묶고 Edge가 실행 후보를 고르는 최소 산출물을 구현한다.
|
||||
|
||||
- [ ] [alias-contract] `models[].id`가 OpenAI-compatible `model` key이자 provider pool의 canonical routing key가 되며, `models[].providers`가 provider id -> served model name map으로 검증된다.
|
||||
- [ ] [candidate-schema] `nodes[].providers[]`가 provider id, type, category, served `models[]`, health, capacity, in-flight, queued, coarse lifecycle capability를 표현한다.
|
||||
- [ ] [target-rewrite] Edge가 선택한 provider id의 `models[].providers[provider_id]` 값을 concrete target으로 rewrite해 Node에 전달하고, Node/provider adapter는 rewrite하지 않는다.
|
||||
- [ ] [selection-policy] Edge가 available provider 중 `in_flight / capacity` load ratio가 가장 낮은 후보를 우선 선택하고, 동률은 deterministic tie-break로 처리하며, queue timeout을 적용한다.
|
||||
- [ ] [pool-compat] 기존 `openai.model_routes`, `adapter + target`, Node provider instance 설정과 새 `models[]`/provider catalog 계약의 호환/마이그레이션 방향이 정리되어 있다.
|
||||
- [x] [alias-contract] `models[].id`가 OpenAI-compatible `model` key이자 provider pool의 canonical routing key가 되며, `models[].providers`가 provider id -> served model name map으로 검증된다.
|
||||
- [x] [candidate-schema] `nodes[].providers[]`가 provider id, type, category, served `models[]`, health, capacity, in-flight, queued, coarse lifecycle capability를 표현한다.
|
||||
- [x] [target-rewrite] Edge가 선택한 provider id의 `models[].providers[provider_id]` 값을 concrete target으로 rewrite해 Node에 전달하고, Node/provider adapter는 rewrite하지 않는다.
|
||||
- [x] [selection-policy] Edge가 available provider 중 `in_flight / capacity` load ratio가 가장 낮은 후보를 우선 선택하고, 동률은 deterministic tie-break로 처리하며, queue timeout을 적용한다.
|
||||
- [x] [pool-compat] 기존 `openai.model_routes`, `adapter + target`, Node provider instance 설정과 새 `models[]`/provider catalog 계약의 호환/마이그레이션 방향이 정리되어 있다.
|
||||
|
||||
### Epic: [provider-catalog] Provider Operations Catalog
|
||||
|
||||
운영자가 model provider pool과 provider 상태를 같은 기준으로 비교하기 위한 read-only catalog 산출물을 묶는다.
|
||||
|
||||
- [ ] [category-map] MVP provider category가 `api`, `cli`, `local_inference`로 정리되고 read-only catalog 표시 필드에 반영된다.
|
||||
- [x] [category-map] MVP provider category가 `api`, `cli`, `local_inference`로 정리되고 read-only catalog 표시 필드에 반영된다.
|
||||
- [ ] [device-status] provider health, capacity, in-flight, derived load ratio, served models, queued 상태가 catalog/status 표면에 정리된다.
|
||||
- [ ] [lifecycle-cap] Ollama, Lemonade, vLLM, SGLang 등의 lifecycle 차이가 coarse capability flags로 표시되고 상세 qualification report는 후속 범위로 분리된다.
|
||||
- [ ] [boundary] provider adapter 구현, Edge-owned provider pool routing, read-only operations catalog, 후속 runtime control의 책임 경계가 정리되어 있다.
|
||||
- [x] [lifecycle-cap] Ollama, Lemonade, vLLM, SGLang 등의 lifecycle 차이가 coarse capability flags로 표시되고 상세 qualification report는 후속 범위로 분리된다.
|
||||
- [x] [boundary] provider adapter 구현, Edge-owned provider pool routing, read-only operations catalog, 후속 runtime control의 책임 경계가 정리되어 있다.
|
||||
- [ ] [ops-review] Edge config와 Edge-owned read-only catalog/status를 기준으로 `iop-edge` CLI, Control Plane, Client가 어떤 정보를 소비하는지 확인할 수 있다.
|
||||
|
||||
## 완료 리뷰
|
||||
|
||||
- 상태: 없음
|
||||
- 요청일: 없음
|
||||
- 완료 근거: 계획 Milestone이며 model provider pool과 catalog 기능 Task가 아직 충족되지 않았다.
|
||||
- 완료 근거: 진행중 Milestone이며 `agent-task/archive/2026/06/m-provider-catalog-device-status/01_pool_schema/complete.log`와 `agent-task/archive/2026/06/m-provider-catalog-device-status/02+01_edge_dispatch/complete.log`의 Roadmap/Spec Completion 근거로 `alias-contract`, `candidate-schema`, `target-rewrite`, `selection-policy`를 완료 반영했다. `pool-compat`는 `01_pool_schema` 계획의 API-3 compatibility 산출물, `apps/edge/README.md`와 `configs/edge.yaml`의 migration/compat note, `TestLoadEdge_ProviderPoolLegacyCompatibility` 및 OpenAI provider-pool legacy fallback tests를 S05 보조 근거로 완료 반영했다. Provider Operations Catalog는 `packages/go/config`, `proto/iop/runtime.proto`, `apps/edge/internal/service/model_queue.go`, `apps/edge/README.md`, `configs/edge.yaml`, `apps/edge/internal/edgecmd/nodes.go` 근거로 `category-map`, `lifecycle-cap`, `boundary`를 완료 반영했으며, `device-status`와 `ops-review`는 Control Plane/Client 표면 계획이 남아 있다.
|
||||
- 리뷰 필요:
|
||||
- [ ] 사용자가 완료 결과를 확인했다
|
||||
- [ ] archive 이동을 승인했다
|
||||
|
|
@ -97,4 +97,7 @@ provider/device/model별 qualification report와 benchmark/품질 비교는 이
|
|||
- 선행 작업: Node provider 상태와 Capacity Queue 기반, Edge 모델 그룹 Queue 스케줄링 전환
|
||||
- 후속 작업: provider pool scheduler 구현, Provider-Device-Model Qualification 리포트와 Lifecycle 관리, provider enable/disable, benchmark/품질 평가
|
||||
- 작업 메모: [pool-compat] `apps/edge/README.md`와 `configs/edge.yaml`에 기존 `openai.model_routes`/`adapter + target`/Node provider instance 설정을 새 `models[]` provider pool의 1:1 migration 입력 및 fallback/compat 경로로 유지하는 방향을 정리했다. SDD 대상 Milestone의 공식 Task 체크는 `complete.log`의 Roadmap/Spec Completion 근거로 처리한다.
|
||||
- 작업 메모: [sync] `01_pool_schema` 완료 로그로 `alias-contract`, `candidate-schema`를, `02+01_edge_dispatch` 완료 로그로 `target-rewrite`, `selection-policy`를 Roadmap/Spec Completion 기준 완료 처리했다.
|
||||
- 작업 메모: [sync-correction] `pool-compat`는 `01_pool_schema`의 공식 Roadmap/Spec Targets에는 빠졌지만, SDD S05 Evidence Map이 요구한 migration/compat note와 기존 `model_routes` regression evidence가 `apps/edge/README.md`, `configs/edge.yaml`, `packages/go/config/config_test.go`, `apps/edge/internal/openai/server_test.go`에 남아 있어 완료로 보정했다.
|
||||
- 작업 메모: [provider-catalog-small] `api`, `cli`, `local_inference` category는 `config.Category*`와 `ProviderSnapshot.category`에 반영되어 있고, coarse lifecycle capability는 `nodes[].providers[].lifecycle_capabilities`와 `ProviderSnapshot.lifecycle_capabilities` 및 `iop-edge nodes list` configured provider 요약으로 확인 가능하다. 상세 lifecycle/qualification과 runtime control은 SDD/범위 제외 기준에 따라 후속 Milestone으로 분리한다.
|
||||
- 확인 필요: 없음
|
||||
|
|
|
|||
|
|
@ -0,0 +1,198 @@
|
|||
<!-- task=m-provider-catalog-device-status/03_catalog_status_http plan=1 tag=REVIEW_API -->
|
||||
|
||||
# Code Review Reference - REVIEW_API
|
||||
|
||||
> **[IMPLEMENTING AGENT - READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a selected SDD decision or selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only SDD/Milestone lock decisions in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-20
|
||||
task=m-provider-catalog-device-status/03_catalog_status_http, plan=1, tag=REVIEW_API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
|
||||
- Task ids:
|
||||
- `device-status`: provider health, capacity, in-flight, derived load ratio, served models, queued 상태가 catalog/status 표면에 정리된다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## Spec Targets
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
|
||||
- Acceptance scenarios:
|
||||
- `S07`: task=`device-status`; evidence=`Control Plane HTTP status JSON exposes provider health, capacity, in_flight, derived load_ratio, served_models, queued without runtime/resource telemetry.`
|
||||
- Completion mode: spec-check-on-pass
|
||||
|
||||
## Archive Evidence Snapshot
|
||||
|
||||
- Archived plan: `agent-task/m-provider-catalog-device-status/03_catalog_status_http/plan_local_G04_0.log`
|
||||
- Archived review: `agent-task/m-provider-catalog-device-status/03_catalog_status_http/code_review_local_G04_0.log`
|
||||
- Verdict: FAIL
|
||||
- Required summary:
|
||||
- `apps/control-plane/cmd/control-plane/http_views.go:158` uses `json:"load_ratio,omitempty"`, so a valid idle provider with `load_ratio=0` omits the required status field from `/edges/{id}/status` JSON.
|
||||
- `apps/control-plane/cmd/control-plane/main_test.go:638` already has a second provider fixture with implicit zero load ratio, but raw JSON assertions do not verify that `load_ratio` is present and equals `0`.
|
||||
- Affected files:
|
||||
- `apps/control-plane/cmd/control-plane/http_views.go`
|
||||
- `apps/control-plane/cmd/control-plane/main_test.go`
|
||||
- Prior verification evidence:
|
||||
- Implementer recorded `go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1`, `go test ./apps/control-plane/... -count=1`, and `git diff --check` as passing.
|
||||
- Review rerun confirmed `go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1`, `go test ./apps/control-plane/... -count=1`, and `git diff --check` pass before this follow-up.
|
||||
- Carryover:
|
||||
- Roadmap Targets and Spec Targets remain unchanged.
|
||||
- This follow-up still works toward SDD `S07` and Milestone task `device-status`.
|
||||
- Narrow archive reread allowed if needed:
|
||||
- `agent-task/m-provider-catalog-device-status/03_catalog_status_http/plan_local_G04_0.log`
|
||||
- `agent-task/m-provider-catalog-device-status/03_catalog_status_http/code_review_local_G04_0.log`
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-local-G03.md` -> `code_review_local_G03_1.log`, `PLAN-local-G03.md` -> `plan_local_G03_1.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-provider-catalog-device-status/03_catalog_status_http/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [REVIEW_API-1] Zero Load Ratio JSON 보존 | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] `apps/control-plane/cmd/control-plane/http_views.go`에서 `providerSnapshotView.LoadRatio` JSON tag를 `json:"load_ratio"`로 바꿔 zero value도 직렬화한다.
|
||||
- [x] `apps/control-plane/cmd/control-plane/main_test.go`에서 second provider raw JSON assertion에 `load_ratio` key 존재 여부와 `float64(0)` 값을 추가한다.
|
||||
- [x] `go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1`를 실행한다.
|
||||
- [x] `go test ./apps/control-plane/... -count=1`와 `git diff --check`를 실행한다.
|
||||
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G03_1.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_local_G03_1.log`로 아카이브한다.
|
||||
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [x] PASS이면 active task 디렉터리 `agent-task/m-provider-catalog-device-status/03_catalog_status_http/`를 `agent-task/archive/YYYY/MM/m-provider-catalog-device-status/03_catalog_status_http/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-provider-catalog-device-status/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-*-G??.md`와 `CODE_REVIEW-*-G??.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 연결된 SDD/Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
계획에 따라 정확한 2개 파일만 수정했다. 추가 변경 사항 없음.
|
||||
|
||||
1. `apps/control-plane/cmd/control-plane/http_views.go`: `LoadRatio` JSON tag에서 `omitempty` 제거
|
||||
2. `apps/control-plane/cmd/control-plane/main_test.go`: second provider raw JSON에 `load_ratio: 0` assertion 추가
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
없음. 단일 JSON tag 조정만 수행.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 SDD 결정 또는 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `LoadRatio` JSON tag가 `omitempty` 없이 zero value를 직렬화하는지 확인한다.
|
||||
- raw JSON assertion이 second provider의 `load_ratio: 0` 존재 여부를 검증하는지 확인한다.
|
||||
- SDD `S07`의 status JSON evidence가 계속 충족되는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### REVIEW_API-1 중간 검증
|
||||
|
||||
```text
|
||||
$ go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1
|
||||
ok iop/apps/control-plane/cmd/control-plane 0.008s
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```text
|
||||
$ go test ./apps/control-plane/... -count=1
|
||||
ok iop/apps/control-plane/cmd/control-plane 0.148s
|
||||
|
||||
ok iop/apps/control-plane/internal/wire 1.487s
|
||||
|
||||
$ git diff --check
|
||||
(no output - no whitespace errors)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT - BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
||||
## 섹션 소유권
|
||||
|
||||
| Section | Owner | Note |
|
||||
|---------|-------|------|
|
||||
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these |
|
||||
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify |
|
||||
| Spec Targets | Fixed at stub creation from plan when present | Implementing agent must not modify |
|
||||
| Archive Evidence Snapshot | Fixed at stub creation from follow-up plan | Implementing agent may narrowly reread only the listed archive paths if needed |
|
||||
| 구현 항목별 완료 여부 | Implementing agent | Check `[ ]` to `[x]` only |
|
||||
| 구현 체크리스트 | Implementing agent | Check `[ ]` to `[x]` only |
|
||||
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify |
|
||||
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
|
||||
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected SDD/Milestone lock decision blocks implementation |
|
||||
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Review focus |
|
||||
| 검증 결과 | Implementing agent | Paste actual command output |
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: PASS
|
||||
- 차원별 평가:
|
||||
- Correctness: Pass
|
||||
- Completeness: Pass
|
||||
- Test coverage: Pass
|
||||
- API contract: Pass
|
||||
- Code quality: Pass
|
||||
- Plan deviation: Pass
|
||||
- Verification trust: Pass
|
||||
- Spec conformance: Pass
|
||||
- 발견된 문제:
|
||||
- 없음
|
||||
- 다음 단계:
|
||||
- PASS complete.log 작성 후 task directory archive 이동.
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
<!-- task=m-provider-catalog-device-status/03_catalog_status_http plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[IMPLEMENTING AGENT - READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a selected SDD decision or selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only SDD/Milestone lock decisions in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-20
|
||||
task=m-provider-catalog-device-status/03_catalog_status_http, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
|
||||
- Task ids:
|
||||
- `device-status`: provider health, capacity, in-flight, derived load ratio, served models, queued 상태가 catalog/status 표면에 정리된다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## Spec Targets
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
|
||||
- Acceptance scenarios:
|
||||
- `S07`: task=`device-status`; evidence=`Control Plane HTTP status JSON exposes provider health, capacity, in_flight, derived load_ratio, served_models, queued without runtime/resource telemetry.`
|
||||
- Completion mode: spec-check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-local-G04.md` -> `code_review_local_G04_0.log`, `PLAN-local-G04.md` -> `plan_local_G04_0.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-provider-catalog-device-status/03_catalog_status_http/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 연결된 SDD/Milestone 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Provider Snapshot JSON DTO 추가 | [x] |
|
||||
| [API-2] Status JSON 회귀 테스트 보강 | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] Control Plane HTTP status DTO에 provider snapshot read-only fields를 추가하고 `EdgeNodeSnapshot.ProviderSnapshots`를 JSON으로 전달한다.
|
||||
- [x] `/edges/{id}/status` 테스트에 provider snapshot fixture와 JSON assertion을 추가해 `health`, `capacity`, `in_flight`, `load_ratio`, `served_models`, `queued`, `category`, `lifecycle_capabilities`가 보존되는지 확인한다.
|
||||
- [x] raw config secret non-leak 검증이 provider snapshot 추가 후에도 유지되는지 확인한다.
|
||||
- [x] `go test ./apps/control-plane/...`를 실행한다.
|
||||
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G04_0.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_local_G04_0.log`로 아카이브한다.
|
||||
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-provider-catalog-device-status/03_catalog_status_http/`를 `agent-task/archive/YYYY/MM/m-provider-catalog-device-status/03_catalog_status_http/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-provider-catalog-device-status/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G03.md`와 `CODE_REVIEW-local-G03.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 연결된 SDD/Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
계획에 명시된 구현을 그대로 따랐습니다. 추가 변경 사항은 없습니다.
|
||||
|
||||
- `providerSnapshotView` struct는 plan의 목표 코드와 동일하게 12개 필드로 추가
|
||||
- `providerSnapshotViews` helper는 proto getter만 사용하며 nil/empty slice는 nil 반환
|
||||
- `edgeStatusResponseViewFromProto`는 `n.GetProviderSnapshots()`를 `providerSnapshotViews()`로 변환하여 매핑
|
||||
- 테스트 fixture는 plan에 명시된 2개 provider snapshot (vllm-gpu, ollama-cpu) 추가
|
||||
- typed DTO assertion과 raw JSON assertion 모두 plan에 명시된 필드 검증 포함
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
- `providerSnapshotView`는 pointer가 아닌 value slice (`[]providerSnapshotView`)로 정의하여, 빈 경우 `null`이 아닌 `nil` JSON으로 직렬화되도록 함 (omitempty 태그와 함께)
|
||||
- `ServedModels`와 `LifecycleCapabilities`는 `append([]string(nil), ...)`로 복사하여 원본 슬라이스 수정 방지
|
||||
- `providerSnapshotViews` helper는 nil 입력 시 nil 반환하여 `json:"provider_snapshots,omitempty"`이 작동하도록 함
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 SDD 결정 또는 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `edgeStatusResponseViewFromProto`가 `n.GetProviderSnapshots()`를 누락하지 않는지 확인한다.
|
||||
- `provider_snapshots` JSON은 read-only status/catalog 필드만 포함하고 Node token/address/raw adapter settings를 노출하지 않는지 확인한다.
|
||||
- test fixture가 `load_ratio`, `served_models`, `lifecycle_capabilities`까지 검증하는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### API-1 중간 검증
|
||||
|
||||
```text
|
||||
$ go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1
|
||||
=== RUN TestEdgeRegistryHTTPHandlersGetLiveStatus
|
||||
=== RUN TestEdgeRegistryHTTPHandlersGetLiveStatus/Success
|
||||
=== RUN TestEdgeRegistryHTTPHandlersGetLiveStatus/MissingEdge
|
||||
=== RUN TestEdgeRegistryHTTPHandlersGetLiveStatus/UpstreamErrorOrTimeout
|
||||
--- PASS: TestEdgeRegistryHTTPHandlersGetLiveStatus (0.00s)
|
||||
--- PASS: TestEdgeRegistryHTTPHandlersGetLiveStatus/Success (0.00s)
|
||||
--- PASS: TestEdgeRegistryHTTPHandlersGetLiveStatus/MissingEdge (0.00s)
|
||||
--- PASS: TestEdgeRegistryHTTPHandlersGetLiveStatus/UpstreamErrorOrTimeout (0.00s)
|
||||
PASS
|
||||
ok iop/apps/control-plane/cmd/control-plane 0.008s
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
|
||||
```text
|
||||
$ go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1
|
||||
=== RUN TestEdgeRegistryHTTPHandlersGetLiveStatus
|
||||
=== RUN TestEdgeRegistryHTTPHandlersGetLiveStatus/Success
|
||||
=== RUN TestEdgeRegistryHTTPHandlersGetLiveStatus/MissingEdge
|
||||
=== RUN TestEdgeRegistryHTTPHandlersGetLiveStatus/UpstreamErrorOrTimeout
|
||||
--- PASS: TestEdgeRegistryHTTPHandlersGetLiveStatus (0.00s)
|
||||
--- PASS: TestEdgeRegistryHTTPHandlersGetLiveStatus/Success (0.00s)
|
||||
--- PASS: TestEdgeRegistryHTTPHandlersGetLiveStatus/MissingEdge (0.00s)
|
||||
--- PASS: TestEdgeRegistryHTTPHandlersGetLiveStatus/UpstreamErrorOrTimeout (0.00s)
|
||||
PASS
|
||||
ok iop/apps/control-plane/cmd/control-plane 0.008s
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```text
|
||||
$ go test ./apps/control-plane/... -count=1
|
||||
ok iop/apps/control-plane/cmd/control-plane 0.143s
|
||||
ok iop/apps/control-plane/internal/wire 1.478s
|
||||
|
||||
$ git diff --check
|
||||
(no output - no whitespace errors)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT - BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
||||
## 섹션 소유권
|
||||
|
||||
| Section | Owner | Note |
|
||||
|---------|-------|------|
|
||||
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these |
|
||||
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify |
|
||||
| Spec Targets | Fixed at stub creation from plan when present | Implementing agent must not modify |
|
||||
| 구현 항목별 완료 여부 | Implementing agent | Check `[ ]` to `[x]` only |
|
||||
| 구현 체크리스트 | Implementing agent | Check `[ ]` to `[x]` only |
|
||||
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify |
|
||||
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
|
||||
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected SDD/Milestone lock decision blocks implementation |
|
||||
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Review focus |
|
||||
| 검증 결과 | Implementing agent | Paste actual command output |
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: FAIL
|
||||
- 차원별 평가:
|
||||
- Correctness: Fail
|
||||
- Completeness: Fail
|
||||
- Test coverage: Fail
|
||||
- API contract: Fail
|
||||
- Code quality: Pass
|
||||
- Plan deviation: Pass
|
||||
- Verification trust: Pass
|
||||
- Spec conformance: Fail
|
||||
- 발견된 문제:
|
||||
- Required: [apps/control-plane/cmd/control-plane/http_views.go](/config/workspace/iop/apps/control-plane/cmd/control-plane/http_views.go:158)의 `load_ratio,omitempty` 때문에 유효한 idle 상태값인 `load_ratio=0`이 `/edges/{id}/status` JSON에서 누락됩니다. SDD `S07`과 이 plan의 `Spec Targets`는 derived `load_ratio`가 status JSON에 노출되는 것을 요구하므로, `LoadRatio` JSON tag에서 `omitempty`를 제거하고 zero value가 직렬화되는지 보장해야 합니다. [apps/control-plane/cmd/control-plane/main_test.go](/config/workspace/iop/apps/control-plane/cmd/control-plane/main_test.go:638)의 두 번째 provider fixture가 이미 zero-load 케이스지만 raw JSON에서 `load_ratio` 존재 여부를 검증하지 않아 회귀가 잡히지 않습니다. `rawP1["load_ratio"]`가 존재하고 `float64(0)`인지 확인하는 assertion을 추가하세요.
|
||||
- 다음 단계:
|
||||
- FAIL 후속 plan/review를 작성한다.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# Complete - m-provider-catalog-device-status/03_catalog_status_http
|
||||
|
||||
## 완료 일시
|
||||
|
||||
2026-06-20
|
||||
|
||||
## 요약
|
||||
|
||||
Control Plane provider status JSON 노출 작업을 2개 루프로 완료했다. 최종 판정: PASS.
|
||||
|
||||
## 루프 이력
|
||||
|
||||
| Plan | Review | Verdict | 메모 |
|
||||
|------|--------|---------|------|
|
||||
| `plan_local_G04_0.log` | `code_review_local_G04_0.log` | FAIL | provider snapshot JSON 추가와 테스트 보강은 완료됐으나, `load_ratio=0`이 `omitempty`로 누락되는 API 계약 문제가 발견됐다. |
|
||||
| `plan_local_G03_1.log` | `code_review_local_G03_1.log` | PASS | `load_ratio` zero value를 JSON에 보존하고 raw JSON regression assertion을 추가했다. |
|
||||
|
||||
## 구현/정리 내용
|
||||
|
||||
- Control Plane `/edges/{id}/status` node JSON에 `provider_snapshots`를 추가해 provider health, capacity, in-flight, queued, served models, category, lifecycle capabilities, load ratio를 전달한다.
|
||||
- provider snapshot 변환은 proto getter만 사용하고 string slice는 복사해 HTTP DTO로 격리했다.
|
||||
- status handler test에 provider snapshot fixture, typed DTO assertion, raw JSON assertion, config/provider snapshot secret non-leak assertion을 추가했다.
|
||||
- `load_ratio` JSON tag에서 `omitempty`를 제거해 idle provider의 `load_ratio=0`도 명시적으로 노출되도록 했다.
|
||||
|
||||
## 최종 검증
|
||||
|
||||
- `go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1` - PASS; `ok iop/apps/control-plane/cmd/control-plane 0.005s`
|
||||
- `go test ./apps/control-plane/... -count=1` - PASS; `ok iop/apps/control-plane/cmd/control-plane 0.141s`, `ok iop/apps/control-plane/internal/wire 1.487s`
|
||||
- `git diff --check` - PASS; no output.
|
||||
|
||||
## Roadmap Completion
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
|
||||
- Completed task ids:
|
||||
- `device-status`: PASS; evidence=`agent-task/archive/2026/06/m-provider-catalog-device-status/03_catalog_status_http/plan_local_G03_1.log`, `agent-task/archive/2026/06/m-provider-catalog-device-status/03_catalog_status_http/code_review_local_G03_1.log`; verification=`go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1`, `go test ./apps/control-plane/... -count=1`, `git diff --check`
|
||||
- Not completed task ids: 없음
|
||||
|
||||
## Spec Completion
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
|
||||
- Completed scenario ids:
|
||||
- `S07`: PASS; task=`device-status`; evidence=`agent-task/archive/2026/06/m-provider-catalog-device-status/03_catalog_status_http/plan_local_G03_1.log`, `agent-task/archive/2026/06/m-provider-catalog-device-status/03_catalog_status_http/code_review_local_G03_1.log`; verification=`go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1`, `go test ./apps/control-plane/... -count=1`, `git diff --check`
|
||||
- Not completed scenario ids: 없음
|
||||
|
||||
## 잔여 Nit
|
||||
|
||||
- 없음
|
||||
|
||||
## 후속 작업
|
||||
|
||||
- 없음
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
<!-- task=m-provider-catalog-device-status/03_catalog_status_http plan=1 tag=REVIEW_API -->
|
||||
|
||||
# PLAN REVIEW_API - Preserve Zero Load Ratio In Status JSON
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 follow-up plan은 직전 코드리뷰의 Required 1건만 해결한다. 구현은 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성까지 끝나야 완료다. 코드 변경 후 검증 명령을 실행하고 실제 stdout/stderr를 review stub에 붙인 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다.
|
||||
|
||||
구현 중 선택된 SDD 결정 또는 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 막으면 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:
|
||||
- `device-status`: provider health, capacity, in-flight, derived load ratio, served models, queued 상태가 catalog/status 표면에 정리된다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## Spec Targets
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
|
||||
- Acceptance scenarios:
|
||||
- `S07`: task=`device-status`; evidence=`Control Plane HTTP status JSON exposes provider health, capacity, in_flight, derived load_ratio, served_models, queued without runtime/resource telemetry.`
|
||||
- Completion mode: spec-check-on-pass
|
||||
|
||||
## Archive Evidence Snapshot
|
||||
|
||||
- Archived plan: `agent-task/m-provider-catalog-device-status/03_catalog_status_http/plan_local_G04_0.log`
|
||||
- Archived review: `agent-task/m-provider-catalog-device-status/03_catalog_status_http/code_review_local_G04_0.log`
|
||||
- Verdict: FAIL
|
||||
- Required summary:
|
||||
- `apps/control-plane/cmd/control-plane/http_views.go:158` uses `json:"load_ratio,omitempty"`, so a valid idle provider with `load_ratio=0` omits the required status field from `/edges/{id}/status` JSON.
|
||||
- `apps/control-plane/cmd/control-plane/main_test.go:638` already has a second provider fixture with implicit zero load ratio, but raw JSON assertions do not verify that `load_ratio` is present and equals `0`.
|
||||
- Affected files:
|
||||
- `apps/control-plane/cmd/control-plane/http_views.go`
|
||||
- `apps/control-plane/cmd/control-plane/main_test.go`
|
||||
- Prior verification evidence:
|
||||
- Implementer recorded `go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1`, `go test ./apps/control-plane/... -count=1`, and `git diff --check` as passing.
|
||||
- Review rerun confirmed `go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1`, `go test ./apps/control-plane/... -count=1`, and `git diff --check` pass before this follow-up.
|
||||
- Carryover:
|
||||
- Roadmap Targets and Spec Targets remain unchanged.
|
||||
- This follow-up still works toward SDD `S07` and Milestone task `device-status`.
|
||||
- Narrow archive reread allowed if needed:
|
||||
- `agent-task/m-provider-catalog-device-status/03_catalog_status_http/plan_local_G04_0.log`
|
||||
- `agent-task/m-provider-catalog-device-status/03_catalog_status_http/code_review_local_G04_0.log`
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- lane/grade: `local-G03`.
|
||||
- 근거: 단일 JSON tag 조정과 기존 HTTP handler test assertion 보강이다. 로컬 Go test와 `git diff --check`로 결정적으로 검증 가능하다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- 포함: `LoadRatio` zero value가 Control Plane HTTP JSON에서 보존되도록 DTO tag를 수정한다.
|
||||
- 포함: 기존 status handler success fixture의 second provider를 사용해 raw JSON에 `load_ratio: 0`이 존재하는지 확인한다.
|
||||
- 제외: provider queue/status 계산, proto 원본/생성물, Client DTO/UI, Edge CLI 표시. 이번 Required와 직접 관련이 없다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `apps/control-plane/cmd/control-plane/http_views.go`에서 `providerSnapshotView.LoadRatio` JSON tag를 `json:"load_ratio"`로 바꿔 zero value도 직렬화한다.
|
||||
- [ ] `apps/control-plane/cmd/control-plane/main_test.go`에서 second provider raw JSON assertion에 `load_ratio` key 존재 여부와 `float64(0)` 값을 추가한다.
|
||||
- [ ] `go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1`를 실행한다.
|
||||
- [ ] `go test ./apps/control-plane/... -count=1`와 `git diff --check`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [REVIEW_API-1] Zero Load Ratio JSON 보존
|
||||
|
||||
#### 문제
|
||||
|
||||
`ProviderSnapshot.LoadRatio`의 `0`은 idle provider의 정상 상태값이다. 현재 Control Plane HTTP DTO는 `json:"load_ratio,omitempty"`를 사용해 `0`을 JSON에서 생략한다. 이러면 SDD `S07`이 요구한 derived `load_ratio` 노출이 idle provider에서 깨지고, Client는 `0`과 필드 없음/unknown을 구분할 수 없다.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
`LoadRatio` JSON tag에서 `omitempty`를 제거한다. 기존 테스트 fixture의 second provider는 `LoadRatio`를 명시하지 않아 zero value 케이스로 쓸 수 있으므로, raw JSON assertion에서 `rawP1["load_ratio"]`가 존재하고 `float64(0)`인지 검증한다.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/control-plane/cmd/control-plane/http_views.go`: `LoadRatio float32 `json:"load_ratio"``
|
||||
- [ ] `apps/control-plane/cmd/control-plane/main_test.go`: `rawP1` assertion에 `load_ratio` 존재/값 검증 추가.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1
|
||||
```
|
||||
|
||||
기대 결과: status handler success/missing/upstream error subtest가 모두 통과하고, zero `load_ratio` raw JSON assertion도 통과한다.
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test ./apps/control-plane/... -count=1
|
||||
git diff --check
|
||||
```
|
||||
|
||||
기대 결과: Control Plane Go tests가 통과하고 공백 오류가 없다. Go test cache output은 허용하지 않는다.
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
<!-- task=m-provider-catalog-device-status/03_catalog_status_http plan=0 tag=API -->
|
||||
|
||||
# PLAN API - Control Plane Provider Status JSON
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 plan의 구현은 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성까지 끝나야 완료다. 코드 변경 후 검증 명령을 실행하고 실제 stdout/stderr를 review stub에 붙인 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 스킬 전용이다.
|
||||
|
||||
구현 중 선택된 SDD 결정 또는 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 막으면 review stub의 `사용자 리뷰 요청` 섹션에 연결 대상, 차단 근거, 실행한 명령, 재개 조건을 기록하고 멈춘다. 직접 사용자에게 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 환경, secret, service 준비, 일반 범위 변경, 증거 공백은 사용자 리뷰 요청이 아니라 검증 결과 또는 후속 plan 대상으로 기록한다.
|
||||
|
||||
## 배경
|
||||
|
||||
Edge wire/proto에는 `EdgeNodeSnapshot.provider_snapshots`와 `ProviderSnapshot`의 catalog/status 필드가 이미 있다. 그러나 Control Plane HTTP `/edges/{id}/status` 변환은 node의 config summary만 내보내고 provider snapshots를 버린다. 이 때문에 Edge-owned read-only provider status가 Control Plane과 Client 표면으로 전달되지 않아 `device-status`를 닫을 수 없다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 선택된 SDD 결정 또는 선택된 Milestone lock decision만 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 중 직접 사용자 프롬프트는 금지하며, code-review가 정당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
|
||||
- Task ids:
|
||||
- `device-status`: provider health, capacity, in-flight, derived load ratio, served models, queued 상태가 catalog/status 표면에 정리된다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## Spec Targets
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
|
||||
- Acceptance scenarios:
|
||||
- `S07`: task=`device-status`; evidence=`Control Plane HTTP status JSON exposes provider health, capacity, in_flight, derived load_ratio, served_models, queued without runtime/resource telemetry.`
|
||||
- Completion mode: spec-check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `AGENTS.md`
|
||||
- `agent-ops/rules/project/rules.md`
|
||||
- `agent-ops/rules/common/rules-roadmap.md`
|
||||
- `agent-ops/skills/common/router.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-ops/skills/common/update-roadmap/SKILL.md`
|
||||
- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
|
||||
- `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
|
||||
- `agent-ops/rules/project/domain/control-plane/rules.md`
|
||||
- `agent-ops/rules/project/domain/platform-common/rules.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/control-plane-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `proto/iop/control.proto`
|
||||
- `proto/gen/iop/control.pb.go`
|
||||
- `proto/iop/runtime.proto`
|
||||
- `proto/gen/iop/runtime.pb.go`
|
||||
- `apps/control-plane/cmd/control-plane/http_views.go`
|
||||
- `apps/control-plane/cmd/control-plane/main_test.go`
|
||||
- `apps/edge/internal/controlplane/connector.go`
|
||||
- `apps/edge/internal/service/status_provider.go`
|
||||
- `apps/edge/internal/service/model_queue.go`
|
||||
- `apps/edge/internal/service/status_provider_test.go`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`.
|
||||
- `agent-test/local/rules.md`를 읽었다. Go quick check는 현재 local checkout에서 실행하고, Docker compose가 필요한 검증은 원격 runner가 필요하다는 규칙을 확인했다.
|
||||
- 매칭 profile: `agent-test/local/control-plane-smoke.md`를 읽었다. 적용 명령은 `go test ./apps/control-plane/...`다.
|
||||
- 참고 profile: `agent-test/local/platform-common-smoke.md`를 읽었다. 이번 plan은 proto 원본/생성물 변경 없이 기존 generated field를 소비하므로 `make proto`는 요구하지 않는다.
|
||||
- HTTP lifecycle 자체를 새 endpoint로 확장하지 않고 기존 `/edges/{id}/status` JSON DTO를 보강한다. `/healthz`, `/readyz`, `make test-control-plane-edge-wire`는 smoke 후보지만 이 plan의 필수 최종 검증은 대상 Go 패키지 테스트로 둔다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- 현재 `TestEdgeRegistryHTTPHandlersGetLiveStatus`는 node config summary와 secret non-leak만 검증하고 provider snapshots JSON을 검증하지 않는다.
|
||||
- `edgeStatusResponseViewFromProto`가 `EdgeNodeSnapshot.ProviderSnapshots`를 누락해도 현재 테스트가 실패하지 않는다.
|
||||
- Edge internal status coverage는 `apps/edge/internal/service/status_provider_test.go`에서 provider `Capacity`, `InFlight`, `Queued`, `LoadRatio`까지 검증되어 있다. 이번 plan은 Control Plane JSON 변환의 회귀 테스트만 추가한다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbols: none.
|
||||
- 새 internal DTO 후보: `providerSnapshotView`.
|
||||
- 변경 call site: `edgeStatusResponseViewFromProto` 내부 node mapping only.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 평가했다.
|
||||
- 공유 task group: `agent-task/m-provider-catalog-device-status/`.
|
||||
- `03_catalog_status_http`: Control Plane HTTP DTO/API test. dependency: none.
|
||||
- `04+03_client_catalog_view`: Client DTO/UI 소비. dependency: `03_catalog_status_http` complete.log.
|
||||
- 분할 이유: Control Plane JSON 계약이 Client UI의 입력 계약이므로 API surface를 먼저 닫고, Client는 그 JSON shape에 의존해 별도 검증한다. Go HTTP tests와 Flutter widget tests는 서로 다른 runner/리스크를 가진다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- 포함: `apps/control-plane/cmd/control-plane/http_views.go`, `apps/control-plane/cmd/control-plane/main_test.go`.
|
||||
- 제외: `proto/iop/*.proto`와 `proto/gen/**`. 이미 `EdgeNodeSnapshot.provider_snapshots`와 `ProviderSnapshot` generated getters가 있으므로 계약 변경이 아니다.
|
||||
- 제외: Edge queue/status 계산. `apps/edge/internal/service/model_queue.go`와 status tests가 이미 provider stats를 만든다.
|
||||
- 제외: Client DTO/UI. dependent plan `04+03_client_catalog_view`에서 처리한다.
|
||||
- 제외: runtime control, resource telemetry, qualification report. SDD D05-D06과 Milestone 범위 제외에 따라 후속 범위다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- lane/grade: `local-G04`.
|
||||
- 근거: 단일 Go app HTTP DTO 변환과 대상 테스트 보강이다. 기존 proto/generated 타입이 있어 public wire contract 변경은 없고, `go test ./apps/control-plane/...`로 결정적으로 검증 가능하다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] Control Plane HTTP status DTO에 provider snapshot read-only fields를 추가하고 `EdgeNodeSnapshot.ProviderSnapshots`를 JSON으로 전달한다.
|
||||
- [ ] `/edges/{id}/status` 테스트에 provider snapshot fixture와 JSON assertion을 추가해 `health`, `capacity`, `in_flight`, `load_ratio`, `served_models`, `queued`, `category`, `lifecycle_capabilities`가 보존되는지 확인한다.
|
||||
- [ ] raw config secret non-leak 검증이 provider snapshot 추가 후에도 유지되는지 확인한다.
|
||||
- [ ] `go test ./apps/control-plane/...`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [API-1] Provider Snapshot JSON DTO 추가
|
||||
|
||||
#### 문제
|
||||
|
||||
[apps/control-plane/cmd/control-plane/http_views.go](/config/workspace/iop/apps/control-plane/cmd/control-plane/http_views.go:138)에서 `edgeNodeSnapshotView`가 `NodeID`, `Alias`, `Label`, `Connected`, `Config`만 가진다. [apps/control-plane/cmd/control-plane/http_views.go](/config/workspace/iop/apps/control-plane/cmd/control-plane/http_views.go:191)의 `edgeStatusResponseViewFromProto`도 `n.GetProviderSnapshots()`를 읽지 않아 Edge가 보낸 provider status가 HTTP JSON에서 사라진다.
|
||||
|
||||
현재 코드:
|
||||
|
||||
```go
|
||||
138 type edgeNodeSnapshotView struct {
|
||||
139 NodeID string `json:"node_id"`
|
||||
140 Alias string `json:"alias"`
|
||||
141 Label string `json:"label"`
|
||||
142 Connected bool `json:"connected"`
|
||||
143 Config *nodeConfigSummaryView `json:"config,omitempty"`
|
||||
144 }
|
||||
...
|
||||
191 func edgeStatusResponseViewFromProto(resp *iop.EdgeStatusResponse) edgeStatusResponseView {
|
||||
192 nodes := make([]edgeNodeSnapshotView, 0, len(resp.GetNodes()))
|
||||
193 for _, n := range resp.GetNodes() {
|
||||
194 nodes = append(nodes, edgeNodeSnapshotView{
|
||||
195 NodeID: n.GetNodeId(),
|
||||
196 Alias: n.GetAlias(),
|
||||
197 Label: n.GetLabel(),
|
||||
198 Connected: n.GetConnected(),
|
||||
199 Config: newNodeConfigSummaryView(n.GetConfig()),
|
||||
200 })
|
||||
201 }
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
`providerSnapshotView`를 `http_views.go`에 추가하고 `edgeNodeSnapshotView`에 `ProviderSnapshots []providerSnapshotView 'json:"provider_snapshots,omitempty"'`를 둔다. 변환 helper는 proto getters만 사용하고 nil/empty는 nil을 반환한다.
|
||||
|
||||
목표 코드:
|
||||
|
||||
```go
|
||||
type providerSnapshotView struct {
|
||||
Adapter string `json:"adapter,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Capacity int32 `json:"capacity"`
|
||||
InFlight int32 `json:"in_flight"`
|
||||
Queued int32 `json:"queued"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Category string `json:"category,omitempty"`
|
||||
ServedModels []string `json:"served_models,omitempty"`
|
||||
Health string `json:"health,omitempty"`
|
||||
LoadRatio float32 `json:"load_ratio,omitempty"`
|
||||
LifecycleCapabilities []string `json:"lifecycle_capabilities,omitempty"`
|
||||
}
|
||||
|
||||
func providerSnapshotViews(snaps []*iop.ProviderSnapshot) []providerSnapshotView {
|
||||
if len(snaps) == 0 {
|
||||
return nil
|
||||
}
|
||||
views := make([]providerSnapshotView, 0, len(snaps))
|
||||
for _, p := range snaps {
|
||||
views = append(views, providerSnapshotView{
|
||||
Adapter: p.GetAdapter(),
|
||||
Status: p.GetStatus(),
|
||||
Capacity: p.GetCapacity(),
|
||||
InFlight: p.GetInFlight(),
|
||||
Queued: p.GetQueued(),
|
||||
ID: p.GetId(),
|
||||
Type: p.GetType(),
|
||||
Category: p.GetCategory(),
|
||||
ServedModels: append([]string(nil), p.GetServedModels()...),
|
||||
Health: p.GetHealth(),
|
||||
LoadRatio: p.GetLoadRatio(),
|
||||
LifecycleCapabilities: append([]string(nil), p.GetLifecycleCapabilities()...),
|
||||
})
|
||||
}
|
||||
return views
|
||||
}
|
||||
```
|
||||
|
||||
`edgeStatusResponseViewFromProto`의 node append에는 `ProviderSnapshots: providerSnapshotViews(n.GetProviderSnapshots())`를 추가한다.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/control-plane/cmd/control-plane/http_views.go`: DTO struct와 helper 추가.
|
||||
- [ ] `apps/control-plane/cmd/control-plane/http_views.go`: `edgeStatusResponseViewFromProto` node mapping에 provider snapshots 추가.
|
||||
- [ ] `apps/control-plane/cmd/control-plane/main_test.go`: JSON shape assertion 추가.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- 작성: `apps/control-plane/cmd/control-plane/main_test.go`의 `TestEdgeRegistryHTTPHandlersGetLiveStatus/Success`에 provider snapshot fixture와 typed/raw assertion을 추가한다.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1
|
||||
```
|
||||
|
||||
기대 결과: provider snapshot JSON assertion과 기존 secret non-leak assertion이 모두 통과한다.
|
||||
|
||||
### [API-2] Status JSON 회귀 테스트 보강
|
||||
|
||||
#### 문제
|
||||
|
||||
[apps/control-plane/cmd/control-plane/main_test.go](/config/workspace/iop/apps/control-plane/cmd/control-plane/main_test.go:444)의 fixture는 `EdgeStatusResponse.Nodes[0]`에 config만 넣고 provider snapshots를 넣지 않는다. 그래서 `provider_snapshots` 누락이 회귀로 잡히지 않는다.
|
||||
|
||||
현재 fixture 일부:
|
||||
|
||||
```go
|
||||
444 return &iop.EdgeStatusResponse{
|
||||
445 RequestId: "req-123",
|
||||
446 EdgeId: "edge-a",
|
||||
447 EdgeName: "Edge A",
|
||||
448 ObservedTimeUnixNano: connectedAt.UnixNano(),
|
||||
449 Nodes: []*iop.EdgeNodeSnapshot{
|
||||
450 {
|
||||
451 NodeId: "node-1",
|
||||
452 Alias: "alpha",
|
||||
453 Label: "node0",
|
||||
454 Connected: true,
|
||||
455 Config: &iop.NodeConfigPayload{
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
fixture에 provider snapshot을 추가하고 typed DTO와 raw JSON 모두 확인한다. raw assertion은 `provider_snapshots` 키가 있고 secret/config internals가 들어가지 않는다는 기존 검증과 공존해야 한다.
|
||||
|
||||
목표 fixture:
|
||||
|
||||
```go
|
||||
ProviderSnapshots: []*iop.ProviderSnapshot{
|
||||
{
|
||||
Adapter: "vllm-gpu",
|
||||
Status: "available",
|
||||
Capacity: 4,
|
||||
InFlight: 1,
|
||||
Queued: 2,
|
||||
Id: "provider-vllm-primary",
|
||||
Type: "vllm",
|
||||
Category: "api",
|
||||
ServedModels: []string{"nvidia/Qwen3.6-35B-A3B-NVFP4"},
|
||||
Health: "available",
|
||||
LoadRatio: 0.25,
|
||||
LifecycleCapabilities: []string{"list_models", "load_model"},
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
Assert `node.ProviderSnapshots[0]` preserves all fields, and raw JSON has `provider_snapshots[0].load_ratio == 0.25`, `served_models[0]`, `lifecycle_capabilities[0]`.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/control-plane/cmd/control-plane/main_test.go`: fixture에 provider snapshot 추가.
|
||||
- [ ] `apps/control-plane/cmd/control-plane/main_test.go`: typed DTO assertion 추가.
|
||||
- [ ] `apps/control-plane/cmd/control-plane/main_test.go`: raw JSON key assertion 추가.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- 작성: 위 test에 직접 추가한다. 별도 새 test보다 기존 status handler 성공 케이스에 붙이는 편이 HTTP view 계약을 한 곳에서 검증한다.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
go test ./apps/control-plane/cmd/control-plane -run TestEdgeRegistryHTTPHandlersGetLiveStatus -count=1
|
||||
```
|
||||
|
||||
기대 결과: status handler 성공/누락/502 subtest가 모두 통과한다.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/control-plane/cmd/control-plane/http_views.go` | API-1 |
|
||||
| `apps/control-plane/cmd/control-plane/main_test.go` | API-2 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test ./apps/control-plane/... -count=1
|
||||
git diff --check
|
||||
```
|
||||
|
||||
기대 결과: Go control-plane tests가 통과하고 공백 오류가 없다. Go test cache output은 허용하지 않는다. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
<!-- task=m-provider-catalog-device-status/04+03_client_catalog_view plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[IMPLEMENTING AGENT - READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a selected SDD decision or selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only SDD/Milestone lock decisions in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-20
|
||||
task=m-provider-catalog-device-status/04+03_client_catalog_view, plan=0, tag=API
|
||||
|
||||
## 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
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-local-G05.md` -> `code_review_local_G05_0.log`, `PLAN-local-G05.md` -> `plan_local_G05_0.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-provider-catalog-device-status/04+03_client_catalog_view/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 연결된 SDD/Milestone 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Client Provider Snapshot DTO 추가 | [ ] |
|
||||
| [API-2] Nodes Panel Provider Catalog 표시 | [ ] |
|
||||
| [API-3] Ops Review Evidence 연결 | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] 선행 task `agent-task/m-provider-catalog-device-status/03_catalog_status_http/complete.log` 또는 matching archive complete.log가 존재하는지 확인하고, 없으면 구현하지 말고 dependency missing으로 기록한다.
|
||||
- [ ] Client HTTP DTO에 `ProviderSnapshotView`와 `EdgeNodeSnapshotView.providerSnapshots` parsing을 추가한다.
|
||||
- [ ] Nodes panel에 read-only Provider Catalog/Status 섹션을 추가해 provider id/type/category, health/status, capacity, in-flight, queued, load ratio, served models, lifecycle capabilities를 표시한다.
|
||||
- [ ] Fake status repository와 widget test fixture에 provider snapshot을 추가하고 Nodes panel test가 provider catalog/status 표시를 검증하도록 보강한다.
|
||||
- [ ] `cd apps/client && flutter test`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G05_0.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_local_G05_0.log`로 아카이브한다.
|
||||
- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-provider-catalog-device-status/04+03_client_catalog_view/`를 `agent-task/archive/YYYY/MM/m-provider-catalog-device-status/04+03_client_catalog_view/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-provider-catalog-device-status/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G05.md`와 `CODE_REVIEW-local-G05.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 연결된 SDD/Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 SDD 결정 또는 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- 구현 전 `03_catalog_status_http` complete.log가 확인되었는지 본다.
|
||||
- DTO parsing이 `capacity`, `in_flight`, `queued`, `load_ratio` numeric 값을 안전하게 처리하는지 확인한다.
|
||||
- Nodes panel이 provider catalog/status를 표시하되 runtime control UI를 추가하지 않았는지 확인한다.
|
||||
- `ops-review` evidence에 Edge CLI, Control Plane, Client, `user_review_0.log` 연결이 남았는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### API-1 중간 검증
|
||||
|
||||
```text
|
||||
$ cd apps/client && flutter test test/widget_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
|
||||
```text
|
||||
$ cd apps/client && flutter test test/widget_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-3 중간 검증
|
||||
|
||||
```text
|
||||
$ test -f agent-task/m-provider-catalog-device-status/03_catalog_status_http/complete.log || find agent-task/archive -path '*/m-provider-catalog-device-status/03_catalog_status_http/complete.log' -print -quit
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```text
|
||||
$ cd apps/client && flutter test
|
||||
(output)
|
||||
|
||||
$ git diff --check
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT - BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
||||
## 섹션 소유권
|
||||
|
||||
| Section | Owner | Note |
|
||||
|---------|-------|------|
|
||||
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these |
|
||||
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify |
|
||||
| Spec Targets | Fixed at stub creation from plan when present | Implementing agent must not modify |
|
||||
| 구현 항목별 완료 여부 | Implementing agent | Check `[ ]` to `[x]` only |
|
||||
| 구현 체크리스트 | Implementing agent | Check `[ ]` to `[x]` only |
|
||||
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify |
|
||||
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
|
||||
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected SDD/Milestone lock decision blocks implementation |
|
||||
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Review focus |
|
||||
| 검증 결과 | Implementing agent | Paste actual command output |
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
<!-- task=m-provider-catalog-device-status/04+03_client_catalog_view plan=0 tag=API -->
|
||||
|
||||
# PLAN API - Client Provider Catalog View
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 plan은 `04+03_client_catalog_view` dependent split task다. 같은 task group의 `03_catalog_status_http`가 `complete.log`를 만들기 전에는 구현을 시작하지 않는다. 구현이 끝나도 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성까지 끝나야 완료다. 검증 명령의 실제 stdout/stderr를 review stub에 붙이고 active 파일을 그대로 둔 뒤 리뷰 준비를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 스킬 전용이다.
|
||||
|
||||
구현 중 선택된 SDD 결정 또는 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 막으면 review stub의 `사용자 리뷰 요청` 섹션에 연결 대상, 차단 근거, 실행한 명령, 재개 조건을 기록하고 멈춘다. 직접 사용자에게 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 환경, secret, service 준비, 일반 범위 변경, 증거 공백은 사용자 리뷰 요청이 아니라 검증 결과 또는 후속 plan 대상으로 기록한다.
|
||||
|
||||
## 배경
|
||||
|
||||
Control Plane Client는 `/edges/{id}/status` JSON을 소비하지만 `EdgeNodeSnapshotView`가 provider snapshots를 파싱하지 않는다. Nodes panel도 node config adapters만 보여 주므로 운영자가 provider category, status, load, served models, lifecycle capability를 Client에서 확인할 수 없다. 03번 plan이 Control Plane HTTP JSON 계약을 닫으면, 이 plan은 Client DTO와 Nodes view에서 같은 read-only catalog/status를 표시한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 선택된 SDD 결정 또는 선택된 Milestone lock decision만 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 중 직접 사용자 프롬프트는 금지하며, code-review가 정당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
|
||||
|
||||
## 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
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `AGENTS.md`
|
||||
- `agent-ops/rules/project/rules.md`
|
||||
- `agent-ops/rules/common/rules-roadmap.md`
|
||||
- `agent-ops/skills/common/router.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-ops/skills/common/update-roadmap/SKILL.md`
|
||||
- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-catalog-device-status.md`
|
||||
- `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/SDD.md`
|
||||
- `agent-ops/rules/project/domain/client/rules.md`
|
||||
- `agent-ops/rules/project/domain/control-plane/rules.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/client-smoke.md`
|
||||
- `agent-test/local/control-plane-smoke.md`
|
||||
- `apps/client/lib/control_plane_status_client.dart`
|
||||
- `apps/client/lib/control_plane_status_dto.dart`
|
||||
- `apps/client/lib/control_plane_status_repository.dart`
|
||||
- `apps/client/lib/widgets/nodes_panel.dart`
|
||||
- `apps/client/lib/widgets/edges_panel.dart`
|
||||
- `apps/client/test/widget_test.dart`
|
||||
- `apps/client/test/control_plane_status_controller_test.dart`
|
||||
- `apps/control-plane/cmd/control-plane/http_views.go`
|
||||
- `proto/iop/control.proto`
|
||||
- `proto/iop/runtime.proto`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`.
|
||||
- `agent-test/local/rules.md`를 읽었다. Flutter client 포함 검증은 원격 runner 또는 동기화된 Flutter 환경 기준이며, 현재 checkout의 변경분이 원격에 없으면 원격 검증 완료로 보지 않는다고 확인했다.
|
||||
- 매칭 profile: `agent-test/local/client-smoke.md`를 읽었다. 필수 명령은 `cd apps/client && flutter test`다.
|
||||
- 관련 upstream profile: `agent-test/local/control-plane-smoke.md`를 읽었다. 이 plan은 Control Plane 코드를 수정하지 않으며 03번 plan의 complete.log를 선행 조건으로 둔다.
|
||||
- Docker compose, web dev server, analyzer는 이번 DTO/widget 범위의 필수 검증이 아니다. Flutter analyzer 영향이 확인되면 구현 에이전트가 `cd apps/client && flutter analyze --no-fatal-infos`를 추가 실행하고 review stub에 기록한다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `EdgeNodeSnapshotView.fromJson`은 `provider_snapshots`를 파싱하지 않는다. HTTP repository가 JSON을 받아도 DTO에서 버린다.
|
||||
- `NodesPanel`은 node config adapters만 표시한다. provider status, category, served models, load ratio, lifecycle capabilities가 UI에 없다.
|
||||
- `FakeControlPlaneStatusRepository.fetchEdgeStatus` fixture와 `Client App opens Nodes panel...` widget test는 provider catalog/status가 표시되어야 한다는 회귀 조건을 갖고 있지 않다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbols: none.
|
||||
- 새 DTO 후보: `ProviderSnapshotView`.
|
||||
- 새 field 후보: `EdgeNodeSnapshotView.providerSnapshots`.
|
||||
- 변경 call sites: `EdgeNodeSnapshotView` constructor usages in `apps/client/test/widget_test.dart` and `apps/client/test/control_plane_status_controller_test.dart` may need `providerSnapshots: []` only if constructor is kept `required`; prefer optional default list to minimize call-site churn.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 평가했다.
|
||||
- 공유 task group: `agent-task/m-provider-catalog-device-status/`.
|
||||
- predecessor: `03_catalog_status_http` - 현재 active plan으로 존재하며 complete.log는 아직 없음.
|
||||
- current task: `04+03_client_catalog_view` - `03` complete.log가 필요하다.
|
||||
- 분할 이유: Client DTO/UI는 Control Plane HTTP JSON shape에 의존한다. Go API 변환과 Flutter widget 검증은 서로 다른 ownership/test runner를 가지므로 별도 review unit으로 둔다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- 포함: `apps/client/lib/control_plane_status_dto.dart`, `apps/client/lib/widgets/nodes_panel.dart`, `apps/client/test/widget_test.dart`. 필요 시 DTO parse 단위 test를 기존 client test 파일에 추가할 수 있다.
|
||||
- 제외: `apps/control-plane/**`. 03번 plan의 PASS가 선행 조건이다.
|
||||
- 제외: generated Dart proto. Client는 Control Plane HTTP JSON DTO를 소비하므로 proto binding 수정이 아니다.
|
||||
- 제외: Edge CLI. 작은 직접 작업으로 `iop-edge nodes list` configured provider summary가 추가되어 있다.
|
||||
- 제외: runtime enable/disable, drain, fallback 우선순위, capacity override. SDD D06과 Milestone 범위 제외에 따라 후속 범위다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- lane/grade: `local-G05`.
|
||||
- 근거: Flutter DTO와 widget 표시 변경이며 UI test로 검증 가능하다. 다만 Client runner/Flutter SDK 상태와 layout 회귀 위험이 있어 G04보다 한 단계 높였다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] 선행 task `agent-task/m-provider-catalog-device-status/03_catalog_status_http/complete.log` 또는 matching archive complete.log가 존재하는지 확인하고, 없으면 구현하지 말고 dependency missing으로 기록한다.
|
||||
- [ ] Client HTTP DTO에 `ProviderSnapshotView`와 `EdgeNodeSnapshotView.providerSnapshots` parsing을 추가한다.
|
||||
- [ ] Nodes panel에 read-only Provider Catalog/Status 섹션을 추가해 provider id/type/category, health/status, capacity, in-flight, queued, load ratio, served models, lifecycle capabilities를 표시한다.
|
||||
- [ ] Fake status repository와 widget test fixture에 provider snapshot을 추가하고 Nodes panel test가 provider catalog/status 표시를 검증하도록 보강한다.
|
||||
- [ ] `cd apps/client && flutter test`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- 이 subtask dir 이름 `04+03_client_catalog_view`는 predecessor index `03`만 요구한다.
|
||||
- 구현 시작 전 다음 중 하나를 확인한다.
|
||||
- active: `agent-task/m-provider-catalog-device-status/03_catalog_status_http/complete.log`
|
||||
- archive: `agent-task/archive/*/*/m-provider-catalog-device-status/03_catalog_status_http/complete.log`
|
||||
- 현재 계획 작성 시점에는 predecessor complete.log가 없다. 구현 에이전트는 03 PASS 이후 진행한다.
|
||||
|
||||
### [API-1] Client Provider Snapshot DTO 추가
|
||||
|
||||
#### 문제
|
||||
|
||||
[apps/client/lib/control_plane_status_dto.dart](/config/workspace/iop/apps/client/lib/control_plane_status_dto.dart:65)의 `EdgeNodeSnapshotView`는 provider snapshots field가 없다. [apps/client/lib/control_plane_status_dto.dart](/config/workspace/iop/apps/client/lib/control_plane_status_dto.dart:80)의 `fromJson`도 `provider_snapshots`를 읽지 않는다.
|
||||
|
||||
현재 코드:
|
||||
|
||||
```dart
|
||||
65 class EdgeNodeSnapshotView {
|
||||
66 final String nodeId;
|
||||
67 final String alias;
|
||||
68 final String label;
|
||||
69 final bool connected;
|
||||
70 final NodeConfigSummaryView? config;
|
||||
...
|
||||
80 factory EdgeNodeSnapshotView.fromJson(Map<String, dynamic> json) {
|
||||
81 return EdgeNodeSnapshotView(
|
||||
82 nodeId: json['node_id'] as String? ?? '',
|
||||
83 alias: json['alias'] as String? ?? '',
|
||||
84 label: json['label'] as String? ?? '',
|
||||
85 connected: json['connected'] as bool? ?? false,
|
||||
86 config: json['config'] != null
|
||||
87 ? NodeConfigSummaryView.fromJson(json['config'] as Map<String, dynamic>)
|
||||
88 : null,
|
||||
89 );
|
||||
90 }
|
||||
91 }
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
`ProviderSnapshotView` DTO를 추가한다. Numeric JSON은 Go에서 `int`/`float`로 올 수 있으므로 `capacity`, `in_flight`, `queued`는 `as num?` 후 `toInt()`, `load_ratio`는 `toDouble()`로 파싱한다. `EdgeNodeSnapshotView` 생성자에는 `List<ProviderSnapshotView> providerSnapshots = const []` default를 둬 기존 fixture churn을 줄인다.
|
||||
|
||||
목표 코드:
|
||||
|
||||
```dart
|
||||
class ProviderSnapshotView {
|
||||
final String id;
|
||||
final String adapter;
|
||||
final String type;
|
||||
final String category;
|
||||
final String status;
|
||||
final String health;
|
||||
final int capacity;
|
||||
final int inFlight;
|
||||
final int queued;
|
||||
final double loadRatio;
|
||||
final List<String> servedModels;
|
||||
final List<String> lifecycleCapabilities;
|
||||
|
||||
ProviderSnapshotView({
|
||||
required this.id,
|
||||
required this.adapter,
|
||||
required this.type,
|
||||
required this.category,
|
||||
required this.status,
|
||||
required this.health,
|
||||
required this.capacity,
|
||||
required this.inFlight,
|
||||
required this.queued,
|
||||
required this.loadRatio,
|
||||
required this.servedModels,
|
||||
required this.lifecycleCapabilities,
|
||||
});
|
||||
|
||||
factory ProviderSnapshotView.fromJson(Map<String, dynamic> json) {
|
||||
return ProviderSnapshotView(
|
||||
id: json['id'] as String? ?? '',
|
||||
adapter: json['adapter'] as String? ?? '',
|
||||
type: json['type'] as String? ?? '',
|
||||
category: json['category'] as String? ?? '',
|
||||
status: json['status'] as String? ?? '',
|
||||
health: json['health'] as String? ?? '',
|
||||
capacity: (json['capacity'] as num?)?.toInt() ?? 0,
|
||||
inFlight: (json['in_flight'] as num?)?.toInt() ?? 0,
|
||||
queued: (json['queued'] as num?)?.toInt() ?? 0,
|
||||
loadRatio: (json['load_ratio'] as num?)?.toDouble() ?? 0,
|
||||
servedModels: List<String>.from(json['served_models'] ?? []),
|
||||
lifecycleCapabilities: List<String>.from(json['lifecycle_capabilities'] ?? []),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/client/lib/control_plane_status_dto.dart`: DTO 추가.
|
||||
- [ ] `apps/client/lib/control_plane_status_dto.dart`: `EdgeNodeSnapshotView.providerSnapshots` 추가 및 JSON parsing.
|
||||
- [ ] `apps/client/test/widget_test.dart` 또는 기존 client unit test: JSON parse assertion 추가 또는 widget fixture에서 파싱 경로 검증.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- 작성: `apps/client/test/widget_test.dart`의 fake repository fixture에 provider snapshots를 추가한다. 별도 pure DTO test가 더 간결하면 같은 test file 또는 `control_plane_status_controller_test.dart`에 추가해도 된다.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter test test/widget_test.dart
|
||||
```
|
||||
|
||||
기대 결과: 기존 app/widget tests와 provider DTO 표시 assertion이 통과한다.
|
||||
|
||||
### [API-2] Nodes Panel Provider Catalog 표시
|
||||
|
||||
#### 문제
|
||||
|
||||
[apps/client/lib/widgets/nodes_panel.dart](/config/workspace/iop/apps/client/lib/widgets/nodes_panel.dart:211)은 config가 있을 때 concurrency와 adapters만 표시한다. provider snapshots가 추가되어도 UI에서 볼 수 없다.
|
||||
|
||||
현재 코드:
|
||||
|
||||
```dart
|
||||
211 if (node.config != null) ...[
|
||||
212 const SizedBox(height: 16),
|
||||
213 const Divider(color: Color(0xFF334155)),
|
||||
214 const SizedBox(height: 12),
|
||||
215 Row(
|
||||
216 children: [
|
||||
217 const Icon(Icons.bolt, size: 16, color: Color(0xFF6366F1)),
|
||||
218 const SizedBox(width: 6),
|
||||
219 Text(
|
||||
220 'Concurrency Limit: ${node.config!.concurrency}',
|
||||
...
|
||||
226 const Text(
|
||||
227 'Adapters',
|
||||
...
|
||||
234 Wrap(
|
||||
235 spacing: 8,
|
||||
236 runSpacing: 8,
|
||||
237 children: node.config!.adapters.map((adapter) {
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Node card 안에 `Provider Catalog` 섹션을 추가한다. Cards 안에 별도 `Card`를 중첩하지 말고 기존 dark panel 안에서 `Column`, `Wrap`, `Container`로 compact rows를 만든다. 표시 필드는 다음을 포함한다.
|
||||
|
||||
- provider label: `id`가 있으면 id, 없으면 adapter.
|
||||
- type/category: `type`과 `category`.
|
||||
- state: `health` 우선, 없으면 `status`.
|
||||
- load: `inFlight/capacity`, `queued`, `loadRatio`.
|
||||
- served models: comma-separated first line, 길면 wrap.
|
||||
- lifecycle capabilities: coarse flags만 표시.
|
||||
|
||||
UI text는 기존 Nodes panel 스타일에 맞춰 작고 조밀하게 둔다. 새 marketing/설명 문구는 추가하지 않는다.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/client/lib/widgets/nodes_panel.dart`: provider section helper 추가.
|
||||
- [ ] `apps/client/lib/widgets/nodes_panel.dart`: node card에 provider snapshots가 있을 때만 section 렌더링.
|
||||
- [ ] `apps/client/test/widget_test.dart`: `Provider Catalog`, provider id/type/category/status/load/models/lifecycle text assertion 추가.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- 작성: `Client App opens Nodes panel and displays active Nodes and configurations`에 provider display assertion을 추가한다.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter test test/widget_test.dart
|
||||
```
|
||||
|
||||
기대 결과: Nodes panel test가 provider catalog/status 표시까지 검증한다.
|
||||
|
||||
### [API-3] Ops Review Evidence 연결
|
||||
|
||||
#### 문제
|
||||
|
||||
Milestone `ops-review`는 `iop-edge` CLI, Control Plane, Client가 어떤 정보를 소비하는지 확인할 수 있어야 한다. 작은 직접 작업으로 `iop-edge nodes list` configured provider summary는 추가됐지만, Client plan이 PASS될 때 전체 소비 경로 근거가 review/complete log에 남아야 한다.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
review stub의 `주요 설계 결정` 또는 `검증 결과`에 다음 증거를 남긴다.
|
||||
|
||||
- Edge CLI: `apps/edge/internal/edgecmd/nodes.go`가 configured provider category/type/models/capacity/lifecycle summary를 출력하고 `TestNodesListPrintsConfiguredProviders`가 통과했다.
|
||||
- Control Plane: predecessor 03 complete.log가 `/edges/{id}/status` provider snapshots JSON을 검증했다.
|
||||
- Client: 이 plan의 widget test가 provider catalog/status 표시를 검증했다.
|
||||
- SDD review: `agent-roadmap/sdd/operational-observability-provider-management/provider-catalog-device-status/user_review_0.log`가 D01-D06 해결 이력으로 존재한다.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `CODE_REVIEW-local-G05.md`: 구현 후 주요 설계 결정 또는 검증 결과에 위 evidence 연결을 기록한다.
|
||||
- [ ] source code는 API-1/API-2 범위를 넘기지 않는다.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- 별도 테스트 없음. API-1/API-2 테스트와 predecessor 03 complete.log가 evidence다.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
test -f agent-task/m-provider-catalog-device-status/03_catalog_status_http/complete.log || find agent-task/archive -path '*/m-provider-catalog-device-status/03_catalog_status_http/complete.log' -print -quit
|
||||
```
|
||||
|
||||
기대 결과: 03 predecessor complete.log 경로가 출력된다.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/client/lib/control_plane_status_dto.dart` | API-1 |
|
||||
| `apps/client/lib/widgets/nodes_panel.dart` | API-2 |
|
||||
| `apps/client/test/widget_test.dart` | API-1, API-2 |
|
||||
| `agent-task/m-provider-catalog-device-status/04+03_client_catalog_view/CODE_REVIEW-local-G05.md` | API-3 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter test
|
||||
git diff --check
|
||||
```
|
||||
|
||||
기대 결과: Flutter tests가 통과하고 공백 오류가 없다. Go test cache output은 해당 없음. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -136,11 +136,27 @@ type fleetCommandResponse struct {
|
|||
}
|
||||
|
||||
type edgeNodeSnapshotView struct {
|
||||
NodeID string `json:"node_id"`
|
||||
Alias string `json:"alias"`
|
||||
Label string `json:"label"`
|
||||
Connected bool `json:"connected"`
|
||||
Config *nodeConfigSummaryView `json:"config,omitempty"`
|
||||
NodeID string `json:"node_id"`
|
||||
Alias string `json:"alias"`
|
||||
Label string `json:"label"`
|
||||
Connected bool `json:"connected"`
|
||||
Config *nodeConfigSummaryView `json:"config,omitempty"`
|
||||
ProviderSnapshots []providerSnapshotView `json:"provider_snapshots,omitempty"`
|
||||
}
|
||||
|
||||
type providerSnapshotView struct {
|
||||
Adapter string `json:"adapter,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Capacity int32 `json:"capacity"`
|
||||
InFlight int32 `json:"in_flight"`
|
||||
Queued int32 `json:"queued"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Category string `json:"category,omitempty"`
|
||||
ServedModels []string `json:"served_models,omitempty"`
|
||||
Health string `json:"health,omitempty"`
|
||||
LoadRatio float32 `json:"load_ratio"`
|
||||
LifecycleCapabilities []string `json:"lifecycle_capabilities,omitempty"`
|
||||
}
|
||||
|
||||
type nodeConfigSummaryView struct {
|
||||
|
|
@ -176,6 +192,30 @@ func newNodeConfigSummaryView(payload *iop.NodeConfigPayload) *nodeConfigSummary
|
|||
}
|
||||
}
|
||||
|
||||
func providerSnapshotViews(snaps []*iop.ProviderSnapshot) []providerSnapshotView {
|
||||
if len(snaps) == 0 {
|
||||
return nil
|
||||
}
|
||||
views := make([]providerSnapshotView, 0, len(snaps))
|
||||
for _, p := range snaps {
|
||||
views = append(views, providerSnapshotView{
|
||||
Adapter: p.GetAdapter(),
|
||||
Status: p.GetStatus(),
|
||||
Capacity: p.GetCapacity(),
|
||||
InFlight: p.GetInFlight(),
|
||||
Queued: p.GetQueued(),
|
||||
ID: p.GetId(),
|
||||
Type: p.GetType(),
|
||||
Category: p.GetCategory(),
|
||||
ServedModels: append([]string(nil), p.GetServedModels()...),
|
||||
Health: p.GetHealth(),
|
||||
LoadRatio: p.GetLoadRatio(),
|
||||
LifecycleCapabilities: append([]string(nil), p.GetLifecycleCapabilities()...),
|
||||
})
|
||||
}
|
||||
return views
|
||||
}
|
||||
|
||||
func edgeRegistryViewFromState(state wire.EdgeConnectionState) edgeRegistryView {
|
||||
return edgeRegistryView{
|
||||
EdgeID: state.EdgeID,
|
||||
|
|
@ -192,11 +232,12 @@ func edgeStatusResponseViewFromProto(resp *iop.EdgeStatusResponse) edgeStatusRes
|
|||
nodes := make([]edgeNodeSnapshotView, 0, len(resp.GetNodes()))
|
||||
for _, n := range resp.GetNodes() {
|
||||
nodes = append(nodes, edgeNodeSnapshotView{
|
||||
NodeID: n.GetNodeId(),
|
||||
Alias: n.GetAlias(),
|
||||
Label: n.GetLabel(),
|
||||
Connected: n.GetConnected(),
|
||||
Config: newNodeConfigSummaryView(n.GetConfig()),
|
||||
NodeID: n.GetNodeId(),
|
||||
Alias: n.GetAlias(),
|
||||
Label: n.GetLabel(),
|
||||
Connected: n.GetConnected(),
|
||||
Config: newNodeConfigSummaryView(n.GetConfig()),
|
||||
ProviderSnapshots: providerSnapshotViews(n.GetProviderSnapshots()),
|
||||
})
|
||||
}
|
||||
return edgeStatusResponseView{
|
||||
|
|
|
|||
|
|
@ -464,6 +464,32 @@ func TestEdgeRegistryHTTPHandlersGetLiveStatus(t *testing.T) {
|
|||
Concurrency: 4,
|
||||
},
|
||||
},
|
||||
ProviderSnapshots: []*iop.ProviderSnapshot{
|
||||
{
|
||||
Adapter: "vllm-gpu",
|
||||
Status: "available",
|
||||
Capacity: 4,
|
||||
InFlight: 1,
|
||||
Queued: 2,
|
||||
Id: "provider-vllm-primary",
|
||||
Type: "vllm",
|
||||
Category: "api",
|
||||
ServedModels: []string{"nvidia/Qwen3.6-35B-A3B-NVFP4"},
|
||||
Health: "available",
|
||||
LoadRatio: 0.25,
|
||||
LifecycleCapabilities: []string{"list_models", "load_model"},
|
||||
},
|
||||
{
|
||||
Adapter: "ollama-cpu",
|
||||
Status: "backlog",
|
||||
Capacity: 2,
|
||||
InFlight: 2,
|
||||
Queued: 5,
|
||||
Id: "provider-ollama-default",
|
||||
Type: "ollama",
|
||||
Health: "unavailable",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
|
@ -505,6 +531,41 @@ func TestEdgeRegistryHTTPHandlersGetLiveStatus(t *testing.T) {
|
|||
t.Fatalf("unexpected adapter summary: %+v", adapter)
|
||||
}
|
||||
|
||||
// Provider snapshots assertions
|
||||
if len(node.ProviderSnapshots) != 2 {
|
||||
t.Fatalf("expected 2 provider snapshots, got %d", len(node.ProviderSnapshots))
|
||||
}
|
||||
|
||||
// First provider snapshot (vllm-gpu)
|
||||
p0 := node.ProviderSnapshots[0]
|
||||
if p0.Adapter != "vllm-gpu" || p0.Status != "available" || p0.Capacity != 4 {
|
||||
t.Fatalf("unexpected provider snapshot[0]: %+v", p0)
|
||||
}
|
||||
if p0.InFlight != 1 || p0.Queued != 2 {
|
||||
t.Fatalf("unexpected in_flight/queued: in_flight=%d queued=%d", p0.InFlight, p0.Queued)
|
||||
}
|
||||
if p0.ID != "provider-vllm-primary" || p0.Type != "vllm" || p0.Category != "api" {
|
||||
t.Fatalf("unexpected id/type/category: id=%q type=%q category=%q", p0.ID, p0.Type, p0.Category)
|
||||
}
|
||||
if len(p0.ServedModels) != 1 || p0.ServedModels[0] != "nvidia/Qwen3.6-35B-A3B-NVFP4" {
|
||||
t.Fatalf("unexpected served_models: %+v", p0.ServedModels)
|
||||
}
|
||||
if p0.Health != "available" || p0.LoadRatio != 0.25 {
|
||||
t.Fatalf("unexpected health/load_ratio: health=%q load_ratio=%f", p0.Health, p0.LoadRatio)
|
||||
}
|
||||
if len(p0.LifecycleCapabilities) != 2 || p0.LifecycleCapabilities[0] != "list_models" || p0.LifecycleCapabilities[1] != "load_model" {
|
||||
t.Fatalf("unexpected lifecycle_capabilities: %+v", p0.LifecycleCapabilities)
|
||||
}
|
||||
|
||||
// Second provider snapshot (ollama-cpu)
|
||||
p1 := node.ProviderSnapshots[1]
|
||||
if p1.Adapter != "ollama-cpu" || p1.Status != "backlog" || p1.Health != "unavailable" {
|
||||
t.Fatalf("unexpected provider snapshot[1]: %+v", p1)
|
||||
}
|
||||
if p1.Capacity != 2 || p1.InFlight != 2 || p1.Queued != 5 {
|
||||
t.Fatalf("unexpected capacity/in_flight/queued for ollama: capacity=%d in_flight=%d queued=%d", p1.Capacity, p1.InFlight, p1.Queued)
|
||||
}
|
||||
|
||||
bodyStr := resp.Body.String()
|
||||
if strings.Contains(bodyStr, "super-secret-token") || strings.Contains(bodyStr, "secret_key") || strings.Contains(bodyStr, "settings") {
|
||||
t.Fatalf("leaked raw config settings in response: %s", bodyStr)
|
||||
|
|
@ -536,6 +597,64 @@ func TestEdgeRegistryHTTPHandlersGetLiveStatus(t *testing.T) {
|
|||
t.Fatalf("config summary missing key %q: %#v", required, rawConfig)
|
||||
}
|
||||
}
|
||||
|
||||
// Provider snapshots raw JSON assertions
|
||||
rawProviderSnapshots, ok := rawNode["provider_snapshots"].([]any)
|
||||
if !ok {
|
||||
t.Fatalf("provider_snapshots key missing or not an array: %#v", rawNode)
|
||||
}
|
||||
if len(rawProviderSnapshots) != 2 {
|
||||
t.Fatalf("expected 2 provider_snapshots in raw JSON, got %d", len(rawProviderSnapshots))
|
||||
}
|
||||
|
||||
rawP0, ok := rawProviderSnapshots[0].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("provider_snapshots[0] is not an object: %#v", rawProviderSnapshots[0])
|
||||
}
|
||||
if rawP0["adapter"] != "vllm-gpu" {
|
||||
t.Fatalf("provider_snapshots[0].adapter: got %v", rawP0["adapter"])
|
||||
}
|
||||
if rawP0["capacity"] != float64(4) {
|
||||
t.Fatalf("provider_snapshots[0].capacity: got %v", rawP0["capacity"])
|
||||
}
|
||||
if rawP0["load_ratio"] != float64(0.25) {
|
||||
t.Fatalf("provider_snapshots[0].load_ratio: got %v", rawP0["load_ratio"])
|
||||
}
|
||||
servedModels, ok := rawP0["served_models"].([]any)
|
||||
if !ok || len(servedModels) != 1 {
|
||||
t.Fatalf("provider_snapshots[0].served_models: got %#v", rawP0["served_models"])
|
||||
}
|
||||
if servedModels[0] != "nvidia/Qwen3.6-35B-A3B-NVFP4" {
|
||||
t.Fatalf("provider_snapshots[0].served_models[0]: got %v", servedModels[0])
|
||||
}
|
||||
lifecycleCaps, ok := rawP0["lifecycle_capabilities"].([]any)
|
||||
if !ok || len(lifecycleCaps) != 2 {
|
||||
t.Fatalf("provider_snapshots[0].lifecycle_capabilities: got %#v", rawP0["lifecycle_capabilities"])
|
||||
}
|
||||
if lifecycleCaps[0] != "list_models" || lifecycleCaps[1] != "load_model" {
|
||||
t.Fatalf("provider_snapshots[0].lifecycle_capabilities: got %v", lifecycleCaps)
|
||||
}
|
||||
|
||||
// Second provider snapshot raw check
|
||||
rawP1, ok := rawProviderSnapshots[1].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("provider_snapshots[1] is not an object: %#v", rawProviderSnapshots[1])
|
||||
}
|
||||
if rawP1["adapter"] != "ollama-cpu" || rawP1["health"] != "unavailable" {
|
||||
t.Fatalf("provider_snapshots[1] adapter/health: got %#v", rawP1)
|
||||
}
|
||||
// Zero load_ratio must be present in raw JSON (omitempty 제거 검증)
|
||||
if rawP1["load_ratio"] != float64(0) {
|
||||
t.Fatalf("provider_snapshots[1].load_ratio: expected 0 for idle provider, got %v", rawP1["load_ratio"])
|
||||
}
|
||||
|
||||
// Ensure raw JSON does not leak config internals into provider_snapshots
|
||||
providerSnapshotsStr, _ := json.Marshal(rawP0)
|
||||
for _, forbidden := range []string{"runtime", "settings", "secret"} {
|
||||
if strings.Contains(string(providerSnapshotsStr), forbidden) {
|
||||
t.Fatalf("provider_snapshot leaked raw config key %q: %s", forbidden, providerSnapshotsStr)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MissingEdge", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -179,6 +179,56 @@ func TestNodeRegisterUpsertsYAMLAndOutputsBootstrap(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNodesListPrintsConfiguredProviders(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cfgPath := filepath.Join(dir, "edge.yaml")
|
||||
yaml := `
|
||||
server:
|
||||
listen: "0.0.0.0:9090"
|
||||
nodes:
|
||||
- id: "node-gpu-01"
|
||||
alias: "gpu-node"
|
||||
token: "token-1"
|
||||
providers:
|
||||
- id: "vllm-gpu"
|
||||
type: "vllm"
|
||||
category: "api"
|
||||
adapter: "vllm-gpu"
|
||||
models:
|
||||
- "nvidia/Qwen3.6-35B-A3B-NVFP4"
|
||||
capacity: 4
|
||||
lifecycle_capabilities:
|
||||
- "list_models"
|
||||
- "load_model"
|
||||
`
|
||||
if err := os.WriteFile(cfgPath, []byte(yaml), 0o600); err != nil {
|
||||
t.Fatalf("write %s: %v", cfgPath, err)
|
||||
}
|
||||
|
||||
root := NewRoot(Options{})
|
||||
var out bytes.Buffer
|
||||
root.SetOut(&out)
|
||||
root.SetErr(&out)
|
||||
root.SetArgs([]string{"--config", cfgPath, "nodes", "list"})
|
||||
if err := root.Execute(); err != nil {
|
||||
t.Fatalf("execute nodes list: %v\n%s", err, out.String())
|
||||
}
|
||||
|
||||
got := out.String()
|
||||
for _, want := range []string{
|
||||
"node-gpu-01",
|
||||
"providers: vllm-gpu",
|
||||
"type=vllm/api",
|
||||
"models=nvidia/Qwen3.6-35B-A3B-NVFP4",
|
||||
"capacity=4",
|
||||
"lifecycle=list_models,load_model",
|
||||
} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf("nodes list output missing %q:\n%s", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchYAMLPreservesArtifactBootstrapField(t *testing.T) {
|
||||
origYAML := `server:
|
||||
listen: "0.0.0.0:9090"
|
||||
|
|
|
|||
|
|
@ -48,8 +48,44 @@ When the server is offline or this command is run standalone, all nodes will sho
|
|||
fmt.Fprintf(cmd.OutOrStdout(), "%s\n", strings.Repeat("-", 100))
|
||||
for _, n := range nodes {
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "%-25s %-20s %-40s %-12s\n", n.ID, n.Alias, n.Token, "offline (configured)")
|
||||
if summary := formatConfiguredProviders(n.Providers); summary != "" {
|
||||
fmt.Fprintf(cmd.OutOrStdout(), " providers: %s\n", summary)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func formatConfiguredProviders(providers []config.NodeProviderConf) string {
|
||||
if len(providers) == 0 {
|
||||
return ""
|
||||
}
|
||||
summaries := make([]string, 0, len(providers))
|
||||
for _, p := range providers {
|
||||
if p.ID == "" {
|
||||
continue
|
||||
}
|
||||
parts := []string{p.ID}
|
||||
if p.Type != "" || p.Category != "" {
|
||||
typeCategory := string(p.Category)
|
||||
if p.Type != "" && typeCategory != "" {
|
||||
typeCategory = p.Type + "/" + typeCategory
|
||||
} else if p.Type != "" {
|
||||
typeCategory = p.Type
|
||||
}
|
||||
parts = append(parts, "type="+typeCategory)
|
||||
}
|
||||
if len(p.Models) > 0 {
|
||||
parts = append(parts, "models="+strings.Join(p.Models, ","))
|
||||
}
|
||||
if p.Capacity > 0 {
|
||||
parts = append(parts, fmt.Sprintf("capacity=%d", p.Capacity))
|
||||
}
|
||||
if len(p.LifecycleCapabilities) > 0 {
|
||||
parts = append(parts, "lifecycle="+strings.Join(p.LifecycleCapabilities, ","))
|
||||
}
|
||||
summaries = append(summaries, strings.Join(parts, " "))
|
||||
}
|
||||
return strings.Join(summaries, "; ")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue