visionA/local-tool/.autoflow/04-architecture/reviews/architect-review-of-prd-and-design-v2.2-firmware.md
jim800121chen 46514d77d7 docs(local-tool): M9 — Kneron Dongle FW 偵測 + 升降版(A+B、翻案 R5-Q9)
L 級新功能、PRD/Design/TDD/ADR 三方協作 + 互審 + M9-6 SDK 雙驗證、總計 ~9000 行文件。

範圍:
- A 階段(MVP、5 人天):KL520 + KL720 自動升級 KDP1 → KDP2
- B 階段(10.5 人天):手動降版面向一般使用者 + KL630 / KL730 擴展
- 合計 15.5 人天、安裝包 +7MB(保守 bundle 策略)

關鍵決策:
- 翻案 R5-Q9(progress.md 第二輪使用者決策「韌體燒錄 flash → B 砍掉」)
- 跨平台用 KneronPLUS Python C API、不用 DFUT.exe
- 多版本目錄結構選 C metadata(firmware/<chip>/{version}/ + CURRENT_VERSION)
- Kneron firmware redistribution 授權與 R5-B4 預置模型同性質、發佈前評估

文件產出:
- PRD v2.2(PRD-v2.md 495 行 + features/feature-firmware-management.md 599 行)
- Design v2.2(firmware-management.md 948 行 + control-panel.md §6a graceful shutdown)
- TDD v2.2(v2/firmware-management.md 823 行 + ADR-001 218 行)
- 8 份 research(含 M9-6 弱驗證 + 強驗證、~3200 行)
- 3 份三方互審報告(PM/Design/Architect cross-review)

M9-6 強驗證重大發現(影響 B 階段):
- KL730 product_id 實際是 0x732(不是 0x0730)
- KL630/KL730 firmware 是 embedded Linux rootfs(不是 .bin、不同代設計)
- KneronPLUS Python 沒 update_kdp_firmware_from_files 公開 API、warrenchen 走 ctypes
- 不影響 A 階段、B 階段 M9-8 需 spike

下一步:派 backend M9-1 起跑(bridge.py handle_firmware_upgrade)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:40:56 +08:00

36 KiB
Raw Blame History

Architect 互審報告 — PM PRD + Design Specv2.2 Firmware 管理)

作者Architect Agent 日期2026-05-24 任務性質:三方互審 — Architect 視角審 PM PRD + Design Spec 對照基準:

  • 我的 TDD.autoflow/04-architecture/v2/firmware-management.md
  • 我的 ADR.autoflow/04-architecture/adr/ADR-001-firmware-management.md
  • 7 份 research.autoflow/04-architecture/research-kl520-fw-management/

TL;DR

整體判斷:PM PRD 與 Design Spec 技術上都可實現、與我的 TDD 高度一致、沒有架構衝突。 但有幾個對齊問題需要解決,主要落在三處:

  1. 狀態機名稱不對齊Design preparing/loading/flashing/verifying vs TDD connecting/loading_loader/loading_firmware/verifying)—— 必須我修 TDD(採 Design 的命名、對使用者語意較自然)
  2. 失敗類型 6 vs 8 不一致TDD 列 6 stage + 6 錯誤碼、Design 列 8 種失敗類型)—— Design 多列的 timeout + disconnect 是橫切性失敗、本來就在我的錯誤碼裡(FW_UPGRADE_FAILED / FW_UPGRADE_BRICK_RISK)—— 補一張對應表即可
  3. 降版進行中 graceful shutdown 拒絕Design §14.4 第 6 點自提懸念)—— 我 TDD 沒設計、必須補進 TDD §8

其餘對齊度高、issue 多為「補一句說明」級。M9-6 不卡 M9-1~5、A 階段可以照計畫啟動。


一、對 PM PRD 的審閱

1.1 AC-FW-* 技術可行性逐條檢視

