Files
awoooi/docs/superpowers/plans/2026-04-08-sprint5-complete-solution.md

26 KiB
Raw Blame History

Sprint 5 指令中心重設計 — 完整解決方案與整合計畫

建立: 2026-04-08 (台北) 建立者: Claude Code 狀態: 📋 待統帥審核 ADR: ADR-060 React Flow + elkjs (已批准)


一、現有架構與資料流完整地圖

1.1 資料從哪裡來?

┌─────────────────────────────────────────────────────────┐
│                    資料源 (SSOT)                          │
│  ops/monitoring/service-registry.yaml (717行)            │
│  定義: 6 K8s + 10 Docker + 1 Systemd + 6 Nodes +       │
│        6 Pages + 8 API + 4 AI = 41 個受監控實體          │
│        + 15 個告警模板 + 9 個自動修復動作                │
└────────────────────────┬────────────────────────────────┘
                         │
           ┌─────────────┼─────────────────┐
           ▼             ▼                 ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Prometheus   │ │ Alertmanager │ │ Blackbox Exporter │
│ 22 targets   │ │ 71 rules     │ │ HTTP/TCP probes   │
└──────┬───────┘ └──────┬───────┘ └────────┬──────────┘
       │                │                   │
       ▼                ▼                   ▼
┌──────────────────────────────────────────────────────┐
│               後端 API 層                              │
│  apps/api/src/services/host_aggregator.py (500+行)    │
│  HostAggregator.fetch_all() → 4 主機並行 TCP/HTTP 探測│
│  回傳: AggregatedStatus {hosts, services, metrics}    │
└────────────────────────┬─────────────────────────────┘
                         │
           ┌─────────────┼─────────────────┐
           ▼             ▼                 ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ GET          │ │ GET          │ │ GET              │
│ /dashboard   │ │ /dashboard/  │ │ /dashboard/hosts │
│ (聚合資料)   │ │ stream (SSE) │ │ (4主機概覽)      │
│ 389行        │ │ 每30秒推送   │ │                  │
└──────┬───────┘ └──────┬───────┘ └────────┬──────────┘
       │                │                   │
       ▼                ▼                   ▼
┌──────────────────────────────────────────────────────┐
│               前端 Store 層                            │
│  apps/web/src/stores/dashboard.store.ts (449行)       │
│  Zustand: Host[] + HostService[] + HostMetrics        │
│  SSE 自動重連 (exponential backoff)                   │
└────────────────────────┬─────────────────────────────┘
                         │
           ┌─────────────┼─────────────────┐
           ▼             ▼                 ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ page.tsx     │ │ topology/    │ │ neural-command/   │
│ (首頁)       │ │ page.tsx     │ │ page.tsx          │
│ HostGrid     │ │ (103行,簡單) │ │ (4元件,1052行)    │
│ MetricsStrip │ │              │ │                   │
└──────────────┘ └──────────────┘ └──────────────────┘

1.2 現有 API 回傳的完整資料結構

// GET /api/v1/dashboard 回傳:
interface DashboardResponse {
  timestamp: string
  environment: string
  mock_mode: boolean
  overall_status: 'healthy' | 'degraded' | 'unhealthy'
  hosts: HostStatusResponse[]      // 4 個主機
  alerts_count: number
  pending_approvals: number
}

interface HostStatusResponse {
  ip: string                       // "192.168.0.110"
  name: string                     // "DevOps 金庫"
  role: string                     // "devops" | "security" | "k3s" | "ai_web"
  status: string                   // "healthy" | "degraded" | "unhealthy" | "unreachable"
  services: ServiceStatus[]        // 每主機的服務清單
  metrics: HostMetricsResponse     // CPU/Memory/Disk/Load/Baseline
  last_check: string
}

interface ServiceStatus {
  name: string                     // "PostgreSQL"
  status: 'up' | 'down' | 'degraded'
  port: number                     // 5432
  latency_ms: number               // 2.3
  error: string | null
}

