# M9-5 三平台實機驗證計畫 — Kneron Dongle FW 升降版 > 對應 PRD:`.autoflow/02-prd/features/feature-firmware-management.md`(AC-FW-1.1 ~ AC-FW-1.9) > 對應 TDD:`.autoflow/04-architecture/v2/firmware-management.md` §11(測試策略) > 對應 Design:`.autoflow/03-design/v2/firmware-management.md` §7(失敗復原 UX 8 種) > 階段:**A 階段最後 milestone**(M9-5、1 人天、Testing 出 plan + 自動化腳本、使用者下週實機驗證) > 版本:v1.0(2026-05-25) > 作者:Testing Agent --- ## 0. 文件結構導覽 | 章節 | 主題 | |------|------| | §1 | 範圍與驗證目標 | | §2 | 測試環境前置(硬體 + 軟體) | | §3 | 三平台 × 兩 chip × 四情境 × 一致驗證表(24 combo) | | §4 | 功能性驗證(Functional) — happy path、4 情境 | | §5 | 可靠性驗證(Reliability) — 失敗注入 8 種 reason | | §6 | 效能驗證(Performance) — 升級時長護欄 | | §7 | UX 驗證 — badge / modal / 復原 UI / R-FW-11 modal 不可關 | | §8 | 整合驗證 — SIGTERM / Wails OnBeforeClose / force-quit modal | | §9 | 自動化腳本對照表(E2E unit-style + 手動 checklist) | | §10 | 通過 / 失敗判定規則 | | §11 | Bug 回報格式 | | §12 | 下週執行建議順序(哪平台先 / 哪情境先) | | §13 | Pass → next-step / Fail → 派工建議 | --- ## 1. 範圍與驗證目標 ### 1.1 範圍邊界 **在範圍**(M9-5 必驗): - KL520 + KL720 兩 chip 的 FW 升級流程(A 階段、KDP1 → KDP2 + KDP2 → KDP2 short-circuit) - macOS / Windows / Linux 三平台 - 4 個升級情境(§4.1) - 8 種失敗注入路徑(§5.2) - AC-FW-1.1 ~ AC-FW-1.9 全部 acceptance criteria - §8.6 graceful shutdown + Wails OnBeforeClose force-quit modal **不在範圍**(B 階段或 post-launch): - KL630 / KL730 升降版(B 階段 M9-10 才開、AC-FW-3.5) - 手動降版(B 階段 M9-11/12) - 多版本目錄結構(B 階段 M9-9) - KneronPLUS wheel 升 3.1.2 跨主版本回歸(B 階段 M9-13) - Beta usability test(UR-1~3、post-launch) ### 1.2 驗證目標(pass criteria) | 類別 | 目標 | |------|------| | Functional | 4 情境 × 6 combo(3 平台 × 2 chip)= 24 個升級實驗、success rate ≥ 95% | | Reliability | 8 種 reason 注入測試、UI friendly message 正確顯示、no server crash | | Performance | KL520 ≤ 60s 上界、KL720 ≤ 200s 上界(AC-FW-1.7 + TDD §7.2 護欄) | | UX | Badge 4 色顯示正確、modal 不可關(R-FW-11)、復原 UI 對應 8 種 reason | | Integration | SIGTERM 延遲關閉、Wails OnBeforeClose force-quit modal 阻擋成功 | --- ## 2. 測試環境前置 ### 2.1 硬體(使用者已備齊) | 項目 | 說明 | |------|------| | KL520 dongle | USB Boot 版(KDP1 legacy)+ KDP2 standard 版各 1 根;若只有 KDP2 dongle、可從 KDP2 跑 short-circuit 路徑 | | KL720 dongle | 至少 1 根(已預燒 KDP2、會走 short-circuit) | | macOS 機器 | Apple Silicon 或 Intel x86_64(兩者皆可) | | Windows 機器 | Windows 10/11、x86_64、有 admin 權限(KneronPLUS WinUSB driver) | | Linux 機器 | Ubuntu 20.04+ 或同等 distro、udev rules 已綁定 | | USB hub | 推薦(拔插測試方便、不必碰主機 port) | ### 2.2 軟體 每平台需要: - visionA-local 最新 A 階段 build(含 M9-1 ~ M9-4 commits) - Chrome / Edge / Firefox 任一(測 UI、瀏覽器 tab) - Terminal / Console(看 server log、跑 `git log` 等) ### 2.3 測試資料 - 純淨環境:每平台跑前刪 `~/Library/Application Support/visiona-local/` 或同等位置、確保沒有 leftover state - KL520 KDP1 dongle 是必要的(最重要的 happy path);若沒有、降級為「KDP2 short-circuit only」+ §11 bug-report 指出此 gap --- ## 3. 三平台 × 兩 chip × 四情境驗證表 每個 cell = 1 個驗證實驗。理想全部 ✅,部分 ⚠ partial 可接受、❌ 阻擋發布。 | # | 平台 | Chip | 情境 | 預期 stage 序列 | timeout 上界 | 驗證標準 | |---|------|------|------|---------------|------------|---------| | 1 | macOS | KL520 | KDP1 → KDP2 完整升級(4 stage) | preparing → loading → flashing → verifying → done | 60s | §4.1 情境 A | | 2 | macOS | KL520 | KDP2 → KDP2 short-circuit(3 stage) | preparing → flashing → verifying → done | 60s | §4.1 情境 B | | 3 | macOS | KL720 | KDP2 升級 / load_firmware_from_file | preparing → flashing → verifying → done | 200s | §4.1 情境 C | | 4 | Windows | KL520 | KDP1 → KDP2 完整升級 | 同 1 | 60s | §4.1 情境 A | | 5 | Windows | KL520 | KDP2 short-circuit | 同 2 | 60s | §4.1 情境 B | | 6 | Windows | KL720 | KDP2 升級 | 同 3 | 200s | §4.1 情境 C | | 7 | Linux | KL520 | KDP1 → KDP2 完整升級 | 同 1 | 60s | §4.1 情境 A | | 8 | Linux | KL520 | KDP2 short-circuit | 同 2 | 60s | §4.1 情境 B | | 9 | Linux | KL720 | KDP2 升級 | 同 3 | 200s | §4.1 情境 C | | 10 | macOS | KL520 | 來回升級(情境 A 完成後再跑 A) | 兩次 1 stage 序列 | 各 60s | §4.1 情境 D | | 11 | Windows | KL520 | 來回升級 | 同 10 | 各 60s | §4.1 情境 D | | 12 | Linux | KL520 | 來回升級 | 同 10 | 各 60s | §4.1 情境 D | **Functional combo = 12**(3 平台 × 2 chip × 2 主情境 = 12;情境 D 是延伸驗證、加 3 combo = 15、但 reliability + UX + integration 各加部分 combo、合計約 24 個實驗)。 **Reliability 額外實驗**: - 13-20:每平台跑「拔 USB 中斷」「kill bridge.py」「kill server」「modal R-FW-11 不可關」4 種注入,至少 macOS + Windows 各 4 = 8 combo(Linux 補上 = 12) **Integration 額外實驗**: - 21-24:每平台跑「升級期間關 Wails 視窗→force-quit modal」「升級期間 SIGTERM→延遲關閉」2 種、合計 6 combo 合計 ~24 個 combo,使用者可依時間裁切(§12 給優先順序)。 --- ## 4. 功能性驗證(Functional) ### 4.1 4 個升級情境(每平台 × 每 chip 都跑) #### 情境 A:KDP1 → KDP2 完整升級(4 stage、AC-FW-1.2) **前置條件**:插一根 KL520 KDP1 legacy dongle(firmware 字串 `KDP Comp/U` 或類似)。 **測試步驟**: 1. 確認 visionA-local 已啟動、瀏覽器開 Devices 頁 2. 確認偵測到 dongle、卡片右上角 FW badge **紅色** + 文字「KDP1 (legacy)」+ tooltip「此韌體為舊版 KDP1...」 3. 點卡片內「升級到最新」按鈕(labels:`升級到 v2.2.0`) 4. 確認 confirm modal 顯示: - 標題「升級韌體」 - from/to:from = `KDP1`、to = `v2.2.0` - 警告:「升級過程約需 30 秒、期間切勿拔除裝置或關閉應用程式」 - 「開始升級」按鈕(primary、不是 destructive) 5. 點「開始升級」、modal 切到 upgrading phase 6. 確認 progress modal: - **不顯示** ✕ / 取消按鈕(R-FW-11 緩解) - progress bar 從 0% 開始 - 階段文字依序變化:「階段 1 / 4:準備(偵測 + 連接裝置)」→「階段 2 / 4:載入引導程式」→「階段 3 / 4:寫入韌體」→「階段 4 / 4:驗證完成」 - 紅色 banner「請勿拔除裝置」全程顯示 - 「已耗時」秒數遞增、「預估剩餘」秒數遞減(依 etaMs 計算) - ESC / 點外部不關 modal(捕獲、抖動或無反應) 7. 升級成功(30 秒內)、modal 短暫顯示綠勾 ✓ 「完成」+ afterVersion `v2.2.0` 8. 約 1.5 秒後 modal 自動關閉 9. 右上角 toast 出現:「KL520 #1 升級成功 — 從 KDP1 升級到 v2.2.0(耗時 X 秒)」、停留 6 秒 10. Devices 卡片 FW badge **變綠**、firmware version `v2.2.0` **Pass 條件**: - ✅ 全部步驟完成、所有 UI 元素如預期 - ✅ 升級在 60s 內完成(KL520 timeout 上界) - ✅ 4 stage 全部出現、順序正確 - ✅ Server log 沒有 panic、error #### 情境 B:KDP2 → KDP2 short-circuit(3 stage) **前置條件**:KL520 dongle 已是 KDP2、firmware 字串為 `v2.1.0` 或 `v2.2.x` 但比 bundled current 舊(觸發 yellow badge)。或情境 A 跑完後重新跑情境 B。 **測試步驟**: 1. Devices 頁卡片 FW badge **黃色**(older) 2. 點升級按鈕、confirm modal 3. 點「開始升級」、modal 切 upgrading 4. 確認 progress stage 序列:**3 stage**(不是 4):「階段 1 / 3:準備」→「階段 2 / 3:寫入韌體」→「階段 3 / 3:驗證完成」(**沒有** loading 階段) 5. 升級成功、badge 變綠 **Pass 條件**: - ✅ 3 stage 而不是 4 stage(核心差異、`stageOrdinal` helper 邏輯驗證) - ✅ 升級在 60s 內完成 #### 情境 C:KL720 升級 **前置條件**:KL720 dongle(firmware 為 KDP2,可能與 bundled current 一樣或舊)。 **測試步驟**: 1. Devices 頁卡片 badge:綠 (若 already current) 或黃 (older 可升) 2. 若是黃色,點升級 3. 同情境 A/B,但 estimated duration 標 `3 分鐘`(180 秒) 4. progress modal stage 序列同 KDP2 short-circuit **Pass 條件**: - ✅ 升級在 200s 內完成(KL720 timeout 上界) - ✅ progress 跑滿且沒有卡死 #### 情境 D:來回升級(regression) **前置條件**:情境 A 已跑完、再跑一次完整 KDP1 → KDP2 升級流程(如果有第二根 KL520 KDP1 legacy dongle,建議用第二根;否則只能跑一次)。 **測試步驟**: 1. 重新跑情境 A 2. 確認第二次升級不會: - 被「Error 15 SEND_DATA_TOO_LARGE」打斷(既有 KL520 reset bug fix 應該已處理 `needsReset=true`) - bridge.py 帶舊 firmware state 連線 3. 第二次升級流程跟第一次完全一樣 **Pass 條件**: - ✅ 沒有 Error 15 - ✅ 連續兩次升級都成功 ### 4.2 多裝置同時升級(per-device 隔離、firmware-store activeDeviceId 機制) **前置條件**:兩根 dongle 同時連接(建議 1× KL520 + 1× KL720)。 **測試步驟**: 1. Devices 頁顯示兩張卡片 2. 點 KL520 卡片的升級按鈕(modal 開) 3. 升級進行中、**切回 Devices 頁** 看另一張 KL720 卡片狀態 4. 點 KL720 卡片的升級按鈕 — 預期:**會被擋住**(因為 firmware-store 一次只允許一個 activeDeviceId、其他 device 的事件被忽略) - 或者 modal 開但顯示「裝置正在進行其他作業」(409 FW_DEVICE_BUSY) 5. 等 KL520 完成 6. 點 KL720 升級、流程正常 **Pass 條件**: - ✅ 第一個升級進行中、不會被第二個 modal 蓋掉 - ✅ Frontend store 不出現「兩個 progress event 互相覆蓋」 --- ## 5. 可靠性驗證(Reliability、失敗注入) ### 5.1 對應 Design §7.1 的 8 種失敗類型 | # | 失敗類型 | 注入方法 | 預期 reason | 預期 UI | |---|---------|---------|------------|--------| | 1 | scan_not_found | 升級前拔 USB → 立刻點升級 | `scan_not_found` | error modal「找不到裝置」+「重新插拔後重試」按鈕 | | 2 | connect_failed | 多開 visionA-local(兩 instance 搶 device)→ 點升級 | `connect_failed` | 「無法連接裝置」+「重試」按鈕 | | 3 | loader_write_failed | 升級到 loading stage 時 `pkill -9 python` | `loader_write_failed` | 「引導程式載入失敗」+「拔插後重試」 | | 4 | upgrade_mid_failed | 升級到 flashing stage 時 `pkill -9 python` | `upgrade_mid_failed` | 「韌體寫入失敗、聯絡技術支援」+「複製錯誤訊息」+「取得協助」(mailto:) | | 5 | disconnect_during_op | 升級到 flashing stage 時拔 USB | `disconnect_during_op` | brick warning banner + 「Contact Support」(**不顯示 retry 按鈕**) | | 6 | timeout | 修改 bridge.py 加 `time.sleep(70)` 模擬 KL520 ≥ 60s(或 KL720 ≥ 200s) | `timeout` | 「操作超時」+「拔插後重新掃描」 | | 7 | verify_mismatch | 升級成功但 backend 偽造 firmware 字串不對(需 mock 或實機難以重現) | `verify_mismatch` | brick warning + ContactSupport | | 8 | verify_not_found | 升級成功但 rescan 找不到 device | `verify_not_found` | brick warning + ContactSupport | **實機限制**:#7 verify_mismatch / #8 verify_not_found 在實機很難穩定重現、建議用 E2E 自動化(mock backend)覆蓋。 ### 5.2 注入測試步驟模板 對每個失敗類型: 1. 啟動 visionA-local 2. 跑情境 A 或 B 升級流程到 confirm modal 3. 點「開始升級」、進入 upgrading phase 4. 等到指定 stage、執行注入動作(拔 USB / kill process / etc.) 5. 觀察 UI 變化 6. 截圖 + 紀錄 timestamp ### 5.3 重點驗證項 對每個失敗: - [ ] error modal 顯示對應 friendly message(中文 / 英文視 i18n 設定) - [ ] 顯示 errorCode(如 `FW_E102`) - [ ] 技術資訊區可展開、包含 stage / reason / deviceId / rawError - [ ] 「複製錯誤訊息」按鈕可點、複製成功變「已複製 ✓」 - [ ] 若是 destructive reason(disconnect/verify_mismatch/verify_not_found): - [ ] 顯示 brick warning role=note - [ ] 不顯示 Retry 按鈕 - [ ] 顯示「Contact Support」destructive 按鈕(enabled、點擊開 mailto:) - [ ] 若是 recoverable reason(其他): - [ ] 顯示 Retry / ReplugRetry / Rescan 按鈕 - [ ] 點 Retry 後流程重啟(store 回 confirming phase + 重新打 API) - [ ] Server log 無 panic、無 crash - [ ] Devices 頁卡片狀態不會卡住、re-plug 後可繼續用 --- ## 6. 效能驗證(Performance) ### 6.1 升級時長護欄(AC-FW-1.7、TDD §7.2) | Chip | 預估 | 護欄上界 | 護欄超過行為 | |------|------|---------|-------------| | KL520 | ~30s(warrenchen 實測) | 60s | timeout、UI 顯示 `Reason="timeout"` | | KL720 | ~180s(warrenchen 實測) | 200s | timeout、UI 顯示 `Reason="timeout"` + `FW_UPGRADE_BRICK_RISK` | ### 6.2 量測方法 對每個 functional 升級實驗(§3 表格 1-12): 1. 開瀏覽器 DevTools Network tab、觀察 WS 「firmware_progress」event 時序 2. 紀錄: - `stage=preparing` event 第一次出現的 timestamp - `stage=done` event 出現的 timestamp - 差距 = 升級實測時長 3. 對照表格: | Combo | 預期時長 | 實測時長 | Pass / Fail | |-------|---------|---------|------------| | macOS × KL520 × KDP1→KDP2 | < 60s | | | | macOS × KL520 × short-circuit | < 60s | | | | macOS × KL720 | < 200s | | | | Windows × KL520 × KDP1→KDP2 | < 60s | | | | ... | | | | **Pass 條件**:所有 combo 升級時長 ≤ 護欄上界。 **Warn 條件**:時長 > 預估但 < 護欄、log 為 P3 改善項。 **Fail 條件**:時長 ≥ 護欄、阻擋發布、回 Backend 派工調 timeout / 修流程。 ### 6.3 progress event 推送頻率 - 預期:每 stage 切換時推 1 個 event、每秒至少 1 個 event(含 ETA 更新) - 若 stage 間 > 10s 沒有 event:UI ETA 失準、log 為 Backend P2 改善項 --- ## 7. UX 驗證 ### 7.1 Badge 4 色狀態(AC-FW-1.1) | State | 條件 | 顏色 | 文字 | |-------|------|------|------| | current | firmwareVersion = bundled current | 綠 | 版本字串(如 `v2.2.0`) | | older | firmwareVersion 比 current 舊(同 KDP2 系列)| 黃 | 版本字串 | | legacy | firmwareIsLegacy = true(KDP1)| 紅 | `KDP1` | | unknown | firmwareVersion 或 bundled 為空 / `unknown` | 灰 | `Unknown` 或 `Loading...` | **測試步驟**: 1. 在三平台分別跑:插一根 KDP1 legacy dongle、確認紅 badge 2. 升級到 v2.2.0 後、確認綠 badge 3. 修改 bundle CURRENT_VERSION(如果 A 階段沒實作則此步驟跳過、由 B 階段 M9-9 驗)→ 確認黃 badge 4. 拔 USB → 卡片消失(不該顯示灰 badge、直接 device 不在清單) 5. 連線中 bridge.py 沒回 firmware 字串 → 顯示灰 badge(過渡狀態) ### 7.2 升級 modal R-FW-11「不可關」驗證(AC-FW-1.9) 升級 upgrading phase 期間驗證: - [ ] ✕ 按鈕不存在(DialogContent showCloseButton={false}) - [ ] ESC 不關閉 modal - [ ] 點 modal 外部(黑色 overlay)不關閉 - [ ] 「取消」按鈕不存在 - [ ] 即使刷新瀏覽器頁面後再回來、升級期間仍在進行(progress modal 由 store 重新 hydrate) ### 7.3 失敗 modal 復原 UI(Design §7.2) | Reason | 主要按鈕 | 次要按鈕 | |--------|---------|---------| | scan_not_found | 「重新插拔後重試」 | 關閉 | | connect_failed | 「重試」 | 關閉 | | loader_write_failed | 「拔插後重試」 | 關閉 | | upgrade_mid_failed | 「重試」 | 關閉 | | timeout | 「拔插後重新掃描」 | 關閉 | | disconnect_during_op | **「Contact Support」**(destructive、mailto:) | 關閉、**無 retry** | | verify_mismatch | **「Contact Support」** | 關閉、**無 retry** | | verify_not_found | **「Contact Support」** | 關閉、**無 retry** | 每種失敗都驗: - [ ] 主要按鈕文案正確 - [ ] destructive reason 不顯示 Retry - [ ] Contact Support 點擊開 mailto: handler(subject + body 帶 errorCode + 技術資訊) - [ ] 「複製錯誤訊息」可複製、變「已複製 ✓」2 秒後復原 - [ ] errorCode 以 mono 字型顯示 ### 7.4 Success toast 行為 - [ ] toast 顯示「{deviceName} 升級成功 — 從 X 升級到 Y(耗時 N 秒)」 - [ ] toast 停留 6 秒(不是預設 4 秒) - [ ] toast 自動消失、不需手動關 - [ ] 升級成功後 Devices 頁自動 refresh、badge 變綠 --- ## 8. 整合驗證(Integration) ### 8.1 升級期間 SIGTERM 延遲關閉(TDD §8.6.1) **測試步驟**: 1. macOS / Linux:找出 server PID(`ps aux | grep visiona-local-server` 或 visionA-local 控制台顯示) 2. 啟動升級流程(情境 A 或 C、長一點的) 3. 升級到 flashing stage 時、執行 `kill -TERM ` 4. 預期: - server 不立即退出 - 透過 WS 廣播 `server:shutdown-pending` event 到 `"system"` room - shutdown 延遲到 firmware task 完成(最多 `firmware.MaxShutdownWait = 220s`) - firmware task 跑完才走原本 graceful shutdown 流程 5. 觀察 server log、應有: - `firmware: N active firmware task(s) detected, delaying shutdown up to ...` - firmware task 完成後、`firmware: all firmware tasks completed, proceeding to shutdown` 6. 升級結束後、server 才真正退出 7. Frontend modal 在 server 退出前該完成正常的 upgrade success toast 流程 **Pass 條件**: - ✅ server 不在升級進行中退出(不 brick) - ✅ SIGTERM event log 有 `delaying shutdown` 紀錄 - ✅ 升級成功完成、device 升級後狀態正常 ### 8.2 Wails OnBeforeClose force-quit modal(TDD §8.6.2 + visiona-local/firmware_close_guard.go) **測試步驟**: 1. 任一平台 + 任一 chip 2. 啟動升級流程(情境 A 或 C) 3. 升級到 flashing stage 時、點 Wails 視窗的 ✕ 關閉按鈕(macOS:紅色關閉、Windows:右上 ✕、Linux:window manager close) 4. 預期: - Wails 視窗 **不關閉** - Wails 收到 `OnBeforeClose` 後查 `/api/firmware/active-tasks` → `hasActive: true` - emit Wails event `app:firmware-in-progress` 帶 task payload - Frontend modal 攔截、顯示「韌體切換進行中、為避免裝置損毀、無法關閉應用程式」 - modal 顯示 task info:device name / chip / direction / stage / elapsedMs / etaSeconds - modal 顯示「繼續等待」(primary、回到原本升級流程)+ 「強制關閉」(destructive)按鈕 5. 點「強制關閉」、預期第二層 FORCE 確認 modal(要使用者輸入「FORCE」字串) 6. 不輸入 / 取消 → 升級繼續、modal 關閉、Wails 視窗回到正常 7. 輸入「FORCE」+ 確認 → `ConfirmForceClose` binding 被叫、Wails 走 graceful shutdown(會 brick 裝置、風險已接受、Design §6a) 8. 升級正常結束(不點強制關閉的情境)後、再點 ✕ → Wails 正常關閉 **Pass 條件**: - ✅ 升級進行中視窗無法關 - ✅ force-quit modal 顯示 + payload 正確 - ✅ 第二層 FORCE 確認可運作 - ✅ 升級完成後視窗正常可關 ### 8.3 升級期間 server crash 容忍 **測試步驟**: 1. 升級進行中、`kill -9 `(強制殺、不走 graceful shutdown) 2. Frontend WS 應於 3 秒內偵測到斷線、自動重連嘗試 3. server 重啟後(手動 / Wails 自動)、Frontend bootId 機制應觸發 reload 4. Devices 頁應重新 scan、卡片回到「device 連線中」或「沒有 device」狀態 5. 觀察 dongle 實體狀態 — 可能 brick(取決於升級 stage、屬接受風險、TDD §8.6.1 hard timeout 後也走 shutdown) **Pass 條件**: - ✅ Frontend 不卡死、UI 可繼續操作 - ✅ Server 重啟後狀態正常 - ⚠ Dongle 可能 brick(屬接受風險、需給技術支援 SOP 救磚) --- ## 9. 自動化腳本對照表 | 測試類型 | 涵蓋方式 | 對應檔 | |---------|---------|--------| | Backend smoke schema 對齊(MJ3) | 已修、go test 全綠 | `server/internal/api/ws/firmware_ws_test.go` | | Frontend store 邏輯 | 已有 vitest 60 tests 全綠 | `frontend/src/tests/stores/firmware-store.test.ts` | | Frontend component(badge / error view) | 已有 vitest 全綠 | `frontend/src/tests/components/firmware-*.test.tsx` | | **E2E happy path(mock WS + UI)** | M9-5 新增 vitest 風格 | `.autoflow/06-testing/scripts/firmware-upgrade-happy-path.spec.ts` | | **E2E 失敗注入(8 reason × UI)** | M9-5 新增 vitest 風格 | `.autoflow/06-testing/scripts/firmware-upgrade-error-recovery.spec.ts` | | **E2E R-FW-11 modal 不可關** | M9-5 新增 vitest 風格 | `.autoflow/06-testing/scripts/firmware-r-fw-11-modal-not-closable.spec.ts` | | **E2E Wails OnBeforeClose 攔截邏輯** | M9-5 新增(unit-style on Go) | `.autoflow/06-testing/scripts/wails-onbeforeclose-firmware-active.spec.ts` | | 三平台實機驗證 | 人工 checklist | `.autoflow/06-testing/scripts/manual-checklist-{macos,windows,linux}.md` | **注意**:因為 visionA-local 沒有 Playwright / Cypress、實機驗證需要實體 dongle、E2E 腳本走 **vitest + RTL + mock WS / mock fetch** 路線,模擬 backend 整條 stage 流轉、frontend 跑真實 component build。實機驗證走人工 checklist(§12)。 --- ## 10. 通過 / 失敗判定規則 ### 10.1 P0(阻擋發布) 任一發生 → A 階段不可進入發布流程: - Functional 升級成功率 < 90%(24 combo 中 ≥ 3 個失敗) - 升級期間 server crash / panic - 升級期間 dongle 100% brick(無法復原) - §8.1 SIGTERM 期間 server 不延遲關閉(直接中斷 firmware task) - §8.2 Wails 視窗在升級進行中可直接關閉(OnBeforeClose 沒生效) ### 10.2 P1(嚴重但可繞過) 任一發生 → 開 P1 bug ticket、視 release window 決定是否阻擋: - 升級時長 > 護欄但 < 護欄 × 1.5(KL520 60-90s / KL720 200-300s) - Frontend store 多裝置隔離失效(兩個 progress modal 互相覆蓋) - destructive reason 仍顯示 retry 按鈕 - force-quit modal 第二層 FORCE 確認字串可繞過 ### 10.3 P2(一般問題) - Badge 顏色顯示錯誤(如 older 顯示成 current) - toast 持續時間錯誤 - i18n key 漏譯 / 文案錯誤 ### 10.4 P3(改善建議) - progress event 推送頻率太低(卡頓 > 5s) - error modal 「技術資訊」展開後排版錯亂 --- ## 11. Bug 回報格式 對每個發現的問題,使用以下格式回報到 `.autoflow/06-testing/bugs/M9-5-BUG-{NN}.md`: ```markdown # M9-5-BUG-NN: [標題] ## 嚴重程度:P0 / P1 / P2 / P3 ## 發現方式:Functional / Reliability / Performance / UX / Integration ## 環境:macOS 14.3 / Windows 11 / Ubuntu 22.04 ## 對應驗證:§4.1 情境 A / §5.1 #3 loader_write_failed / etc. ## 對應 AC:AC-FW-1.X / R-FW-11 / R-FW-13 ## 重現步驟 1. ... 2. ... ## 預期行為 (依 PRD / TDD / Design Spec、引用具體章節) ## 實際行為 (觀察到的差異) ## 截圖 (路徑:.autoflow/06-testing/screenshots/M9-5-bug-NN-{step}.png) ## Server log 片段 ``` (從 ~/Library/Application Support/visiona-local/wails.log 或對應位置) ``` ## Frontend WS event log ``` (從 browser DevTools Network → WS frames) ``` ## 建議修復方向(如有) (不一定要、但 testing 觀察過程的線索) ``` --- ## 12. 下週執行建議順序(給使用者) ### 12.1 第 1 天:smoke + happy path(必驗、~3 小時) 優先順序: 1. **macOS × KL520 × KDP2 short-circuit**(最快、不需 KDP1 dongle、5 分鐘) 2. **macOS × KL520 × KDP1 → KDP2 完整升級**(happy path 核心、10 分鐘) 3. **macOS × KL720 × 升級**(單一 chip 驗證、5 分鐘 + 3 分鐘升級 = 10 分鐘) 4. 重複 1-3 在 Windows 5. 重複 1-3 在 Linux → 若 macOS 1-3 全綠、且 Windows / Linux 至少 1 個 combo 綠、可進入第 2 天。 → 若 macOS 任一失敗:停下、回報 bug、不繼續其他平台。 ### 12.2 第 2 天:reliability + UX(4 小時) 對 macOS(或第 1 天最穩的平台)跑: 1. R-FW-11 modal 不可關(5 分鐘) 2. Badge 4 色顯示(10 分鐘) 3. 失敗注入 #1 scan_not_found(5 分鐘) 4. 失敗注入 #3 loader_write_failed(10 分鐘) 5. 失敗注入 #4 upgrade_mid_failed(10 分鐘) 6. 失敗注入 #5 disconnect_during_op(10 分鐘) 7. 失敗注入 #6 timeout(10 分鐘) 8. UX checklist:toast / 復原 UI / mailto: 開啟(30 分鐘) 再對 Windows + Linux 跑 #5 disconnect_during_op + #4 upgrade_mid_failed(兩個 brick risk 最高的)作為 sanity check。 ### 12.3 第 3 天:integration(2-3 小時) 對任一最穩平台跑: 1. §8.1 SIGTERM 延遲關閉(macOS / Linux 推薦、Windows SIGTERM 不直觀) 2. §8.2 Wails OnBeforeClose force-quit modal(三平台都要驗) ### 12.4 整體建議優先順序 ``` P0 must-have(必跑): - §4.1 情境 A × 三平台(核心 happy path) - §4.1 情境 B × 三平台(short-circuit 驗證) - §8.2 Wails OnBeforeClose × 三平台(避免 brick) P1 should-have(強烈建議): - §4.1 情境 C × 三平台(KL720 驗證) - §5.1 #5 disconnect_during_op × 三平台(brick 風險最高) - §8.1 SIGTERM × macOS + Linux - §7.3 失敗 modal 復原 UI × 至少一個平台 P2 nice-to-have(時間允許): - §4.1 情境 D 來回升級 × 任一平台 - §5.1 #1-4 + #6 其他失敗注入 - §6 effective 時長量測 ``` --- ## 13. Pass → Next-step / Fail → 派工建議 ### 13.1 Pass(所有 P0 都綠 + P1 大部分綠) → A 階段可進入發布流程: 1. 給 Orchestrator 「A 階段交付」訊號 2. Orchestrator 啟動 Reviewer agent 對 M9-5 plan + scripts + manual checklist 做最終 review 3. Reviewer 通過後、A 階段五人天宣告完成、可進入: - 法律合規確認(Kneron firmware redistribution 授權、R-FW-5 / Q-FW-1) - 發布 A 階段 visionA-local(含 KL520/KL720 升級 only、KL630/KL730 延 B 階段) - **或** 進入 B 階段 M9-6 強驗證 + M9-7 啟動 ### 13.2 Fail(任一 P0 失敗 / 多個 P1 失敗) 依失敗類型分流派工建議: | Fail 類型 | 派誰 | |----------|------| | §4.1 升級流程崩潰、stage 不正確 | Backend(修 firmware_handler / bridge.py / driver) | | §5 reason 對應錯誤、UI friendly message 沒顯示 | Frontend(修 firmware-store errorMessageKeyFor / firmware-error-view) | | §6 時長 ≥ 護欄 | Backend(調 bridge.py timeout 或優化流程) | | §7.1 Badge 4 色錯誤 | Frontend(修 firmware-badge computeBadgeState) | | §7.2 R-FW-11 modal 可關 | Frontend(修 firmware-upgrade-dialog onOpenChange) | | §8.1 SIGTERM 不延遲 | Backend(修 firmware/shutdown.go + server/main.go signal handler) | | §8.2 Wails 視窗可關 | Wails layer(修 visiona-local/firmware_close_guard.go + frontend modal) | **回報給 Orchestrator 的 escalation 範本**: ``` [M9-5 驗證失敗] - 失敗 combo:[macOS × KL520 × KDP1→KDP2 / 情境 A] - 失敗類型:[P0 升級流程崩潰] - bug ticket:.autoflow/06-testing/bugs/M9-5-BUG-NN.md - 建議派工:Backend(修 bridge.py upgrade_mid 處理) - A 階段交付狀態:阻擋 ``` --- ## 14. 附錄 ### 14.1 重要 log 位置 | 平台 | wails.log | server.log | |------|-----------|-----------| | macOS | `~/Library/Application Support/visiona-local/wails.log` | `~/Library/Application Support/visiona-local/server.log` | | Linux | `~/.local/share/visiona-local/wails.log` | `~/.local/share/visiona-local/server.log` | | Windows | `%APPDATA%\visiona-local\wails.log` | `%APPDATA%\visiona-local\server.log` | ### 14.2 重要 WS endpoint - `ws://127.0.0.1:/ws/devices/:id/firmware-progress` — firmware progress room - broadcast `type=firmware_progress`、`stage=preparing|loading|flashing|verifying|done|error` ### 14.3 重要 API endpoint - `POST /api/devices/:id/firmware/upgrade` — 觸發升級 - `GET /api/firmware/active-tasks` — 查詢進行中 task(Wails 用) ### 14.4 主要相依檔(給 bug 回報時引用) | 檔 | 角色 | |----|------| | `server/internal/firmware/service.go` | 升級 service goroutine | | `server/internal/firmware/shutdown.go` | SIGTERM graceful shutdown | | `server/internal/api/handlers/firmware_handler.go` | API + WS broadcast | | `server/scripts/kneron_bridge.py` | bridge.py upgrade handler | | `visiona-local/firmware_close_guard.go` | Wails OnBeforeClose | | `frontend/src/components/firmware/firmware-upgrade-dialog.tsx` | modal lifecycle | | `frontend/src/stores/firmware-store.ts` | phase / reason mapping | --- ## 變更紀錄 | 日期 | 版本 | 變更 | 作者 | |------|------|------|------| | 2026-05-25 | v1.0 | 初版 — M9-5 三平台實機驗證 plan + 自動化腳本對照表 + 人工 checklist 索引 + 下週執行建議順序 | Testing Agent |