AC ID PM 描述 技術可行性 對應我 TDD 位置 Issue
AC-FW-1.1 scan 後 FW badge 綠/黃/紅三色 可做 TDD §3.1 firmwareIsLegacy / firmwareCanUpgrade / bundledFirmwareVersion
AC-FW-1.2 progress modal 顯示 6 階段 ⚠️ 名稱不對齊 TDD §4.3 列 6 stage 必須修:見後文 §1.6
AC-FW-1.3 升級成功後 5 秒自動關閉 modal + rescan 可做 TDD §5.1 完成 → driver needsReset=true → 下次 connect 走完整 reset
AC-FW-1.4 失敗顯示明確錯誤類型 + 「複製錯誤訊息」 + 「重新插拔後重試」 可做 TDD §5.3 列 4 種失敗類型、與 Design §7.1 8 種對應見後文 §2.3
AC-FW-1.5 升級期間 device 進入 StatusUpgrading 鎖住其他操作 可做 TDD §2.2 新增 status enum + §3.3 FW_DEVICE_BUSY 409
AC-FW-1.6 升級成功後等 5-8 秒讓 USB stable ⚠️ 數字略有出入 TDD §5.1 / §7.2 用 sleep 2s + sleep 3s 兩段(合計 5s PM 寫 5-8s、TDD 寫 5s。PM 區間較保守、TDD 是實測值。建議 PM 改為「5-8 秒(實測 5s 已穩、保留上界容忍)」對齊
AC-FW-1.7 KL520 預估 30s、KL720 預估 180s 可做 TDD §3.3 FW_UPGRADE_FAILED timeout > 60s、FW_UPGRADE_BRICK_RISK timeout > 180s
AC-FW-1.8 升級失敗屬 device 層、不觸發 watchServer Error 可做 TDD §8.1 明確 watchServer 不被觸發
AC-FW-2.1 降版入口在 Settings →「韌體管理」 可做、與 Design §2 一致Settings 第 3 分頁) TDD §5.2 流程進入點
AC-FW-2.2~2.4 卡片 + dropdown + 紅色按鈕 純前端
AC-FW-2.5~2.7 二次確認 modal + 警告語 + 不可外部關閉 純前端
AC-FW-2.6 強制輸入「DOWNGRADE」字串 可做 TDD §3.1 / §3.3 FW_NO_CONFIRM_TOKEN 400
AC-FW-2.8 進行中不顯示取消按鈕 + persistent banner 純前端
AC-FW-2.9 API 強制 confirmToken: "DOWNGRADE" 可做 TDD §3.1 / §3.3 明確規格
AC-FW-2.10 Driver 4 項 safety guards 可做 TDD §2.1 firmware/guards.go
AC-FW-3.1~3.4 KL630/KL730 偵測 + 升降版 + ~60s 可做M9-7~M9-13 落地) TDD §6.2 chip 判斷擴展 + §9 M9-7~M9-10
AC-FW-3.5 若 SDK 不支援、降級為 FW 偵測 only ⚠️ PM 與 TDD 對齊但缺一條技術判定 trigger M9-6 結論後決定 詳見 §1.3

1.2 成功指標PRD §7的 measurement source 對應

PM 指標 我 TDD 是否埋點 Issue
5 分鐘首次推論達成率 +5pp 我 TDD 沒設計埋點 不是 FW feature 該埋的、應是既有 /api/system/metrics 或前端 analytics 範圍。建議 PM 在 §7.1 註明「指標 source 待定、跨多個 feature 統計」、不要當 FW feature 的 AC
FW 升級成功率 ≥ 95% ⚠️ 無自動上報 TDD §5.1 流程結束會有 {status:"upgraded", duration_ms} log、但不會自動上傳。現階段靠技術支援統計 log、不是自動指標。建議 PM 在 §7.2 註明「靠 log 統計、非自動上報、待 telemetry feature」
升降版平均時長 ⚠️ 同上 同上
一般使用者誤觸降版發生率 ≤ 1% 完全無法自動量測 純客服回報統計、非自動。PM §7.2 已標「客服回報」、OK
Brick 事件 ≤ 0.1% 完全無法自動量測 純客服回報、PM §7.2 已標、OK

結論PM §7 列的指標沒有違反技術可行性、但很多依賴客服回報 / 手動統計。建議 PM 在 §7 開頭加一句「v2.2 階段大部分指標靠客服回報 + log 統計、非自動上報;自動 telemetry 是未來 feature」、避免後續 Reviewer 誤判「指標沒實作」。

1.3 AC-FW-3.5 生效條件需要明確

PM 寫「若 KneronPLUS Python wheel 版本不支援 KL630/KL730 的 update API、降級為 FW 偵測 only」、但沒寫「不支援」的判定條件是什麼。我 TDD §6.3 / §7.3 描述了 M9-6 SDK 驗證的範圍、但沒明文說「如果 M9-6 跑出什麼結果 → 觸發 AC-FW-3.5」。

建議補充PM 或我都可以補):

AC-FW-3.5 生效條件M9-6 結論其一):
- 條件 aKneronPLUS Python wheel 沒有 `kp.core.update_kdp_firmware_from_files` 對 product_id 0x0630/0x0730 的 dispatch
- 條件 bSDK 對 .tar firmware 既不支援直接餵、也無法 build-time 解壓出可用 .bin策略 Z 與 Y 都失敗)
- 條件 c升級驗證階段 firmware 字串無法穩定回讀

任一條件成立 → AC-FW-3.5 觸發 → B 階段對 KL630/KL730 範圍縮限為「scan + connect + inference + FW 偵測」、移除 upgrade/downgrade UI。

1.4 工時對齊M9 表)