interface HostMetricsResponse {
  cpu_percent: number              // 45.2
  memory_percent: number           // 62.8
  disk_percent: number             // 71.0
  load_avg_1m: number              // 1.23
  uptime_hours: number             // 720
  cpu_baseline: BaselineResponse   // {baseline: 50, std: 10, sigma: -0.48}
  memory_baseline: BaselineResponse
}

結論: 拓撲圖需要的 90% 資料已經在 API 裡了。

1.3 缺什麼?需要擴充什麼?

需要的資料 現有來源 缺口 解決方案
主機清單 + 狀態 /dashboard hosts 直接用
每主機的服務 /dashboard hosts[].services 直接用
CPU/Memory 指標 /dashboard hosts[].metrics 直接用
即時更新 /dashboard/stream SSE 直接用
服務間依賴關係 不存在 新增靜態定義
群組分類 (拓撲層級) ⚠️ role 欄位只有 4 種 部分 擴充 role + group
AI 介入狀態 ⚠️ 沒有即時 field 擴充 SSE 事件
K8s Pod 明細 ⚠️ 只有 service 級 新增 k3s_monitor 整合

二、完整解決方案 — 分四個工作包

工作包 1: 前端拓撲元件 (核心)

目標: 建立 React Flow + elkjs 的嵌套群組拓撲圖元件

WP1-1: 安裝依賴

cd apps/web
npm install @xyflow/react elkjs

影響檔案:

  • apps/web/package.json (新增 2 deps)
  • apps/web/package-lock.json

驗證:

cd apps/web && node -e "require('@xyflow/react'); require('elkjs'); console.log('OK')"

WP1-2: 建立元件目錄

新增檔案清單 (8 個新檔案):

檔案 行數(預估) 功能
src/components/topology/index.ts 10 匯出
src/components/topology/ServiceTopology.tsx 150 主元件: ReactFlow + Controls + MiniMap
src/components/topology/nodes/GroupNode.tsx 120 群組節點: 展開/收合 + 摘要資訊
src/components/topology/nodes/ServiceNode.tsx 80 服務節點: 狀態燈 + 名稱 + 指標 (memo)
src/components/topology/edges/TopologyEdge.tsx 60 自定義邊線: 漸層 + 動畫
src/components/topology/hooks/useTopologyData.ts 100 從 dashboard.store → nodes/edges 轉換
src/components/topology/hooks/useElkLayout.ts 80 elkjs 嵌套佈局計算
src/components/topology/topology.css 30 邊線動畫 keyframes

總新增: ~630 行


WP1-3: ServiceNode.tsx 詳細設計

// 與現有架構的整合點:
// 1. 讀取 dashboard.store.ts 的 HostService 型別 (已定義)
// 2. 色彩來自 tailwind.config.ts 的 status.* (已定義)
// 3. Icon 來自 lucide-react (已安裝)
// 4. 字型 DM Mono / JetBrains Mono (已載入)

import { memo } from 'react'
import { Handle, Position, type NodeProps } from '@xyflow/react'
import { Server, Database, Globe, Brain, Shield, Box } from 'lucide-react'
import { cn } from '@/lib/utils'

// 服務類型 → Lucide icon 映射
const SERVICE_ICONS: Record<string, React.ElementType> = {
  'PostgreSQL': Database,
  'Redis': Database,
  'Ollama': Brain,
  'OpenClaw': Brain,
  'Harbor': Box,
  'Sentry': Shield,
  'Nginx': Globe,
  'K3s API': Server,
  // ... 完整映射
}

// 狀態 → tailwind.config.ts 色彩
const STATUS_COLORS = {
  up: 'border-status-healthy text-status-healthy',        // #22C55E
  down: 'border-status-severe text-status-severe',         // #FF3300
  degraded: 'border-status-warning text-status-warning',   // #F59E0B
  healthy: 'border-status-healthy text-status-healthy',
  thinking: 'border-status-thinking text-status-thinking',  // #8B5CF6
}

