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

218 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TDD v2 — visionA-localRound-2 refactor 後)
> 作者Architect Agent
> 版本:**v2.2**
> 日期2026-05-24 初版 / 2026-05-25 三方互審後修FW 管理章節吸收 PM + Design 互審 + M9-6 弱驗證新事實)
> 狀態Draftv2.2 由 Architect 根據使用者 2026-05-24 拍板 FW 管理方案 A + B 產出、2026-05-25 三方互審 + 使用者拍板 stage 命名 / M9 工時拆法 / AC-FW-3.5 延後 三項裁決後修;待最終使用者確認)
> 取代:`TDD.md` v1.02026-04-11四輪修訂 + Plan B 補件)
> 決策來源:`progress.md` §「R5 第五輪使用者決策」+「R5-D 補充決策」+「R5-E 階段化啟動新指標」+「M9 Kneron Dongle FW 偵測 + 升降版」2026-05-24+「2026-05-25 三方互審後使用者裁決」
---
## 0. 版本資訊與變更摘要
### 0.0 v2.1 → v2.2 差異速覽2026-05-24 初版 / 2026-05-25 互審後修)
| # | 變更面向 | v2.1 | v2.2 | 觸發 |
|---|---------|------|------|------|
| 1 | **FW 管理** | 沿用既有 RAM-load only`load_firmware_from_file`)、無持久化升降版 | **新增 firmware module + ADR-001**A 階段自動升級 KDP1 → KDP2KL520/KL720+ B 階段 KL630/KL730 driver 擴展 + B2 階段手動降版(面向一般使用者)+ 多版本管理 | M9 啟動2026-05-24|
| 2 | **R5-Q9 翻案** | 「韌體燒錄 flash → B 砍掉」維持 | **範圍切割後翻案**:升級到 Kneron 官方 KDP2 標準版本納入範圍、「使用者燒任意 model 到 flash」繼續砍 | ADR-001 |
| 3 | **新模組** | — | `server/internal/firmware/`service / progress / versions / guards| ADR-001 |
| 4 | **bridge.py handler** | `handle_connect` / `handle_scan` / ... | + `handle_firmware_upgrade` / `handle_firmware_downgrade` / `handle_firmware_list_versions` + `handle_connect` chip 判斷擴 KL630/KL730 + `_resolve_firmware_paths` 支援 .tar / 多版本 | M9-1 / M9-7 / M9-11 |
| 5 | **多版本目錄結構** | 扁平 `firmware/<chip>/fw_*.bin` | **`firmware/<chip>/CURRENT_VERSION` + 版本子目錄**(選項 C metadata 結構A 階段保持扁平、B2 階段 migration | 使用者決策2026-05-24|
| 6 | **安裝包大小** | macOS dmg 163MB | **A 階段 +0KB / B 階段 +7MB保守 bundle 策略)→ ~170MB** | 使用者決策 |
| 7 | **milestone 數量** | M8-1 ~ M8-10 + M8-4b11 個) | **+M9-1 ~ M9-13**13 個新 milestone、5 + 10.5 = **15.5 人天**| M9 啟動 |
| 8 | **總工時估算** | ~12 人天M8| **+15.5 人天M9= 累計 ~27.5 人天** | 以上 |
| 9 | **新增檔案** | — | **`v2/firmware-management.md`** + **`adr/ADR-001-firmware-management.md`** | ADR-001 |
| 10 | **新風險** | R-v2-1 ~ R-v2-7 | + R-FW-1 ~ R-FW-13採 PM 編號 + M9-6 新增 R-FW-13+ R-TAR-1 ~ R-TAR-4TDD-only、合計 17 條、詳 `v2/firmware-management.md` §10| M9 + 2026-05-25 互審 |
| 11 | **stage 命名**2026-05-25 互審後修) | TDD 草案用 `connecting / loading_loader / loading_firmware / verifying` | **採 Design 命名 `preparing / loading / flashing / verifying`**(使用者拍板裁決) | 三方互審 + 使用者裁決 |
| 12 | **M9-7~M9-10 工時拆法**2026-05-25 互審後修) | TDD 自有拆法B0 認 chip 0.5 / .tar 1.5 / KL630 inference 2.0 / KL630 升版 1.0 | **採 PM 拆法**M9-7 1.5 / M9-8 1.0 / M9-9 1.0 / M9-10 1.5)、總和 5.0 不變 | 使用者拍板裁決 |
| 13 | **AC-FW-3.5 階段歸屬**2026-05-25 M9-6 弱驗證新增) | 未明示 A/B 階段 | **延後至 B 階段 M9-10**warrenchen 無 reference 實作 + 需實機驗證 KL630/KL730 適用性) | M9-6 弱驗證 |
| 14 | **wheel 三平台版本不一致**2026-05-25 M9-6 弱驗證新增) | 未察覺 | **macOS/Linux 2.0.0 / Windows 3.1.2**、A 階段不阻塞、B 階段啟動前必須統一 | M9-6 弱驗證 |
| 15 | **graceful shutdown 拒絕**2026-05-25 互審後修) | TDD 草案無 | **新增 §8.6**FW task active 期間 server 延遲 shutdown + Wails force-quit modal避免關 Wails 視窗中斷降版導致 brick | Design A-MID-1 |
| 16 | **`FirmwareProgress` schema**2026-05-25 互審後修) | 5 欄位 | **補 6 欄位**Reason / ElapsedMs / EtaMs / DeviceID / BeforeVer / RawError / ErrorCode給 Frontend ETA UI + 「複製錯誤訊息」+ i18n 對應 + 客服診斷用) | Design A-OK-2 / A-MID-3 + Architect F3 |
**未變更**v2.1 其餘設計保持不動Wails 視窗 = 控制台 UI、瀏覽器 tab 載業務、yt-dlp/Mock 砍除、ffmpeg LGPL 方案 B、CORS 白名單、boot-id 機制、LogBuffer 2000 行 ring buffer、state machine 5 個狀態、watchServer Error state、R5-E 6 階段啟動管線。
### 0.0a v2.0 → v2.1 差異速覽2026-04-14 歷史保留)
| # | 變更面向 | v2.0 | v2.1 | 觸發 |
|---|---------|------|------|------|
| 1 | **AC-1.3 啟動時間** | 10 秒硬指標 | **60 秒 + 6 階段進度顯示**20 s soft timeout / 60 s hard timeout| R5-E1~E6 |
| 2 | **自動開瀏覽器行為** | 首次啟動開一次(`autoOpenedThisSession` flag 控制 per-session-once| **每次 Start/Restart 成功都開**(砍 flagR5-D3| R5-D3 |
| 3 | **AutoOpenBrowser 預設值** | 三平台統一 true | **macOS/Windows true、Linux false**(分平台 default| R5-D2 |
| 4 | **Server 崩潰通知** | 僅控制台 Error banner | **Error banner + OS 原生通知並存**`notify.go` 三平台實作)| R5-D1 |
| 5 | **Shutdown grace period** | 5 s → 10 sArchitect Q4 提議)| **7 s + 1 s 顯示「停止中…」modal**PM Q4 定案)| PM Q4 |
| 6 | **Shutdown race condition** | 靠 polling 3 次失敗0.5-15 s 後顯示 overlay | **WebSocket `server:shutdown-imminent` 廣播**(秒內觸發 overlay| PM Minor 4 |
| 7 | **Restart port 處理** | 允許 fallback3721 → 3722 …)| **強制保留舊 port**,被佔用進 Error state | Architect F-2 |
| 8 | **Preferences 持久化** | 提及 atomic write 但未定位置 | **`<dataDir>/preferences.json` + write-rename**(完整 spec| PM §11-1 |
| 9 | **`videoIsURL` field 處置** | 「視情況保留或刪除」 | **明確刪除**grep 證實是 dead code| PM Minor 5 |
| 10 | **idle RAM 450 MB 目標** | 未澄清範圍 | **澄清不含瀏覽器 tab**(只計 Wails + Go server + Python| PM §11-3 |
| 11 | **日常啟動時間估算** | 無 | **新增估算 ~3.8 s**,符合 AC-2.1 ≤ 5 s | PM Minor 2 |
| 12 | **boot-id 生成方式** | 建議 google/uuid | **用 `crypto/rand` 16 bytes → hex**(不引依賴)| Architect Q3 |
| 13 | **navigator.language fallback** | 基本 startsWith('zh') | **強化:處理 C/POSIX/空字串 + hardcoded 英文 fallback** | Architect Q5 |
| 14 | **milestone 數量** | M8-1 ~ M8-1010 個)| **+M8-4b 階段化啟動**11 個) | R5-E |
| 15 | **總工時估算** | ~10 人天 | **~12 人天**R5-E +1 天、R5-D1 +0.3 天、PM Q4 +0.2 天、Minor 4 +0.3 天 + 其他 0.2 天)| 以上 |
| 16 | **新增檔案** | — | **`v2/startup-pipeline.md`**R5-E 實作細節) | R5-E |
**未變更**v2.0 其餘設計保持不動Wails 視窗 = 控制台 UI、瀏覽器 tab 載業務、yt-dlp/Mock 砍除、ffmpeg LGPL 方案 B、CORS 白名單、boot-id 機制、LogBuffer 2000 行 ring buffer、state machine 5 個狀態。
### 0.1 v1 → v2 差異速覽
| 面向 | v12026-04-11 | v22026-04-14 | 觸發決策 |
|------|-----------------|-----------------|---------|
| **Wails 視窗載入內容** | Splash → `window.location.replace` 跳到 Next.js 主 UIM7-B| Splash 路徑完全移除Wails 永遠停在「控制台 UI」server 狀態 / log panel / 啟停控制 / Open in Browser | R5-1 A+B+G |
| **使用者業務 UI 承載者** | Wails WebViewM7-B 是純 HTTP但仍在 WebView 內)| **瀏覽器 tab**Chrome / Safari / Edge`http://127.0.0.1:<port>/` | R5-1 |
| **yt-dlp 全套** | 保留M6 vendor 35 MB + handler + 前端 URL tab| **完全砍除**vendor + handler + frontend UI + i18n + bootstrap + installer payload | R5-7 前置M8-1 |
| **Mock 模式** | 保留(`--mock` flag + `VISIONA_MOCK` env + UI hint + 兩份 i18n | **完全砍除**Go mock driver / mock camera / env var / flag / UI 元件 / i18n keys | R5-5a |
| **ffmpeg 授權** | GPLevermeet / BtbN / johnvansickle`VISIONA_ALLOW_GPL_FFMPEG=1` release blocker | **LGPL 方案 B混合**Win/Linux 換 BtbN LGPLmacOS 自 build decoder-only ~20 MBbinary commit 到 `vendor/ffmpeg/macos/` | R5-6 / R5-6a / R5-6b |
| **ffprobe** | 未打包 | **三平台都一起包**ffmpeg + ffprobe | R5-6c |
| **Tray** | 第三輪 Q-A 砍掉 | 維持砍,不復議 | R5-3 |
| **關閉視窗行為** | Q7=B 傳統式(關 = 結束 app| 維持 Q7=B但新增關閉前先 `StopServer()` 優雅結束;瀏覽器 tab 偵測 server 離線後顯示全域 Offline Overlay | R5-2 |
| **Server 控制 bindings** | 只有 `GetServerStatus` / `GetServerURL` / `OpenBrowser`(隱式 start| 補齊 `StartServer` / `StopServer` / `RestartServer` / `GetRecentLogs` / `ClearLogs` / `GetSystemInfo` 及完整 state machine | R5-1 |
| **watchServer 失敗行為** | 3 次失敗 → `reportFatal` + `os.Exit(1)`app 一起死) | 3 次失敗 → 切 `ServerStateError`Wails app 保留讓使用者手動 Restart 或查 log | 三方共識 #10 |
| **自動開瀏覽器** | — | 首次 server 就緒自動開一次Settings 的 `openBrowserOnStart` 可關 | R5-4 |
| **CORS 政策** | 寬鬆(`Access-Control-Allow-Origin: <任意 Origin>`| 嚴格 whitelist `http://127.0.0.1:*` + `http://localhost:*`;其他 Origin → 不回 ACAO header / OPTIONS 405 | 三方共識 #5 |
| **綁定 interface** | `--host 127.0.0.1` | 維持不動(不做 LAN | R5-1 |
| **上傳影片副檔名** | `.mp4 / .avi / .mov` | `.mp4 / .avi / .mov / .mpeg / .mpg`(瀏覽器能吃 + Kneron pipeline 吃得到的交集) | 三方共識 #11 |
| **Boot-ID 機制** | 無 | 新增 `GET /api/system/boot-id`server 啟動時產生 UUID瀏覽器每 5 s pollboot-id 變更 → force reload | 三方共識 #14 |
| **控制台 UI 技術選型** | — | vanilla HTML/JS/CSS從現有 `visiona-local/frontend/` splash 改寫 | 三方共識 #7 |
| **沿用率** | — | **85-95%**(詳見 `v2/code-reuse-v2.md` | 三方共識 #1 |
| **總工時** | — | **~10 人天**,拆成 10 個 milestone詳見 `v2/milestone-plan.md` | 三方共識 #1 |
### 0.2 v1 未變的決策v2 繼續沿用)
- 三層程序模型Wails 殼 + Go server 子行程 + Python sidecar— D1
- Python runtime 雙策略bundled / system / auto— D2
- 完全放棄程式碼簽章macOS ad-hoc、Windows 無 Authenticode、Linux 無簽章)— D3
- x86_64 only三平台都不做 ARM — D4
- 預置模型全部打包(~73 MB不做 auto-update、不收 telemetry — D5
- 資料目錄位置、single-instance lock、舊資料目錄遷移、IPC raise 機制全部保留
- 首次安裝 ≤ 5 分鐘、首次推論 ≤ 30 s / 回訪 15 s 等 NFR 目標不變
- Ubuntu 與 Windows 打包流程AppImage / Inno Setup不變
- 中英雙語(前端)機制不變,控制台 UI 使用同一份 `en-US` / `zh-TW` JSON詳見 `v2/control-panel.md`
---
## 1. 新架構總覽
### 1.1 三層進程模型
```
Wails 殼(桌面控制台 UI
└─spawn─▶ Go server 子行程Gin HTTP + WebSocket on 127.0.0.1:random_port
├─spawn─▶ python3 kneron_bridge.pyKneronPLUS SDKsidecar
└─spawn─▶ ffmpeg / ffprobeon-demand 解碼)
Wails 殼 ←── IPC / Wails bindings ── Wails 視窗內的控制台 UIvanilla HTML/JS/CSS
Go server ←── HTTP / WebSocket over loopback ── 瀏覽器 tabNext.js Web UI業務操作全在這裡
```
**關鍵差別於 v1**Wails 視窗**不**載入業務 UI它是獨立的控制台status / log / start-stop / open-in-browser / preferences。業務 UI 在瀏覽器 tab 跑 `http://127.0.0.1:<port>/` 的 Next.js SPA。
完整 ASCII 架構圖詳見:`v2/control-panel.md` §3控制台 UI wireframe`architect-analysis-round2-refactor.md` §A1v1→v2 資料流對照)。
### 1.2 ServerController State Machine
五個狀態:`Stopped / Starting / Running / Stopping / Error`。轉換由 `ServerController.txMu + mu` 雙 mutex 保護,不可跳過中間狀態。
- **Start**`Stopped|Error → Starting → Running`(失敗走 Error
- **Stop**`Running → Stopping → Stopped`
- **Restart**`Running → Stopping → Stopped → Starting → Running`
- **watchServer 3 次失敗**`Running → Error`v1 是 `os.Exit`v2 改為 Error state 讓使用者手動復原)
完整細節見 `v2/server-lifecycle.md` §5。
### 1.3 資料流摘要
| 情境 | 簡述 |
|------|------|
| **冷啟動 + R5-4 自動開瀏覽器** | Wails `OnStartup` → 常規 seed / lock / IPC → `ServerController.Start()` → spawn server + logPump × 2 → 健康檢查 → `state = Running` → 若 `openBrowserOnStart` 且本 session 首次 → `OpenInBrowser("")` |
| **Log 推送到控制台** | server stdout/stderr → `cmd.StdoutPipe/StderrPipe``logPump` goroutinebufio scanner + 10 ms micro-batch→ 同時 (1) 寫 `logs/server.{stdout,stderr}.log` + (2) append 到 `LogBuffer`ring 2000 行)+ (3) `EventsEmit("log:append", []LogLine)` → Wails JS 訂閱 `EventsOn('log:append', ...)` → log panel 增量 render |
| **Restart 期間瀏覽器 tab 自動重連** | 使用者按 Restart → Stop → Start → server 新 boot-id → 瀏覽器 polling `/api/system/boot-id`5 s intervalPage Visibility API偵測到 id 變 → `window.location.reload()` |
| **關 Wails 視窗 (R5-2)** | `OnBeforeClose` return false → `OnShutdown` → watchCancel → `ServerController.Stop()`SIGTERM → 10 s → SIGKILL→ releaseLock → Wails quit。瀏覽器 tab 的 polling 連續 3 次失敗15 s`<ServerOfflineOverlay>` 顯示「Server 已離線」 |
詳細時序與 Go 實作見 `v2/server-lifecycle.md` §2-9瀏覽器端實作見 `v2/web-ui-offline-overlay.md`
---
## 2. 子檔案地圖
| # | 子檔 | 目的 | 對應 R5 決策 / M8 milestone |
|---|------|------|-----------------------------|
| 2.1 | [`v2/control-panel.md`](./v2/control-panel.md) | Wails 控制台 UI + Go App bindings + LogBuffer + log pump + 狀態機 + PreferencesR5-D2/D3+ OS 通知觸發點 | R5-1, R5-5, R5-D1/D2/D3, R5-EM8-4, M8-4b, M8-5 |
| 2.2 | [`v2/ffmpeg-lgpl.md`](./v2/ffmpeg-lgpl.md) | 三平台 LGPL ffmpeg vendor 策略Makefile patch + macOS build script + 授權檔管理) | R5-6, R5-6a, R5-6b, R5-6cM8-3 |
| 2.3 | [`v2/server-lifecycle.md`](./v2/server-lifecycle.md) | Server 生命週期細節state machine、port 分配F-2 強制保留、pipe 捕捉、7+1 秒 graceful shutdown、boot-id、OS 通知§10、Preferences 持久化§11 | R5-2, R5-4, R5-D1, PM Q4, F-2, PM §11-1/11-3M8-4, M8-9 |
| 2.4 | [`v2/cors-security.md`](./v2/cors-security.md) | CORS whitelist middleware、WS origin check、資料驗證邊界 | 三方共識 #5M8-8 |
| 2.5 | [`v2/deletions.md`](./v2/deletions.md) | yt-dlp / Mock 模式全清單(檔案 / 函式 / 行號 / i18n key / vendor / installerv2.1 修正 `videoIsURL` / `NewVideoSourceFromURL` 明確刪除 | R5-5a, R5-7, PM Minor 5M8-1, M8-2 |
| 2.6 | [`v2/web-ui-offline-overlay.md`](./v2/web-ui-offline-overlay.md) | 瀏覽器 tab 的 `<ServerOfflineOverlay>` 實作polling + **WebSocket shutdown-imminent** 雙管道、重試、SSR 相容 | R5-2 三方共識 #14, PM Minor 4M8-7 |
| 2.7 | [`v2/milestone-plan.md`](./v2/milestone-plan.md) | M8-1 ~ M8-10 + **M8-4b 階段化啟動**;總工時 ~12 人天 | 整體M8-* |
| 2.8 | [`v2/code-reuse-v2.md`](./v2/code-reuse-v2.md) | 逐模組沿用 / 改寫 / 新寫比例表 | 整體 |
| 2.9 | [`v2/startup-pipeline.md`](./v2/startup-pipeline.md) | **v2.1 新增**R5-E 6 階段啟動管線實作event schema、StartupPipeline struct、watcher goroutine、60 s hard timeout / 20 s soft timeout、前端 startup-panel.js| R5-E1~E6M8-4b |
| 2.10 | [`v2/firmware-management.md`](./v2/firmware-management.md) | **v2.2 新增**Kneron Dongle FW 偵測 + 升降版A 階段 KL520/KL720 自動升級 + B 階段 KL630/KL730 driver 擴展 + B2 階段手動降版 + 多版本管理 + CURRENT_VERSION metadata + .tar firmware 處理)| ADR-001M9-1 ~ M9-13 |
### 2.11 ADR 索引
| ADR | 主題 | Status |
|-----|------|--------|
| [`adr/ADR-001-firmware-management.md`](./adr/ADR-001-firmware-management.md) | Kneron Dongle FW 偵測 + 升降版(翻案 R5-Q9| Accepted2026-05-24|
---
## 3. 風險清單v2 更新)
繼承 v1 `risks-and-mitigations.md` 全部風險,以下為 v2 新增或升級的條目:
| # | 風險 | 等級 | 新增/升級 | 緩解 |
|---|------|------|----------|------|
| R-v2-1 | **M7-B M1 驗收漏看 Wails 視窗** 的教訓 — v2 控制台是全新 UI重複踩同樣坑的風險 | 🟠 中 | 新增 | M8-10 驗收 checklist 強制三個檢查:(1) 雙擊 .app / .exe / .AppImage 打開後 Wails 視窗顯示的是控制台 UI不是 splash / wizard / 白畫面);(2) 點 Open in Browser 後瀏覽器確實載入 Next.js(3) 點 Stop 後瀏覽器 tab 能看到 Offline Overlay。每個平台都要做 |
| R-v2-2 | **macOS 自 build ffmpeg 的可重現性** — LGPL 合規稽核時必須能證明 `vendor/ffmpeg/macos/ffmpeg` 是我們在特定 configure flags 下從特定 ffmpeg commit build 出來的 | 🔴 高 | 新增 | `vendor/ffmpeg/macos/BUILD.md` 必須記錄ffmpeg release tag`n7.1`、source tarball sha256、完整 `./configure` line、build hostmacOS version + Xcode CLT version、build date、binary sha256。未來升級 ffmpeg 時要重跑並更新 BUILD.md不能「手改一下再傳」。同步把 configure flags 寫進 `Makefile``vendor-ffmpeg-macos-build` target而非埋在 BUILD.md 內)讓其可 reproduce |
| R-v2-3 | **Wails EventsEmit 在高頻 stdout 下丟事件或延遲** — server boot 時 Gin / logger 一次可能噴 200+ 行;推論 frame log 若誤進 stdout 會產生秒級 30-100 行 | 🟠 中 | 新增v1 F-4 升級)| (1) logPump 加 micro-batch緩存 10 ms window 內的行,一次 emit 一個 `log:append` eventpayload 為陣列);(2) server 推論 frame 狀態禁止用 logger.Info改用 debug levelline-rate 測試會檢查此條);(3) 若 LogBuffer 滿 > 80% 時 logPump 降為只寫檔不 emit event控制台看到「…skipped N events…」提示使用者可 Clear Logs。實作細節見 `v2/control-panel.md` §4 |
| R-v2-4 | **Wails 關閉視窗 → StopServer 過程中瀏覽器 tab 的 race condition** — 使用者按 × → Wails OnBeforeClose → ServerController.Stop() → SIGTERM → wait → SIGKILL → Wails 退出,**整段 ~0.5-5s 內**瀏覽器 tab 的 polling 可能看到 ECONNREFUSED 但 Overlay 還沒觸發(需連續 3 次失敗15 s 才顯示) | 🟡 低 | 新增 | 實務上:使用者關了 Wails 視窗通常也會關瀏覽器 tabrace 不構成實際問題。若使用者真的沒關瀏覽器15 s 後 Overlay 會出現使用者看到「Server 已離線」訊息即可理解。不做額外優化Wails 關閉前主動告知瀏覽器 — 需要 Wails → Browser 的 push channel成本太高 |
| R-v2-5 | **macOS 自 build 的 ffmpeg 需要 codesign** — Gatekeeper 會擋未簽章的執行檔 | 🟠 中 | 新增 | (1) 在 macOS build script 最後做 `codesign --force --sign - ...`ad-hoc sign(2) `wails-macos` target 的 `codesign --force --deep --sign - visiona-local.app` 已覆蓋 Resources/bin 下的執行檔,沿用即可;(3) 驗收時用 `spctl --assess --verbose vendor/ffmpeg/macos/ffmpeg` 確認不會被 Gatekeeper 拒絕 |
| R-v2-6 | **boot-id polling 對瀏覽器 tab 休眠的影響** — Chrome 會把背景 tab 的 setInterval 降頻到 1 次/分鐘,可能讓使用者切回 tab 時 60 s 才偵測到 server 重啟 | 🟡 低 | 新增 | 用 Page Visibility APItab visible → 5 s intervaltab hidden → 停 pollingtab 再次 visible → 立即 probe 一次再恢復 5 s interval。實作細節見 `v2/web-ui-offline-overlay.md` §3 |
| R-v2-7 | **砍 Mock 後空白 UI 體驗** — 使用者第一次打開沒插硬體時Devices 頁會是空的 | 🟡 低 | 新增 | (1) Devices 頁顯示友善 empty state「未偵測到 Kneron 裝置。請連接 KL520/KL720 後按『掃描』。」附安裝 driver 按鈕Windows(2) 這是 R5-5a 明示接受的結果PRD v2 也會記錄為預期行為 |
### v2.2 新增 FW 管理相關風險(採 PM PRD §8 編號、完整清單見 `v2/firmware-management.md` §10
| # | 風險 | 等級 | 詳見 |
|---|------|------|------|
| R-FW-1 | 升級後 device re-enumerate 不穩定 + KL520 reset bug 再現 | 中 | `v2/firmware-management.md` §10.1 |
| R-FW-2 | KneronPLUS Python wheel API 支援度 + 三平台版本不一致 | 中 | 同上 |
| R-FW-3 | bridge.py `update_kdp_firmware_from_files` macOS x86_64 / Linux x86_64 未驗證 | 中 | 同上 |
| R-FW-4 | timeout 設定不合理 | 低 | 同上 |
| R-FW-5 | Kneron firmware redistribution 法律 / 簽章授權 | **P0 release gate** | 同上、與 R5-B4 同性質 |
| R-FW-6 | 既有 `flash/` 模組命名混淆 | 低 | 同上 |
| R-FW-7 | 升級失敗 device unknown state | 中 | 同上 |
| R-FW-8 | KneronPLUS SDK 對 KL630/KL730 API 不可預測 | 🔴 高 | `v2/firmware-management.md` §10.2 |
| R-FW-9 | .tar 解包對安裝包大小衝擊 | 低 | 同上 |
| R-FW-10 | KL630/KL730 沒有 Loader mode 概念 | 中 | 同上 |
| R-FW-11 | 一般使用者誤觸降版 brick 風險 | 🔴 高 | 同上、緩解見 §11.3 + §8.6 graceful shutdown 拒絕 |
| R-FW-12 | 多版本管理 UX 複雜度 | 中 | 同上 |
| **R-FW-13**2026-05-25 新增)| wheel 2.0.0 → 3.1.2 跨主版本升級可能 breaking change | 中 | `v2/firmware-management.md` §10.3、M9-6 弱驗證 |
| R-TAR-1 ~ R-TAR-4 | TDD-only 技術細節:.tar handling 特定風險SDK 不接受 .tar 已確認 / build 步驟漏跑 / notarization / Python 3.12+ path filter| 已確認 / 中 / 低 | `v2/firmware-management.md` §10.4 |
v1 已列、v2 解除的風險:
- **ffmpeg GPL release blockerF-5, R9** — R5-6 LGPL 方案 B 解除
- **F-1 Wails tray 在 Linux GNOME 可能壞掉** — R5-3 維持砍 tray風險消失
- **F-3 使用者找不回 app** — R5-2 維持關閉 = 結束,風險消失
---
## 4. 審閱紀錄
| 日期 | 審閱者 | 結論 |
|------|-------|------|
| 2026-04-14 | Architect Agent | v2.0 Draft 產出 |
| 2026-04-14 | PM Agent | v2.0 互審完成(見 `reviews/pm-review-of-tdd-v2.md`)— Major × 4 / Minor × 5 |
| 2026-04-14 | Architect Agent | **v2.1** 產出:吸收 PM Major/Minor 修正 + R5-D 三條補充決策 + R5-E 階段化啟動 + Architect 自補清單F-2 port 保留 / B-1 OS 通知 / Q1/Q3/Q5/Q7 自決)。新增 `v2/startup-pipeline.md`;總工時 ~10 → ~12 人天 |
| 2026-04-14 | PM Agent | v2.1 待再次審閱(確認 Major × 4 都已落地 + R5-D/E 理解一致)|
| 2026-04-14 | Design Agent | v2.1 待審重點R5-E5 啟動階段文案、7+1 秒 stopping modal 文案、startup-panel.js 視覺對齊 Design Spec v2.1|
| 2026-04-14 | 使用者 | v2.1 待確認 |
| 2026-05-24 | Architect Agent | **v2.2** 產出:吸收使用者拍板 FW 管理方案 A + B、新增 `v2/firmware-management.md` + `adr/ADR-001-firmware-management.md`、R5-Q9 翻案範圍切割後。M9-1 ~ M9-13 新 milestone series、15.5 人天 |
| 2026-05-24 | PM Agent | v2.2 PM 互審完成(見 `02-prd/reviews/pm-review-of-tdd-and-design-v2.2-firmware.md`):通過 with Major × 2MJ-A1 ADR 編號、MJ-A2 R5-Q9 行號)+ Minor × 6 |
| 2026-05-24 | Design Agent | v2.2 Design 互審完成(見 `03-design/reviews/design-review-of-prd-and-tdd-v2.2-firmware.md`):通過 with 🔴 嚴重 × 1A-MISMATCH-1 stage 命名)+ 🟠 中等 × 3 + 🟡 輕微 × 4 |
| 2026-05-24 | Architect Agent | Architect 自互審完成(見 `04-architecture/reviews/architect-review-of-prd-and-design-v2.2-firmware.md`):自承 F1-F7 必修 + O1-O3 建議 |
| 2026-05-25 | Architect Agent | **v2.2 互審後修**FW 管理章節吸收三方互審 + M9-6 弱驗證新事實 + 使用者三項裁決stage 採 Design 命名 / M9 採 PM 拆法 / AC-FW-3.5 延後 B 階段 M9-10。修改範圍`v2/firmware-management.md`§1.2 / §1.3 新增 / §1.4 / §1.5 新增 / §3.4 新增 / §4.2 / §4.3 / §5.1 / §5.3 / §6.1 / §7.3 / §8.6 新增 / §9 / §10 / §11.3 / §11.4 / §13 / §14+ `adr/ADR-001-firmware-management.md`Status update + Context R5-Q9 改描述式 + Related 補 PRD + 變更記錄 v1.1|
| 2026-05-25 | 使用者 | v2.2 互審後修待最終確認 |