我 TDD §9 PM §10 對齊度
M9-0 0.5 文件先行 沒列 PM 沒把文件先行算工時、可接受(屬 Orchestrator 範圍)
M9-1 1.0 bridge.py upgrade M9-1 1.0
M9-2 1.0 driver + service M9-2 1.0
M9-3 0.5 API + WS M9-3 0.5
M9-4 1.5 Frontend M9-4 1.5
M9-5 1.0 三平台驗證 M9-5 1.0
M9-6 1.0 SDK 驗證 M9-6 1.0 (與 A 平行)
M9-7 0.5 chip 判斷 M9-7 1.5 ⚠️ PM 寫 1.5、我寫 0.5。0.5 是只改 detector + bridge dispatch1.5 是含 detector + chip-aware connect 全套(含 KL630/KL730 firmware 載入分支)。PM 比較保守、可採 PM 數字(多估留 buffer
M9-8 1.5 .tar handling M9-8 1.0 ⚠️ PM 偏緊、我偏鬆。建議採我 1.5(含策略 Y 解壓 fallback 風險)
M9-9 2.0 KL630/KL730 connect+inference M9-9 1.0 / M9-10 1.5 ⚠️ PM 拆成兩個 milestone多版本目錄重整 + KL630/KL730 升降版)= 2.5。我合併成 M9-9 2.0(純 connect+inference+ M9-10 1.0(升版擴展)= 3.0。兩邊算法不同、需要對齊。詳見 §1.5
M9-11 1.5 多版本降版 M9-11 1.5
M9-12 2.0 降版 UI M9-12 2.0
M9-13 1.0 B 階段驗證 M9-13 1.0
A 合計 5.0 5.0
B 合計 10.5 10.5
總計 15.5 15.5

結論A/B/總工時對齊、但中間 M9-7~M9-10 拆法不同。建議 PM 與我用同一張 M9 表(採 PM 拆法、語意較清楚)、我下輪修 TDD §9 對齊。

1.5 M9-7~M9-10 拆法差異

Milestone PM 拆法 TDD 拆法 建議統一
M9-7 Driver 擴 chip + chip-aware connect 分流1.5 人天) B0 認 chipdetector + bridge.py + driver0.5 人天 採 PM 的 1.5(含 chip-aware connect、語意清楚
M9-8 .tar handling1.0 B1.1 .tar handling1.5 採 TDD 的 1.5M9-6 結論若需策略 Y、解壓 build script 工程更大)
M9-9 多版本目錄重整1.0 B1.2 KL630/KL730 connect + inference2.0 不同 milestone、PM 與 TDD 對齊不到——詳見後文
M9-10 KL630/KL730 升降版1.5 B1.3 FW 升版擴 KL630/KL7301.0 同上

根因PM 把「多版本目錄重整」獨立成 M9-9、TDD 把它合進 M9-11PM 把「KL630/KL730 inference」併進 M9-7、TDD 拆出 M9-9。兩邊都對、只是拆法不同

建議統一(採 PM 拆法、語意對外部審閱者更清楚):

  • M9-7B0 認 chipdriver + bridge.py + detector1.5 人天
  • M9-8B1.1 .tar firmware handling 1.5 人天(採 TDD 數字)
  • M9-9多版本目錄重整 + _resolve_firmware_paths_versioned() 1.0 人天
  • M9-10KL630/KL730 升降版 driver method 1.5 人天
  • M9-11多版本降版後端 1.5 人天
  • 總和 7.0、+ M9-12 2.0 + M9-13 1.0 = B 階段 10.0、與 PM 10.5 差 0.5M9-9 KL630/KL730 inference 需要併進去?)

結論M9 表需要 PM 與 Architect 對一下、最終一張表。我下輪修 TDD 時對齊 PM 的 §10。

1.6 R-FW 風險清單對齊

我 TDD §10 PM §8 對齊度
R-FW-1 升級中拔除KL720 寫 flash brick R-FW-1 KL520 reset bug 再現 ⚠️ 語意不同PM 的 R-FW-1 是「升級後 device re-enumerate 不穩定」、TDD 的 R-FW-1 是「升級中拔 USB brick」
R-FW-2 升級後 USB race R-FW-2 KneronPLUS Python wheel update API 支援度 ⚠️ 同上、不對齊
R-FW-3 KL520 已是 KDP2 誤觸發 R-FW-3 bridge.py update_kdp_firmware_from_files macOS x86_64 未驗證 ⚠️ 同上
R-FW-4 跨晶片誤匹配 R-FW-4 timeout 設定不合理 ⚠️ 同上
R-FW-5 Kneron 授權 R-FW-5 打包 Kneron 官方 firmware 是否合法 同概念、PM 標 P0 release gate與我 ADR §Consequences 對齊)
R-FW-6 HTTP timeout R-FW-6 既有 flash/ 模組命名混淆 ⚠️ 同上
R-FW-7 Windows adminWinUSB R-FW-7 升級失敗 device unknown state ⚠️ 同上
R-FW-8 SDK 對 KL630/KL730 API R-FW-8 同
R-FW-9 .tar 安裝包衝擊 R-FW-9 同
R-FW-10 KL630/KL730 沒 Loader mode R-FW-10 同
R-FW-11 一般使用者誤觸降版 R-FW-11 同
R-FW-12 多版本管理 UX 複雜度 R-FW-12 同
R-TAR-1~4我 TDD 自己加的 4 條) PM 沒列、應補進 PRD §8