export const ServiceNode = memo(({ data }: NodeProps) => {
  const Icon = SERVICE_ICONS[data.name] || Server
  const colorClass = STATUS_COLORS[data.status] || 'border-border'
  
  return (
    <div className={cn(
      'bg-card border-[0.5px] rounded-[8px] px-3 py-2 min-w-[120px]',
      'shadow-sm hover:shadow-md transition-shadow',
      'font-body text-xs',
      colorClass
    )}>
      <Handle type="target" position={Position.Left} className="!bg-border !w-1.5 !h-1.5" />
      
      {/* 狀態燈 + 狀態文字 */}
      <div className="flex items-center gap-1.5 mb-1">
        <span className={cn('w-1.5 h-1.5 rounded-full', /* status bg */)} />
        <span className="text-[8px] text-text3 uppercase tracking-wider">{data.statusText}</span>
      </div>
      
      {/* Icon + 服務名稱 */}
      <div className="flex items-center gap-1.5">
        <Icon className="w-3.5 h-3.5 opacity-60" />
        <span className="font-semibold text-[11px]">{data.name}</span>
      </div>
      
      {/* 位置 + 類型 */}
      <div className="text-[8px] text-text3 font-mono mt-0.5">
        {data.port && `${data.host}:${data.port}`}
        {data.latency && ` · ${data.latency}ms`}
      </div>
      
      <Handle type="source" position={Position.Right} className="!bg-border !w-1.5 !h-1.5" />
    </div>
  )
}, (prev, next) => {
  // 自定義比較: 只在狀態/指標變化時 re-render
  return prev.data.status === next.data.status
    && prev.data.latency === next.data.latency
    && prev.data.cpu === next.data.cpu
})

與現有架構的整合:

  • HostService 型別來自 dashboard.store.ts L28-34 (已定義)
  • cn() 來自 @/lib/utils (已存在)
  • status.* 色彩來自 tailwind.config.ts L39-45 (已定義)
  • lucide-react 已安裝 (v0.577.0)

WP1-4: GroupNode.tsx 詳細設計

// 與現有架構的整合:
// 1. Host 型別來自 dashboard.store.ts (已定義)
// 2. HostGrid 的 statusColor() 邏輯可複用
// 3. useTranslations('topology') 用既有 i18n key

// 群組節點 = 4 主機 (110/112/120/188) + K3s + 外部
// 收合時: 顯示群組名稱 + 服務數 + 健康摘要
// 展開時: 內部節點由 elkjs 自動排列

// 群組分類 (對應 service-registry.yaml nodes[].role):
const GROUP_STYLES = {
  devops:   { border: 'border-blue-400/30',  bg: 'bg-blue-50/30',   label: '基礎設施' },
  security: { border: 'border-red-400/30',   bg: 'bg-red-50/30',    label: 'Security' },
  k3s:      { border: 'border-purple-400/30',bg: 'bg-purple-50/30', label: 'K3s 叢集' },
  ai_web:   { border: 'border-orange-400/30',bg: 'bg-orange-50/30', label: 'AI/數據中心' },
  external: { border: 'border-amber-400/30', bg: 'bg-amber-50/30',  label: '外部服務' },
}

WP1-5: useTopologyData.ts 詳細設計

// 關鍵: 從現有 dashboard.store 讀取 Host[],轉換為 React Flow nodes/edges

import { useDashboardStore } from '@/stores/dashboard.store'
import type { Node, Edge } from '@xyflow/react'

