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>
24 KiB
TDD v2 — visionA-local(Round-2 refactor 後)
作者:Architect Agent 版本:v2.2 日期:2026-05-24 初版 / 2026-05-25 三方互審後修(FW 管理章節吸收 PM + Design 互審 + M9-6 弱驗證新事實) 狀態:Draft(v2.2 由 Architect 根據使用者 2026-05-24 拍板 FW 管理方案 A + B 產出、2026-05-25 三方互審 + 使用者拍板 stage 命名 / M9 工時拆法 / AC-FW-3.5 延後 三項裁決後修;待最終使用者確認) 取代:
TDD.mdv1.0(2026-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 → KDP2(KL520/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-4b(11 個) | +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-4(TDD-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 成功都開(砍 flag;R5-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 s(Architect 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 處理 | 允許 fallback(3721 → 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-10(10 個) | +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 差異速覽
| 面向 | v1(2026-04-11) | v2(2026-04-14) | 觸發決策 |
|---|---|---|---|
| Wails 視窗載入內容 | Splash → window.location.replace 跳到 Next.js 主 UI(M7-B) |
Splash 路徑完全移除,Wails 永遠停在「控制台 UI」:server 狀態 / log panel / 啟停控制 / Open in Browser | R5-1 A+B+G |
| 使用者業務 UI 承載者 | Wails WebView(M7-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 授權 | GPL(evermeet / BtbN / johnvansickle),VISIONA_ALLOW_GPL_FFMPEG=1 release blocker |
LGPL 方案 B(混合):Win/Linux 換 BtbN LGPL;macOS 自 build decoder-only ~20 MB,binary 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 poll,boot-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-TWJSON(詳見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.py(KneronPLUS SDK,sidecar)
└─spawn─▶ ffmpeg / ffprobe(on-demand 解碼)
Wails 殼 ←── IPC / Wails bindings ── Wails 視窗內的控制台 UI(vanilla HTML/JS/CSS)
Go server ←── HTTP / WebSocket over loopback ── 瀏覽器 tab(Next.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 §A1(v1→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 goroutine(bufio 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 interval,Page 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 |
Wails 控制台 UI + Go App bindings + LogBuffer + log pump + 狀態機 + Preferences(R5-D2/D3)+ OS 通知觸發點 | R5-1, R5-5, R5-D1/D2/D3, R5-E;M8-4, M8-4b, M8-5 |
| 2.2 | v2/ffmpeg-lgpl.md |
三平台 LGPL ffmpeg vendor 策略(Makefile patch + macOS build script + 授權檔管理) | R5-6, R5-6a, R5-6b, R5-6c;M8-3 |
| 2.3 | 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-3;M8-4, M8-9 |
| 2.4 | v2/cors-security.md |
CORS whitelist middleware、WS origin check、資料驗證邊界 | 三方共識 #5;M8-8 |
| 2.5 | v2/deletions.md |
yt-dlp / Mock 模式全清單(檔案 / 函式 / 行號 / i18n key / vendor / installer);v2.1 修正 videoIsURL / NewVideoSourceFromURL 明確刪除 |
R5-5a, R5-7, PM Minor 5;M8-1, M8-2 |
| 2.6 | v2/web-ui-offline-overlay.md |
瀏覽器 tab 的 <ServerOfflineOverlay> 實作:polling + WebSocket shutdown-imminent 雙管道、重試、SSR 相容 |
R5-2 三方共識 #14, PM Minor 4;M8-7 |
| 2.7 | v2/milestone-plan.md |
M8-1 ~ M8-10 + M8-4b 階段化啟動;總工時 ~12 人天 | 整體;M8-* |
| 2.8 | v2/code-reuse-v2.md |
逐模組沿用 / 改寫 / 新寫比例表 | 整體 |
| 2.9 | 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~E6;M8-4b |
| 2.10 | v2/firmware-management.md |
v2.2 新增:Kneron Dongle FW 偵測 + 升降版(A 階段 KL520/KL720 自動升級 + B 階段 KL630/KL730 driver 擴展 + B2 階段手動降版 + 多版本管理 + CURRENT_VERSION metadata + .tar firmware 處理) | ADR-001;M9-1 ~ M9-13 |
2.11 ADR 索引
| ADR | 主題 | Status |
|---|---|---|
adr/ADR-001-firmware-management.md |
Kneron Dongle FW 偵測 + 升降版(翻案 R5-Q9) | Accepted(2026-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 host(macOS 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 event(payload 為陣列);(2) server 推論 frame 狀態禁止用 logger.Info,改用 debug level(line-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 視窗通常也會關瀏覽器 tab,race 不構成實際問題。若使用者真的沒關瀏覽器,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 API:tab visible → 5 s interval;tab hidden → 停 polling;tab 再次 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 blocker(F-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 × 2(MJ-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 🔴 嚴重 × 1(A-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 互審後修待最終確認 |