結論

  1. R-FW-1~7 的編號 PM 與 TDD 完全不對齊——可能是 PM 引用了 research 30 的早期編號、我 TDD 內部重新編了。這是重大問題、必須對齊
  2. 建議統一:採 PM 的 R-FW-1~7 編號(因為 PRD 已是「對外的」風險清單、跨多個下游 agent 引用)、我下輪修 TDD §10 重新對應。
  3. R-TAR-1~4 PM 沒列M9-8 .tar handling 的 4 條技術風險(策略 Z 退回 Y / build script 漏跑 / notarization / 跨平台 tarfile—— PM 應補進 PRD §8 或同意「這 4 條留 TDD §10 即可、不進 PRD」。建議後者PRD 是給 PM 視角、TAR 風險偏技術細節。

1.7 Kneron 授權 P0 懸念對齊

PM PRD §8.1 R-FW-5 標 P0 release gate、§9 Q-FW-1 寫「使用者明示先不管、發佈前再評估」。 我 ADR-001 §Consequences 寫「法律 / 簽章授權待釐清、與 R5-B4 同性質、發佈前統一處理、不阻塞開發但 PRD P0 懸念」。 我 ADR-001 §Compliance 標 [ ] 與 Kneron 取得 firmware redistribution 授權(發佈前必須完成、不阻塞開發)

對齊度 完全一致。R5-B4 合併處理也與我 ADR §Related 對齊。

1.8 scope 邊界對齊

範圍 PM PRD §2.4 / §3 我 TDD §1.2 對齊度
升級到 Kneron 官方 KDP2 標準版本
使用者燒任意 model 到 device flash 不做Q9 維持) 不做
使用者選任意 binary 寫 flash 不做 不做
偵測 dongle 當前 FW 版本
切換到內建其他 bundle 版本(含降版)
線上 OTA 更新通道 不做 不做
KL530 / KL830 支援 未提及 不做(本期) ⚠️ PM 沒列「不做 KL530/KL830」、建議 PM §3 補一條「本期不做 KL530 / KL830warrenchen 雲端版雖列出、本期排除)」
DFUT.exe 救磚工具打包 未提及 不做 ⚠️ 同上、PM 應補

結論scope 大方向一致、但 PM 漏列兩個「不做」KL530/KL830 + DFUT.exe建議 PM 補進 §3 範圍切割表

1.9 B2 一般使用者降版 — driver/bridge guard 對齊

PM AC-FW-2.10 列了 4 項 driver 層 safety guards

  1. 拒絕跨晶片誤匹配
  2. 拒絕升版偽裝
  3. 拒絕 no-op
  4. 拒絕 status busy

我 TDD §2.1 / §3.3 對應如下:

  1. firmware/guards.go 拒絕跨晶片
  2. firmware/guards.go 拒絕升版偽裝(FW_INVALID_DIRECTION 400
  3. firmware/guards.go 拒絕 no-op同上錯誤碼
  4. FW_DEVICE_BUSY 409

對齊度 完全一致、bridge.py _validate_downgrade_request 也對應上。


二、對 Design Spec 的審閱

2.1 §11.2 token 對比比率:誰負責新增 design tokens

Design §11.2 列了 6 個新 component-level tokenscolor.fw-badge.*+ 推導表Light/Dark 各 6 個值)+ 對比比率4.7:1 / 5.2:1 / 4.8:1

問題:這些 token 需要寫進 frontend/src/app/globals.css:root[data-theme='dark'] block、不是寫進設計文件就會自動生效。

架構層判斷

  • Design 文件已明確規格、Frontend 實作時對著抄即可
  • 不需要新增到 spec/07-design-tokens.md(既有檔不動、避免架構文件膨脹)
  • 責任歸 Frontend AgentM9-4A 階段、Devices 頁 FW badge實作時順手加 6 個 CSS variable

Dark 模式 legacy.fg 用黑底紅對比的驗證Design 推算 4.8:1、實作時 Frontend 用 contrast checker 驗證(如 https://webaim.org/resources/contrastchecker/)。我不需要 verify 推算數字、信任 Design 的 oklch 推算。

結論Design §11.2 設計合理、無架構層 issue。Frontend Agent 在 M9-4 落地時負責 CSS variable 寫入 + 驗證

2.2 §6.1 「DOWNGRADE」嚴格比對 — 與 TDD §3.1/§3.3 對齊

Design §6.1 規格:

  • input type="text" autocomplete="off" spellcheck="false"
  • 比對方式:event.target.value === 'DOWNGRADE'嚴格相等、case-sensitive、不接受小寫/全形/半形/空白)
  • 「確認切換」按鈕 disabled 條件input 值不等於字面 DOWNGRADE

我 TDD §3.1 / §3.3

  • API request body {version:"v2.1.0", confirmToken:"DOWNGRADE"}
  • API 強制 confirmToken === "DOWNGRADE"(字面字串)
  • 沒帶 / 帶錯 → 400 + FW_NO_CONFIRM_TOKEN

對齊度 前後端規格完全一致。

Design §14.4 第 4 點懸念(中文 i18n 化)Design 建議「短期維持英文字串、跨語言一致」。 我的判斷 同意。後端 confirmToken 是 API contract、不應 i18n 化(會引入 server 端多語對照表、增加攻擊面)。保留英文字面 "DOWNGRADE"、UI 層用 i18n key 顯示提示文字(「請輸入 DOWNGRADE 確認」中文 + 「Type DOWNGRADE to confirm」英文但 input 比對的字串一致

2.3 §7.1 8 種失敗 stage vs TDD §3.3 6 種錯誤碼 + §5.3 4 種失敗類型對應

Design §7.1 列的 8 種(後端錯誤 stage → 友善訊息):

  1. scan 找不到裝置
  2. connect 失敗
  3. loader 寫入失敗
  4. upgrade 寫入失敗
  5. verify 失敗
  6. Timeout (>60s)
  7. Disconnect during op
  8. (無第 8 項、Design 表格只有 7 列、但 §14.3 表寫「8 種失敗類型對應」、可能是 Design 自己估算誤差)

我 TDD §3.3 列的 6 個錯誤碼HTTP 層):

  1. FW_DEVICE_BUSY 409
  2. FW_VERSION_NOT_FOUND 404
  3. FW_INVALID_DIRECTION 400
  4. FW_NO_CONFIRM_TOKEN 400
  5. FW_UPGRADE_FAILED 500
  6. FW_UPGRADE_BRICK_RISK 500