export function useTopologyData() {
  // 直接讀取現有 store — 不需要新的 API 呼叫!
  const hosts = useDashboardStore(s => s.hosts)
  const overallStatus = useDashboardStore(s => s.overallStatus)
  
  // 轉換 Host[] → React Flow Node[]
  const { nodes, edges } = useMemo(() => {
    const nodes: Node[] = []
    const edges: Edge[] = []
    
    // Step 1: 建立群組節點 (每個主機 = 一個群組)
    hosts.forEach(host => {
      nodes.push({
        id: `group-${host.ip}`,
        type: 'groupNode',
        data: {
          label: host.name,        // "DevOps 金庫"
          role: host.role,          // "devops"
          status: host.status,      // "healthy"
          serviceCount: host.services.length,
          healthySvc: host.services.filter(s => s.status === 'up').length,
          metrics: host.metrics,    // CPU/Memory 完整指標
        },
        position: { x: 0, y: 0 }, // elkjs 會計算
      })
      
      // Step 2: 建立服務節點 (每個服務 = 群組內的子節點)
      host.services.forEach(svc => {
        nodes.push({
          id: `svc-${host.ip}-${svc.name}`,
          type: 'serviceNode',
          parentId: `group-${host.ip}`,  // elkjs 嵌套!
          data: {
            name: svc.name,
            status: svc.status,
            port: svc.port,
            latency: svc.latency_ms,
            host: host.ip,
          },
          position: { x: 0, y: 0 },
        })
      })
    })
    
    // Step 3: 服務間依賴邊線 (靜態定義)
    DEPENDENCY_MAP.forEach(([from, to]) => {
      edges.push({
        id: `edge-${from}-${to}`,
        source: from,
        target: to,
        type: 'topologyEdge',
      })
    })
    
    return { nodes, edges }
  }, [hosts])
  
  return { nodes, edges, overallStatus }
}

與現有架構的整合:

  • useDashboardStore 來自 stores/dashboard.store.ts (已存在)
  • Host, HostService 型別已定義
  • 不需要新的 API 呼叫 — 直接用 store 裡的資料

WP1-6: 服務間依賴關係定義

// 靜態依賴定義 (對應 host_aggregator.py HOST_CONFIGS)
// 資料來源: service-registry.yaml + ConfigMap 環境變數

// 格式: [source_node_id, target_node_id]
export const DEPENDENCY_MAP: [string, string][] = [
  // K3s Pod 間依賴
  ['svc-K3s-awoooi-web', 'svc-K3s-awoooi-api'],
  ['svc-K3s-awoooi-api', 'svc-192.168.0.188-PostgreSQL'],
  ['svc-K3s-awoooi-api', 'svc-192.168.0.188-Redis'],
  ['svc-K3s-awoooi-api', 'svc-192.168.0.188-OpenClaw'],
  ['svc-K3s-awoooi-worker', 'svc-192.168.0.188-PostgreSQL'],
  ['svc-K3s-awoooi-worker', 'svc-192.168.0.188-Redis'],
  ['svc-K3s-awoooi-worker', 'svc-192.168.0.188-Ollama'],
  
  // AI 服務依賴
  ['svc-192.168.0.188-OpenClaw', 'svc-192.168.0.188-Ollama'],
  ['svc-192.168.0.188-OpenClaw', 'svc-192.168.0.188-Redis'],
  
  // 監控工具依賴
  ['svc-192.168.0.188-SigNoz', 'svc-192.168.0.188-ClickHouse'],
  
  // DevOps 依賴
  ['svc-K3s-awoooi-api', 'svc-192.168.0.110-Sentry'],
  ['svc-K3s-awoooi-web', 'svc-192.168.0.110-Sentry'],
]

// 這些依賴來自:
// 1. ConfigMap OLLAMA_URL → API 依賴 Ollama
// 2. ConfigMap OPENCLAW_URL → API 依賴 OpenClaw
// 3. config.py DATABASE_URL → API 依賴 PostgreSQL
// 4. config.py REDIS_URL → API 依賴 Redis
// 5. Sentry SDK → API/Web 依賴 Sentry

WP1-7: useElkLayout.ts 詳細設計

import ELK from 'elkjs/lib/elk.bundled'

