# M9-1 Reviewer Round 2 — bridge.py firmware_upgrade(第 2 輪修改驗證) > 審查日期:2026-05-25 > 範圍:backend 第 2 輪修改驗證、不重審第 1 輪細節 > 第 1 輪報告:`m9-1-bridge-firmware-upgrade-review.md` ## TL;DR **通過(with 1 Minor + 1 Suggestion)。建議解除 M9-2 阻擋、可啟動。** backend 第 2 輪修改紀律高、所有 Major / Minor 全部正確落地、4 個情境 stage 序列邏輯清晰可讀、ctypes 簽名測試補上、firmware 字串覆蓋從 substring → 顯式 enumeration + forward-compat。第 2 輪僅發現 1 個 Minor regression(test 檔留下 `_firmware_upgrade_start_ts` 死碼、不影響 prod 但 cleanup 不徹底)+ 1 個 Suggestion。**第 1 輪 8 項 issue 修了 8 項、新發現 0 Critical / 0 Major / 1 Minor / 1 Suggestion**。s1 / s2 follow-up 評估合理、不影響 M9-2 啟動。 --- ## 1. 第 1 輪 issue 修改驗證(逐項) | # | 第 1 輪 Issue | 第 2 輪修改位置 | 驗證結果 | |---|--------------|--------------|---------| | **M1** | `_FwError` / `_FwTimeoutError` / `_fw_handle_failure` 宣告位於 handler 之後、readability + 防禦問題 | `kneron_bridge.py:1535 / 1545 / 1553` 全部移到 handler(1587)之前 | ✅ 完全到位 | | **M2** | `needs_loader` 控制流隱式 | 1717-1722:三個顯式 bool(`needs_loader` / `should_run_loader_stage` / `loader_required_but_missing`)+ 註解四情境 | ✅ 完全到位、M9-2 Go driver 可對照註解 | | **M3** | substring match 對 KDP3 forward-compat 脆弱 | `_fw_classify_legacy` 重寫(1463-1508):`legacy_exact` set + `startswith("KDP1.")` + KDP2-9 prefix forward-compat | ✅ 完全到位 | | **m1** | `_FW_ALLOWED_CHIPS` 雙重防護 | 1180 constant + 1201 內部過 + 1204 字元防護 | ✅ 真的雙重防護、非冗餘 | | **m2** | libkplus fallback non-deterministic | 1324 `sorted()` + 1330 WARNING log | ✅ 完全到位 | | **m3** | double-disconnect risk | `_fw_handle_failure` 不再 disconnect、success 路徑兩處設 `dg=None`、finally 用 `if dg is not None` | ✅ Single owner 原則完整 | | **m4** | `_firmware_upgrade_start_ts` 全域變數 | prod code 全砍、SIGTERM closure capture | ⚠️ prod 完全到位、但 test 檔殘留死碼(標 m5) | | **s4 (1-4)** | 4 個 test case | 4 個新 test 全補(line 405-487 + 500-568) | ✅ 完全到位 | 第 1 輪 8 項 issue 修了 8 項、其中 7 項完全到位、1 項(m4)prod 完全到位但 test 殘留(標 Minor m5)。 --- ## 2. 第 2 輪新發現(regression risk) ### Critical / Major **無**。 ### 🟡 Minor | # | 軸 | 檔案:行 | 問題 | 建議修法 | |---|---|---------|------|---------| | **m5** | Correctness / Test hygiene | `test_kneron_bridge_firmware.py:616, 632` | m4 在 bridge.py 已砍 `_firmware_upgrade_start_ts`、但 test 仍 `bridge._firmware_upgrade_start_ts = 0.0`、`bridge._firmware_upgrade_start_ts = start_ts`。Python 動態 setattr 不會拋錯、但留下死碼掩蓋 m4 修改徹底性 | 刪除 line 616 + 632 兩行賦值、保留 615 `_firmware_upgrade_in_progress = False` | ### 💡 Suggestion | # | 軸 | 檔案:行 | 建議 | |---|---|---------|------| | s5 | Test | `test_kneron_bridge_firmware.py:680` | `test_sigterm_handler_unregistered_after_upgrade` 第二次呼叫 register 會覆蓋第一次的 `_fw_original_sigterm_handler`、測試邏輯能過但意圖不清。建議補註解「測試 register 的 idempotence」或拆兩個 test | --- ## 3. 4 個情境 stage 序列驗證(M2 重構後) | 情境 | 預期 stage 序列 | 實作確認 | 結果 | |------|---------------|---------|------| | 1. KL520 KDP1 legacy + loader.bin | preparing → loading → flashing → verifying → done | 1739-1783 loader stage + 1799-1812 `kp_load_firmware_from_file`;test `test_kl520_kdp1_legacy_full_5_stages`(line 214-247) | ✅ | | 2. KL520 KDP1 legacy 缺 loader.bin | preparing → error(loader_write_failed) | 1730-1736 raise `_FwError("loading", "loader_write_failed", ...)`;test `test_loader_write_failed`(line 346-358) | ✅ | | 3. KL720 KDP1 legacy 無 loader.bin(warrenchen 模式)| preparing → flashing → verifying → done | 1784-1788 `elif needs_loader` skip loading + 1813-1828 `kp_update_kdp_firmware_from_files(scpu, ncpu, True)`;test `test_kl720_kdp_legacy`(line 271-288) | ✅ | | 4. 已 KDP2(KL520 / KL720)| preparing → flashing → verifying → done | `_fw_classify_legacy` KDP2 prefix 命中 return False、走 warrenchen 模式;test `test_kl520_already_kdp2_short_circuit`(line 249-269) | ✅ | **四情境 stage 序列邏輯與 test 驗證皆完全對齊、M2 重構成功。** --- ## 4. s1 / s2 follow-up 評估 | follow-up | 留 follow-up 理由 | 對 M9-2 影響 | |-----------|----------------|------------| | **s1**(handler ~300 行抽 helper)| 抽 helper 需傳大量 shared state、closure 設計細節多、M2 已讓 main flow 可讀性大幅提升、ROI 不高 | ❌ 無影響、M9-2 看 bridge 介面而非內部結構 | | **s2**(poll loop 用 exponential backoff)| 實測 5s 已穩、上界 8s、最多 6 次 poll、CPU 開銷可忽略、純 micro-optimization | ❌ 無影響、Go driver 不參與 polling | 兩個 follow-up 都不阻擋 M9-2。 --- ## 5. TDD §6.1 對齊度 | 項目 | round 1 | round 2 | |------|---------|---------| | Handler input/output schema | 100% | 100% | | stage enum | 100% | 100% | | reason enum 8 種 | 7/8 | 7/8(`validate_failed` downgrade-only、A 階段不需) | | progress event schema | 100% | 100% | | Stage 觸發點 | ✅ | ✅ M2 重構後流程更清晰 | | Timeout 60s/200s | ✅ | ✅ | | USB stable 5-8s | ✅ | ✅ | | Graceful shutdown 拒絕 | ✅ bridge 端 | ✅ bridge 端 | | ctypes 走法 | ✅ | ✅ + binding 簽名 test | | MAGIC 值 | ✅ | ✅ | | 防 firmware 字串脆弱 | ⚠️ substring | ✅ 顯式 enumeration + forward-compat | 對齊度 **98% → 98%**(preparing stage 細分 scan/connect sub-message 留 M9-5);但 M2 + M3 對「未來 KDP3+ device 不會誤觸 loader」這個未來相容性問題顯著改善。 --- ## 6. 既有 6 個 handler 零改動驗證 | Handler | round 1 行為 | round 2 行為 | 結果 | |---------|------------|------------|------| | `handle_scan` / `handle_connect` / `handle_disconnect` / `handle_reset` / `handle_load_model` / `handle_inference` | 693-1202 | 693-1202 | ✅ 6/6 零改動、無 spillover | 第 2 輪修改完全限制在 firmware_upgrade 區段(1205-1983)+ `_resolve_firmware_paths_full`(1183-1240)+ 1180 `_FW_ALLOWED_CHIPS` constant。 --- ## 7. 9 個新增 tests 品質評估 | Test | 對應 | 品質 | |------|------|------| | `test_kl520_legacy_empty_firmware_string` | M3/s3 | ✅ 邊界完整 | | `test_kl520_legacy_usb_boot_strings` | M3/s3 | ✅ subTest 7 變體 | | `test_kl520_legacy_kdp1_variants` | M3/s3 | ✅ KDP1.x / 大小寫 | | `test_kdp3_kdp4_not_legacy` | s3 | ✅ **最關鍵**、KDP3-9 forward-compat | | `test_unknown_firmware_default_not_legacy` | M3 | ✅ 保守 default | | `test_loading_stage_disconnect_during_op` | s4(1) | ✅ 覆蓋 1764-1775 | | `test_loading_stage_reconnect_failed` | s4(2) | ✅ `call_count` 攔截 | | `test_failure_event_full_extra_fields` | s4(3) | ✅ 4 必填欄位 | | `test_libkplus_binding_signatures` | s4(4) | ✅ MockCDLL 設計巧妙 | 9 個新 test 都覆蓋盲點、命名清楚、docstring 帶 line ref、品質高。 --- ## 8. 是否阻擋 M9-2 **否、解除阻擋、可啟動 M9-2**。 理由: 1. 第 1 輪 3 Major 全部完全到位、其中 M2 控制流重構對 M9-2 Go driver 開發直接有幫助 2. M3 firmware 字串覆蓋解決未來 KDP3+ device brick 風險、長期穩定性關鍵改善 3. m5 是 test hygiene 問題、不阻擋功能 4. s1 / s2 follow-up 對 M9-2 介面層完全無影響 --- ## 9. 是否需 backend 第 3 輪修改 **否**。僅 1 Minor + 1 Suggestion、可在 M9-2 期間順手清掉或併入 M9-1 PR 收尾 cleanup commit。 --- ## 10. 結論 - **通過 with Minor**:m5 + s5 不阻擋 M9-2 - **M9-2 啟動建議**:立即派 backend - **m5 + s5 處理**:M9-2 期間順手清、不必為此單獨派 backend round 3