我 TDD §5.3 列的 4 種失敗類型(流程失敗、不是 HTTP 錯誤碼):

  1. device disconnect during upgrade
  2. firmware load 失敗pre-flash
  3. firmware load 失敗中段、timeout > 180s
  4. 升級成功但 verify 失敗

對應表(補進 TDD 與 Design 都需要):

Design §7.1 後端 stage 我 TDD §3.3 錯誤碼 我 TDD §5.3 失敗類型 對齊
scan 找不到裝置 FW_UPGRADE_FAILED (1) disconnect
connect 失敗 FW_UPGRADE_FAILED (1) disconnect
loader 寫入失敗 FW_UPGRADE_FAILED (2) pre-flash
upgrade 寫入失敗 FW_UPGRADE_FAILEDFW_UPGRADE_BRICK_RISK (3) 中段 / (2) pre-flash
verify 失敗 FW_UPGRADE_FAILED (4) verify
Timeout (>60s) FW_UPGRADE_FAILEDFW_UPGRADE_BRICK_RISK (3) 中段
Disconnect during op FW_UPGRADE_BRICK_RISK (1) disconnect

結論Design 8 種(含 1 種空項)= TDD 6 錯誤碼 + 4 失敗類型的不同切面。沒有矛盾、只是 granularity 不同

  • Design 是「使用者面友善訊息」的分類(給 i18n 用)
  • TDD §3.3 是「HTTP API 錯誤碼」(給 frontend 處理)
  • TDD §5.3 是「服務內部失敗分類」(給 bridge.py 處理)

Issue我 TDD 必須補一張「stage → 錯誤碼 → 失敗類型」對應表、讓 Frontend / Testing 不會疑惑「Design 列的 8 種對應後端哪個 code」。下輪修 TDD §3.3 後追加 §3.4「stage 對應 friendly message + 錯誤碼」。

2.4 §8 狀態機名稱 vs TDD §4.3 stage 列舉 — 必須對齊

Design §8 狀態 我 TDD §4.3 stage 對齊度
idle OKDesign 用、TDD 是 device status
confirming OK純前端狀態
preparing connecting 不對齊preparing vs connecting
loading loading_loader 不對齊
flashing loading_firmware 不對齊
verifying verifying
success_toast done ⚠️ 不同切面toast vs stage 完成)
error_modal error ⚠️ 同上

根因Design 命名是 UI 視角preparing 比 connecting 對使用者更友善、TDD 命名是技術視角connecting 對應實際的 USB connect 動作)。

判斷Design 的命名對使用者較自然、TDD 應該採 Design 的命名作為 WebSocket event 的 stage。理由:

  • progress event 的 stage 值最終會被 Frontend 用 i18n key lookupsettings.firmware.progress.stage.preparing)顯示給使用者
  • 如果 TDD 用 connecting 但 Design 的 i18n key 是 preparing、Frontend 需要做 mapping、增加複雜度
  • 一致命名讓 Backend / Frontend / Design / Testing 都用同一組詞

動作我下輪必須修 TDD §4.3、stage 列舉改為:

  • connectingpreparing5%
  • loading_loaderloading20%、僅 KDP1→KDP2 路徑)
  • loading_firmwareflashing50%
  • verifyingverifying90%、不變)
  • donedone100%、不變)
  • errorerror-1、不變

同時要修 TDD §5.1 流程圖中的 stage 名稱、§6.1 bridge.py handler 回傳的 stage 欄位 enum 值。Design §9 i18n keyssettings.firmware.progress.stage.scan/connect/loader/upgrade/verify)也需要 Design 對應修為 preparing/loading/flashing/verifying——或者 Design 維持原 key 名、我內部用 Design 的命名——這需要 Design + Architect 對一下。