const elk = new ELK()

export async function computeElkLayout(
  nodes: Node[],
  edges: Edge[],
  expanded: Set<string>, // 展開的群組 ID
): Promise<{ nodes: Node[], edges: Edge[] }> {
  
  // 建立 elk graph (嵌套結構)
  const elkGraph = {
    id: 'root',
    layoutOptions: {
      'elk.algorithm': 'layered',
      'elk.direction': 'RIGHT',
      'elk.spacing.nodeNode': '30',
      'elk.layered.spacing.nodeNodeBetweenLayers': '60',
      'elk.edgeRouting': 'ORTHOGONAL',
    },
    children: buildElkChildren(nodes, expanded),
    edges: edges.map(e => ({
      id: e.id,
      sources: [e.source],
      targets: [e.target],
    })),
  }
  
  const layout = await elk.layout(elkGraph)
  
  // 將 elk 計算的座標寫回 React Flow nodes
  return applyElkPositions(nodes, layout)
}

工作包 2: 後端 API 擴充

目標: 擴充 dashboard API 支援拓撲圖需要的額外資料

WP2-1: 擴充 DashboardResponse 加入 K8s Pod 明細

修改檔案: apps/api/src/api/v1/dashboard.py

# 新增: K8s Pod 狀態 (來自 k3s_monitor_service)
class PodStatusResponse(BaseModel):
    name: str
    namespace: str
    status: str  # Running, Pending, CrashLoopBackOff
    restart_count: int
    cpu_millicores: int | None
    memory_mb: int | None

class DashboardResponse(BaseModel):
    # ... 現有欄位保留 ...
    k8s_pods: list[PodStatusResponse] = []  # 新增
    ai_diagnosing: list[str] = []            # 新增: OpenClaw 正在分析的服務名稱

與現有架構整合:

  • k3s_monitor_service.py (9,077行) 已有完整的 Pod 查詢邏輯
  • 只需在 aggregated_to_response() 中加入 k3s 資料

WP2-2: 新增 AI 診斷狀態到 SSE

修改檔案: apps/api/src/api/v1/dashboard.pydashboard_update_loop()

# 在 SSE 推送中加入 AI 診斷狀態
# 來源: 從 Redis working memory 讀取正在分析的 incident
async def get_ai_diagnosing_services() -> list[str]:
    """取得 OpenClaw 正在分析的服務名稱"""
    from src.services.incident_service import get_incident_service
    incidents = await get_incident_service().list_active_incidents()
    return [i.affected_services[0] for i in incidents if i.affected_services]

工作包 3: 頁面整合

WP3-1: topology/page.tsx 全面重寫

修改檔案: apps/web/src/app/[locale]/topology/page.tsx 當前: 103 行 (簡單 host list) 目標: ~200 行 (ServiceTopology + 篩選器 + 控制列)

// 整合方式:
// 1. 保留 AppLayout wrapper (現有 layout 不動)
// 2. 替換內容為 ServiceTopology 元件
// 3. 加入篩選器 (按群組/狀態)
// 4. 保留現有 i18n key (topology.*)

import { ServiceTopology } from '@/components/topology'

export default function TopologyPage({ params }) {
  return (
    <AppLayout locale={params.locale}>
      <ServiceTopology
        mode="full"           // 完整模式: 可展開所有群組
        showControls={true}   // 顯示 zoom/fit/篩選
        showMiniMap={true}    // 顯示迷你地圖
      />
    </AppLayout>
  )
}

WP3-2: 首頁 page.tsx 嵌入迷你拓撲

修改檔案: apps/web/src/app/[locale]/page.tsx

// 整合方式:
// 1. 在現有 Metrics Strip 下方加入迷你拓撲 (收合模式)
// 2. 不移除現有 HostGrid — 兩者共存
// 3. 拓撲圖高度固定 300px不影響下方 Incident Feed

<ServiceTopology
  mode="compact"          // 收合模式: 只顯示群組
  showControls={false}    // 不顯示控制列
  showMiniMap={false}
  height={300}
/>

WP3-3: i18n 擴充

修改檔案: messages/zh-TW.json, messages/en.json

{
  "topology": {
    // 保留現有 key
    "title": "拓撲圖",
    "subtitle": "服務依賴與健康狀態",
    // 新增
    "expandAll": "全部展開",
    "collapseAll": "全部收合",
    "showAnomaliesOnly": "只看異常",
    "groupInfra": "基礎設施 (.110)",
    "groupAI": "AI/數據中心 (.188)",
    "groupK3s": "K3s 叢集",
    "groupExternal": "外部服務",
    "healthy": "健康",
    "warning": "警告",
    "critical": "異常",
    "aiDiagnosing": "AI 診斷中",
    "servicesCount": "{count} 個服務",
    "allHealthy": "全部健康",
    "expandGroup": "展開群組",
    "collapseGroup": "收合群組",
    "dependencies": "依賴關係",
    "noData": "無拓撲資料"
  }
}

工作包 4: Tailwind 設計系統擴充

修改檔案: apps/web/tailwind.config.ts

// 新增動畫 (在現有 animation 區塊下方):
animation: {
  // ... 現有 11 種動畫保留 ...
  'edge-flow': 'edge-flow 1.5s linear infinite',
  'ai-pulse': 'ai-pulse 2s ease-in-out infinite',
  'node-glow': 'node-glow 2s ease-in-out infinite',
},
keyframes: {
  // ... 現有 keyframes 保留 ...
  'edge-flow': {
    'to': { 'stroke-dashoffset': '-12' },
  },
  'ai-pulse': {
    '0%, 100%': { 'box-shadow': '0 0 0 0 rgba(74, 144, 217, 0.2)' },
    '50%': { 'box-shadow': '0 0 0 8px rgba(74, 144, 217, 0)' },
  },
  'node-glow': {
    '0%, 100%': { opacity: '1' },
    '50%': { opacity: '0.5' },
  },
}

三、完整檔案影響清單

新增檔案 (8 個)

# 檔案 行數 功能
1 components/topology/index.ts 10 匯出
2 components/topology/ServiceTopology.tsx 150 主元件
3 components/topology/nodes/GroupNode.tsx 120 群組節點
4 components/topology/nodes/ServiceNode.tsx 80 服務節點
5 components/topology/edges/TopologyEdge.tsx 60 自定義邊線
6 components/topology/hooks/useTopologyData.ts 100 資料轉換
7 components/topology/hooks/useElkLayout.ts 80 佈局引擎
8 components/topology/topology.css 30 動畫 CSS

新增總計: ~630 行

修改檔案 (6 個)

# 檔案 改動幅度 內容
1 apps/web/package.json +2 行 新增依賴
2 apps/web/tailwind.config.ts +15 行 新增動畫
3 apps/web/src/app/[locale]/topology/page.tsx 重寫 (~200行) 整合 ServiceTopology
4 apps/web/src/app/[locale]/page.tsx +10 行 嵌入迷你拓撲
5 apps/api/src/api/v1/dashboard.py +30 行 K8s Pod + AI 狀態
6 messages/zh-TW.json + en.json +20 行 i18n key

不修改的檔案 (穩定層)

檔案 原因
dashboard.store.ts 拓撲直接讀取現有 store不需改
host_aggregator.py 現有探測邏輯不動
service-registry.yaml SSOT 不需改
AppLayout/Header/Sidebar 拓撲頁掛在現有 layout 下
HostGrid 首頁保留,拓撲頁獨立
CI/CD workflows 新程式碼走現有 pipeline

四、細化實施步驟 (15 步)

Phase 1: 安裝 + 骨架 (2小時)