建議統一(給 Orchestrator

  • Option ATDD 採 Design 的 preparing/loading/flashing/verifying、Design 的 i18n key 也改成 progress.stage.preparing
  • Option B兩邊維持各自命名、TDD 在 §4.3 加一張對應表「TDD stage → Design i18n key」

建議 Option A(更乾淨、減少 mapping 心智負擔。Design 互審我提一下這點、看 Design 是否同意改 i18n key。

2.5 §14.4 第 6 點:降版進行中 graceful shutdown 拒絕 — TDD 必須補設計

Design §14.4 第 6 點自提懸念:

「降版進行中關閉 Wails 控制台視窗會發生什麼:依 R5-2 控制台關閉 = 結束 server。降版進行中關控制台 = 中斷降版 = 可能 brick。Frontend / Backend 是否需要在控制台側也偵測『降版進行中』並擋下關閉動作?建議 Architect 補充。」

我 TDD 目前沒設計這個。檢視 TDD §8

  • §8.1 watchServer Error state 不被觸發
  • §8.2 KL520 reset bug 對齊
  • §8.5 既有 restartBridge() 不重用
  • 沒提「降版中 graceful shutdown」這條

Design 提的風險是真實的

  • R5-2 規定「關閉 Wails 視窗 = 結束 server」
  • 結束 server = SIGTERM Python sidecar → bridge.py handler 被中斷
  • 如果 handle_firmware_downgrade 正在 update_kdp_firmware_from_files 中段、SIGTERM 中斷 = device flash 寫到一半就停 = brick 風險

設計方向(補進 TDD §8.6

### 8.6 降版進行中 graceful shutdown 拒絕

當 server 收到 SIGTERM使用者關閉 Wails 視窗、或 systemd / Wails close handler 觸發)時:

1. server 進 `shutting_down` state、停止 accept 新 HTTP 連線
2. **檢查 `firmware.Service` 是否有 active task**`StatusUpgrading` / `StatusDowngrading` device
3. 如果有 active task
   a. server **拒絕 graceful shutdown**、回 Wails close handler 一個 `firmwareInProgress: true` 訊號
   b. Wails 控制台收到後**不關閉視窗**、顯示 modal「韌體切換進行中device X、為避免裝置損毀、無法關閉應用程式。請等待約 {遠端推算 ETA}」
   c. modal 不可關閉、不可 dismiss、只能等 firmware task 完成
   d. firmware task 完成後、Wails close handler 重試 graceful shutdown
4. 如果沒 active task正常 graceful shutdown既有 7+1s pattern

實作 hook 點:
- `server/internal/firmware/service.go`:新增 `HasActiveTask() bool` method
- Wails close handler`app.go` 或同等close 前先 query server `/api/firmware/status`、如果有 active task 顯示 modal 並阻擋 close
- bridge.pyfirmware handler 內加 signal handler 拒絕 SIGTERM如果有 active task

風險:
- 使用者強制殺 process`kill -9` / 強制關機)仍可能 brick、無法完全防範
- modal 阻擋使用者關 app 可能造成體驗困擾、但 brick 風險高於體驗困擾

結論我下輪必須補 TDD §8.6 + 在 §9 工時表反映M9-11 多版本降版後端 + 0.5 人天「graceful shutdown 拒絕」或併入 M9-11。Design 也應在 control-panel.md 補對應 modal 規格。

2.6 progress event schema 技術可行性

Design §6.3 進行中 UI 需要的欄位:

  • stage(階段名稱)
  • percentage(進度百分比)
  • elapsed(已耗時)
  • ETA(預估剩餘時間)

我 TDD §4.2 FirmwareProgress

  • Percent int
  • Stage string
  • Direction string
  • Message string
  • Error string

elapsedETA

技術可行性

  • elapsed 簡單、service goroutine 記錄 task 啟動 timestamp、每次 push event 算 time.Since(startTime)
  • ETA⚠️ 複雜。KneronPLUS C API 沒提供進度回傳(update_kdp_firmware_from_files 是 blocking call、percent 是 bridge.py 自己估算(依 stage hardcode。如果 stage 內部進度不可知、ETA 只能根據 stage 對應的「預估時長」算(如 flashing 階段預計 30s 內完成)、不準確。

結論

  • elapsed 可以加進 TDD §4.2
  • ETA 也可以加、但值是估算非精確、Design 應該在 UI 明示「預估剩餘 X 秒」Design §9.6 已有 settings.firmware.progress.estimatedRemaining key、寫「{seconds}s remaining」、OK

動作我下輪修 TDD §4.2、加 Elapsed int64 + ETA int64 欄位

2.7 52 個 i18n keys 與 TDD stage 列舉 / 錯誤碼一致性

Design §9 i18n key 類別 對應我 TDD 一致性
§9.1 頁面結構7 個) 純前端、與 TDD 無關
§9.2 裝置卡片11 個) 與 TDD §3.1 DeviceInfo 衍生欄位對齊
§9.3 版本切換 accordion11 個) 純前端、TDD §5.2 流程進入點
§9.4 升級確認 modal6 個) 純前端
§9.5 降版二次確認 modal13 個) 與 TDD §3.1/§3.3 confirmToken 對齊
§9.6 進度 modal10 個) ⚠️ stage 名稱不對齊(見 §2.4 修 §2.4 stage 命名同時對齊
§9.7 成功 toast5 個) 純前端
§9.8 失敗訊息13 個) 與 TDD §5.3 失敗類型對應、但對應表缺(見 §2.3 TDD 補對應表後一致
§9.9 Devices 頁 FW badge tooltip4 個) 與 TDD §3.1 firmwareIsLegacy 對齊

結論52 keys 大部分與 TDD 一致。只有 §9.6 進度 stage 命名需要在 §2.4 對齊後一併修

2.8 分頁順序「韌體」在硬體與模型之間

Design §2 把 Settings 從 4 變 5 分頁、新分頁「韌體」插在第 3 位(一般 / 硬體 / 韌體 / 模型 / 進階)。