Step 動作 驗證
1.1 npm install @xyflow/react elkjs import 成功
1.2 建立 components/topology/ 目錄結構 ls 確認 8 檔案
1.3 tailwind.config.ts 加入 3 個動畫 build 無錯

Phase 2: 核心元件 (4小時)

Step 動作 驗證
2.1 實作 ServiceNode.tsx (memo + Tailwind + Lucide) 單一節點渲染正確
2.2 實作 GroupNode.tsx (展開/收合 + 摘要) 群組展開/收合切換
2.3 實作 TopologyEdge.tsx (漸層 + CSS 動畫) 三種邊線樣式
2.4 實作 useElkLayout.ts (elkjs 嵌套) 20 節點正確佈局

Phase 3: 資料整合 (3小時)

Step 動作 驗證
3.1 實作 useTopologyData.ts (store → nodes/edges) hosts 正確轉換
3.2 定義 DEPENDENCY_MAP (靜態依賴) 邊線正確連接
3.3 實作 ServiceTopology.tsx (組裝全部) 完整拓撲渲染

Phase 4: 頁面整合 (2小時)

Step 動作 驗證
4.1 topology/page.tsx 全面重寫 拓撲頁完整功能
4.2 page.tsx 嵌入迷你拓撲 首頁有拓撲區塊
4.3 i18n 擴充 所有文字有翻譯

Phase 5: 後端擴充 (2小時)

Step 動作 驗證
5.1 dashboard.py 加入 K8s Pod + AI 狀態 curl 回傳新欄位
5.2 SSE 推送加入 AI 診斷事件 SSE 有新事件類型

Phase 6: 驗收 (1小時)

Step 動作 驗證
6.1 TypeScript 編譯 (tsc --noEmit) 零錯誤
6.2 生產建置 (pnpm build) 建置成功
6.3 視覺驗收 (群組展開/收合/動畫) 統帥確認

五、前置條件 — 先 PoC 還是直接實作?

選項 A: 先做 PoC (建議)

Day 1: PoC (React Flow + elkjs 基本嵌套群組)
       → 部署到內網 http://192.168.0.188:8765/poc-topology.html
       → 統帥實際操作確認展開/收合/佈局效果
       
Day 2: 基於 PoC 回饋 → 製作 3+ 設計稿
       → 統帥選擇佈局比例/面板模式

Day 3-7: 正式實作 (Phase 1-6)

選項 B: 直接實作

Day 1-2: Phase 1-2 (安裝+核心元件)
Day 3:   Phase 3 (資料整合)
Day 4:   Phase 4 (頁面整合)
Day 5:   Phase 5-6 (後端+驗收)

六、需要統帥決策的事項

# 決策項 選項 影響
D1 先 PoC 還是直接實作? A: 先 PoC / B: 直接做 時程差 1-2 天
D2 拓撲圖放在哪裡? A: 替代首頁 / B: 首頁+拓撲頁並存 / C: 只在拓撲頁 影響 page.tsx
D3 K3s Pod 要不要展示? A: 只到服務級 / B: 展開到 Pod 級 影響資料量
D4 依賴關係定義方式 A: 靜態 TypeScript / B: 後端 API 動態 影響維護方式

七、與統帥截圖概念的對應

統帥截圖元素 實作方式 對應 WP
神經拓撲圖 (6節點) ServiceTopology + GroupNode WP1
KPI 橫條 (4指標) 現有 Metrics Strip (不動)
待核准任務 現有 /approvals/pending API
處置統計 (4色) Sprint 4 已實作
活動串流 現有 SSE /dashboard/stream
底部控制列 ServiceTopology controls prop WP1

八、風險矩陣

風險 機率 影響 緩解
elkjs 佈局效果不如預期 先做 PoC 驗證
節點數 100+ DOM 效能 React.memo + 收合群組
依賴關係定義維護 先靜態,未來 OTEL
與現有首頁衝突 拓撲圖是新增元件
build 大小增加 +270KB gzip 可接受