架構層影響評估

  • IPC 路由 / API 路徑 無影響。我 TDD §3.1 API 路徑用 /api/devices/:id/firmware/*、與 Settings UI 分頁順序無關
  • i18n key namespace 無影響。Design 用 settings.firmware.*、與分頁順序無關
  • 既有 settings-update.md 的 4 分頁結構⚠️ 需要更新——Design §14.1 提到「settings-update.md 應在下次補丁加註『分頁順序見 firmware-management.md §2』」、但沒有改 settings-update.md 本身。建議 Design 在 v2.2 同步小修 settings-update.md一行註解即可

結論:架構層無反對。分頁順序純 UI 決策、Design 已論證合理(硬體 → 韌體 → 模型 由低階到高階)。

2.9 失敗復原 UX 的 backend 配合

Design §7.1 列的 retry / re-plug / 取得協助按鈕:

Design 按鈕 Backend 配合 是否需新 API
「重新插拔後重試」 既有 rescan + 重 POST upgrade 不需新 API
「重試」 既有 POST upgrade同一 device 不需新 API
「拔插後重試」 同上 不需新 API
「拔插後重新掃描」 既有 POST /api/devices/scan 不需新 API
「複製錯誤訊息」 ⚠️ 需 backend 回傳完整錯誤上下文 需新增
「取得協助」 ⚠️ 純前端連結i18n key 不需新 API

「複製錯誤訊息」的 backend 對齊

  • Design §7.2 失敗 modal 內展開「技術資訊」、顯示 stage / device / before / raw_error / duration_ms
  • 我 TDD §4.2 FirmwareProgress.Error string、§6.1 bridge.py 失敗回傳 {error:str, stage:"..."}
  • before(升級前的 firmware 字串)、duration_msdevice_id 在 error event 中

動作我下輪修 TDD §4.2、FirmwareProgress 在 error event 中應包含完整失敗 context

type FirmwareProgress struct {
    Percent     int    `json:"percent"`
    Stage       string `json:"stage"`
    Direction   string `json:"direction"`
    Message     string `json:"message,omitempty"`
    Error       string `json:"error,omitempty"`
    Elapsed     int64  `json:"elapsed_ms,omitempty"`
    ETA         int64  `json:"eta_ms,omitempty"`
    // 失敗時補
    DeviceID    string `json:"device_id,omitempty"`
    BeforeVer   string `json:"before_version,omitempty"`
    RawError    string `json:"raw_error,omitempty"`    // bridge.py 拋的原始 exception text
    ErrorCode   string `json:"error_code,omitempty"`   // 如 "fw_upgrade_stage_loader_E102"
}

2.10 R-FW-11.5 / R-FW-11.6 自提風險 — 是否併入 TDD §10

Design §12.2 / §12.3 自提兩個風險:

  • R-FW-11.5使用者輸入「downgrade」小寫繞過 — Frontend 用嚴格 === 比對、不用 .toUpperCase()
  • R-FW-11.6deep-link 跳轉自動展開 accordion 暗示降版 — 改為只 highlight 卡片邊框、不展開

架構層判斷

  • R-FW-11.5:這是 Frontend 實作細節、不是架構層風險。我 TDD §3.1 已要求 backend confirmToken === "DOWNGRADE" 嚴格比對(雙重保險)。不需要進 TDD §10 風險清單
  • R-FW-11.6:純 UX 設計、與架構無關。不需要進 TDD §10

結論:兩個風險都 Design 內部處理、不併入 TDD §10。但建議 PM PRD §8 補一條 R-FW-11.5Frontend 嚴格比對)、明文要求 Reviewer 在 M9-12 檢查 Frontend 是否用了 === 而非 .toUpperCase()


三、我自己 TDD 自查

審完 PM/Design 後、回頭看我的 TDD、發現以下需要下輪修改

3.1 必修項(影響跨 agent 對齊)

# TDD 章節 修什麼 來源
F1 §4.3 Stage 列舉 改名對齊 Designconnecting → preparing / loading_loader → loading / loading_firmware → flashing §2.4
F2 §3.4(新增) 加「stage → 錯誤碼 → 失敗類型」對應表(給 Frontend / Testing 用) §2.3
F3 §4.2 FirmwareProgress Elapsed int64 + ETA int64 + 失敗時補 DeviceID / BeforeVer / RawError / ErrorCode §2.6 / §2.9
F4 §8.6(新增) 補「降版進行中 graceful shutdown 拒絕」設計HasActiveTask + Wails close handler 攔截) §2.5
F5 §9 工時表 對齊 PM 的 M9-7~M9-10 拆法(採 PM 拆法) §1.5
F6 §10 R-FW 風險編號 對齊 PM 的 R-FW-1~7 編號(採 PM 編號) §1.6
F7 §5.1 / §6.1 stage 名稱對齊 F1包含 bridge.py handler 回傳的 stage enum 值) §2.4 連動

3.2 建議修但非必要

# TDD 章節 修什麼 來源
O1 §1.2 範圍邊界 補一條「不做 KL530 / KL830」雖然 1.2 寫了「KL530 / KL830 不做」、但 PM PRD §3 沒列、可以兩邊對齊一下) §1.8
O2 §10 R-TAR-1~4 PM 沒列、保留 TDD §10 即可PM 同意的話) §1.6
O3 §11.2 整合測試表 補「升級期間關 Wails 視窗」測試案例(驗證 §8.6 graceful shutdown 拒絕) §2.5 連動

3.3 不修項(自查後仍然成立)

# 之前提的 5 個 PM 互審注意 是否仍成立
1 §1.2 範圍邊界 R5-Q9 翻案 仍成立、PM PRD §2 完全對齊
2 §8.4 R5-B4 授權對齊 仍成立、PM PRD §8.1 R-FW-5 標 P0
3 §9 工時 15.5 對齊 總工時對齊、只是 M9-7~M9-10 拆法不同F5 修)
4 §11.4 回歸測試 PM PRD 沒寫測試細節、TDD 自帶 OK
# 之前提的 5 個 Design 互審注意 是否仍成立
1 §5.2 降版「DOWNGRADE」字面輸入 Design §6.1 已落地
2 §3.3 錯誤碼 + §5.3 失敗復原文案 Design §7.1 / §9.8 已落地、但需 F2 補對應表
3 §4.3 Stage 列舉 i18n keys ⚠️ 需 F1 修 stage 命名後對齊
4 §10 R-FW-11/12 UI 落地 Design §12.1 已落地、多層 safety net 完整
5 §4.4 多版本目錄 A → B2 migration Design 沒涉及、TDD §4.5 自帶 OK

四、結論

4.1 PM PRD通過 — 需小修

嚴重度 Issue 建議
Major M9 工時表 M9-7~M9-10 拆法與 TDD 不對齊 對齊一張表(採 PM 拆法)
Major R-FW-1~7 編號與 TDD 不對齊 對齊(採 PM 編號)
Minor AC-FW-3.5 生效條件不明 補 a/b/c 條件(見 §1.3
Minor §3 scope 邊界漏列「不做 KL530/KL830 + DFUT.exe」 PM 補 1 條
Minor §7 成功指標 measurement source 模糊 PM 在 §7 開頭補一句「靠客服回報 + log、非自動 telemetry」
Minor AC-FW-1.6 USB stable 等待時間 5-8s vs TDD 5s PM 改為「5-8 秒(實測 5s 已穩、保留上界容忍)」

4.2 Design Spec通過 — 需小修

嚴重度 Issue 建議
Major §8 狀態機名稱與 TDD §4.3 stage 不對齊 共識採 Design 命名preparing / loading / flashing / verifying、TDD 修 stage、Design i18n key 也對應修
Major §14.4 第 6 點 graceful shutdown 拒絕 Design 提出問題、TDD 補 §8.6 落地
Minor §11.2 token 對比比率 Dark legacy.fg 信任 Design 推算、Frontend M9-4 實作時 verify
Minor §7.1 8 種失敗 stage 對應不清晰 Design + TDD 共同補對應表(給 Frontend / Testing 用)
Minor progress event 缺 elapsed / ETA TDD §4.2 補欄位
Minor settings-update.md 沒更新分頁順序 Design 在 v2.2 同步小修一行

4.3 我自己 TDD需修 7 項必修 + 3 項建議

詳見 §3.1 / §3.2。我下輪修 TDD、目標:

  • F1-F7 全修(必修)
  • O1-O3 視情況修(建議)
  • ADR-001 不需修(仍然 Accepted、決策邏輯沒變

4.4 整體判斷

  • 架構層 通過、PM/Design/Architect 三方在 R5-Q9 翻案、A+B 範圍、+7MB 安裝包、CURRENT_VERSION 多版本目錄、二次確認 DOWNGRADE 字串、watchServer 解耦等核心架構決策完全對齊
  • 介面層⚠️ 需修 F1stage 命名)+ F2/F3progress event schema 補欄位)+ F4graceful shutdown 拒絕)
  • 工時 / 風險⚠️ 需修 F5M9 表)+ F6R-FW 編號)
  • M9-6 SDK 驗證:不卡 A 階段、M9-1~5 可以照計畫啟動

五、給 Orchestrator 的建議

5.1 互審後的處理順序(建議)

1. Orchestrator 收齊三方互審報告PM 審 Design+Architect、Design 審 PM+Architect、本報告 = Architect 審 PM+Design
2. 跨報告比對、識別「共識點」「分歧點」
3. 對齊以下幾個關鍵點:
   a. M9 工時表(採 PM 拆法、總和 15.5 不變)
   b. R-FW 編號(採 PM 編號)
   c. stage 命名(採 Design 命名preparing / loading / flashing / verifying
   d. graceful shutdown 拒絕(補 TDD §8.6 + Design control-panel.md 補 modal
4. 確認分歧後、派 Architect 修 TDDF1-F7 + 視情況 O1-O3
5. 派 PM 微修 PRD§1.3 + §3 scope 補 + §7 measurement 註解 + §10 表 M9-7~M9-10 拆法 + R-FW-1~7 對齊)
6. 派 Design 微修§7 失敗對應表 + settings-update.md 一行 + 同意 stage 命名)
7. 第二輪互審(小規模、只審修改處)
8. 通過 → 進 M9-1 / M9-6 開發

5.2 不需要 Orchestrator 額外裁決的事

Item 為何不需裁決
token 對比比率 Frontend M9-4 實作時驗證、Design 推算先信任
R-FW-11.5 / R-FW-11.6 自提風險 Design 內部處理、不進 TDD §10
KL530/KL830 不做 三方都已隱性同意、PM 補一行即可
Confirm token 「DOWNGRADE」未來 i18n 化 短期維持英文、Architect + Design 已同意(不分語系)

5.3 需要 Orchestrator 裁決的事

Item 選項
stage 命名最終採哪一邊 A. 採 Designpreparing / loading / flashing / verifying 建議B. 採 TDDconnecting / loading_loader / loading_firmware / verifying
M9 表最終採哪一邊拆法 A. 採 PM 拆法 建議B. 採 TDD 拆法C. 折衷
graceful shutdown 拒絕的 modal Design 補 control-panel.md / Architect 補 TDD §8.6 / 工時影響(建議併入 M9-11

變更紀錄

日期 版本 變更 作者
2026-05-24 v1.0 初稿、Architect 視角審 PM PRD + Design Spec、識別 F1-F7 必修項 + 6 個 PM Minor + 6 個 Design Minor Architect Agent