jim800121chen 8c27da7cca test(local-tool): M9-5 — three-platform validation plan + e2e scripts + MJ3 fix
A 階段最後 milestone、出測試計畫 + 自動化腳本 + 三平台人工 checklist、使用者下週手動跑實機驗證。

Testing artifacts (8 檔、2630 行):
- .autoflow/06-testing/m9-5-validation-plan.md: 656 行(4 情境 × 3 平台 × 2 chip = 24 combo)
- 4 e2e specs (vitest + RTL + mock WS / mock fetch):
  - firmware-upgrade-happy-path.spec.ts (357 / 4 cases)
  - firmware-upgrade-error-recovery.spec.ts (356 / 4 cases + 8 reason it.each)
  - firmware-r-fw-11-modal-not-closable.spec.ts (303 / 6 cases)
  - wails-onbeforeclose-firmware-active.spec.ts (217 / 9 cases、含 5 todo 占位 M9-12)
- 3 manual checklists: macOS 264 / Windows 234 / Linux 243 行

設計取捨:
- 不引入 Playwright/Cypress (visionA-local frontend 沒裝、屬 architect 決策)、走 vitest + mock
- E2E 腳本放 06-testing/scripts/ 作 spec doc + 可選實作參考
- 實機驗證走人工 checklist (dongle 插拔 / kill process / SIGTERM 等需要實體互動)

MJ3 修復 (M9-4 reviewer round 1 留的 follow-up):
- server/internal/api/ws/firmware_ws_test.go: +16/-8
- "type": "firmware:progress" → "firmware_progress" (對齊 firmwareProgressMessage.Type)
- "phase" → "stage" (對齊 TDD §4.2 + FirmwareProgress.Stage)
- 不動 production code、只 test schema 對齊

執行建議 (給你下週):
- Day 1 P0: macOS+Win+Linux × KL520+KL720 happy path (~3h)
- Day 2 P1: R-FW-11 + disconnect_during_op + upgrade_mid_failed + 失敗注入 (4h)
- Day 3 P2: SIGTERM 延遲關閉 + Wails OnBeforeClose force-quit modal (2-3h)

測試:
- go test ./... -race 全綠 (server / wails / frontend 60 tests)
- MJ3 修復不破壞既有測試

A 階段開發 6/7 完成 (M9 文件 + M9-1 ~ M9-4.5)、剩 M9-5 實機驗證 (你下週跑)、跑完依結果決定 A 階段交付或派 sub-agent 修。

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

1182 lines
88 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

# 專案進度 — visionA-local
## 目的:全新專案(從 edge-ai-platform 衍生的 local 版本)
## 當前階段:🟡 **M9 — Kneron Dongle FW 偵測 + 升降版**L 級新功能、翻案 R5-Q9
## 當前狀態:✅ Architect 研究 plan 完成、使用者拍板方案 A + B、待補 PRD v2.2 / Design Spec v2.2 / TDD v2.2 + ADR-009 → 之後 KL630/KL730 driver 擴展需另一份研究
## 最後更新2026-05-24
## 2026-05-24 M9 啟動Kneron Dongle FW 偵測 + 升降版
### 背景
使用者要在 visionA-local 加 Kneron Dongle FW 偵測 + 升降版功能。同事 warrenchen 在 `gitea/warrenchen/web_academy_prototype/local_service_win` 有可參考實作。同事是雲端網頁 + 本地服務雙進程版本、我們是單進程 Wails、不需要那 7 組 RPC / 自訂 URL scheme / 引導下載安裝本地代理。
### 重要決策2026-05-24
- **採方案 A + B一次做完+ L 級正規流程**
- 方案 AMVP5 人天KL520 + KL720 自動升級 KDP1 → KDP2、安裝包 +0KB
- 方案 B追加5-7 人天):手動降版 + KL630 + KL730、安裝包 +5MB、需先擴 driver
- 合計 ~10-12 人天
- **翻案 R5-Q9**progress.md L776 第二輪 Q9「韌體燒錄 flash → B 砍掉」):
- 當時砍的是「使用者按按鈕燒 model 到 device flash」、不是 FW 升降版
- 現在要做的是「升級到 Kneron 官方 KDP2 標準版本」、範圍縮小避開大部分原始擔憂
- 將透過 ADR-009-firmware-management 留下決策痕跡
- **跨平台用 KneronPLUS C API、不用 DFUT.exe**Windows-only / 30MB 依賴)
- **flash/ vs firmware/ 模組分離**:既有 `flash/` 保持「load model 到 device RAM」原意、新建 `firmware/` 模組做升降版
### Architect 研究產出(已完成 2026-05-24
- `.autoflow/04-architecture/research-kl520-fw-management/00-research-summary.md`184 行、執行摘要)
- `.autoflow/04-architecture/research-kl520-fw-management/10-warrenchen-impl-analysis.md`259 行、warrenchen 實作分析)
- `.autoflow/04-architecture/research-kl520-fw-management/20-our-current-state.md`292 行、現有 code 盤點)
- `.autoflow/04-architecture/research-kl520-fw-management/30-integration-plan.md`662 行、MVP milestone + 風險清單)
### 關鍵技術發現
1. visionA-local 既有 code 已有 70% 的 FW 偵測 + RAM-load 邏輯bridge.py 已 `kp.core.load_firmware_from_file`
2. 缺的是「持久化升降版」(既有是 RAM-load、要呼叫 `kp_update_kdp_firmware_from_files` 寫 flash
3. KL520/KL720 firmware 已 bundle 在 `server/scripts/firmware/{KL520,KL720}/`~360KB
4. KL630/KL730 driver 現況沒處理 product_id0x0630 / 0x0730+ firmware 是 .tar 不是 .bin、要先擴 driver 才能加 FW 支援
### 與既有架構的銜接點
1. watchServer Error stateFW 升級失敗是 device 層、不應觸發 server Error
2. KL520 reset bug2026-04-21升級成功後 device re-enumerate、要等 5-8 秒 + 主動 rescan
3. R5-E 60s 啟動上限FW 升級是使用者主動觸發、不在啟動 pipeline、無關但升級本身 30-180s、UI 要 progress bar
### B 階段研究完成2026-05-24 第二輪)
-`.autoflow/04-architecture/research-kl520-fw-management/40-b-phase-kl630-kl730-extension.md`703 行)
-`.autoflow/04-architecture/research-kl520-fw-management/41-tar-firmware-handling.md`458 行)
-`.autoflow/04-architecture/research-kl520-fw-management/42-manual-downgrade-for-end-users.md`551 行)
### B 階段新發現
- warrenchen 沒做 KL630/KL730 FW 升降版、只 bundle firmware tar、沒實作流程
- KL630/KL730 連線流程跟 KL520/KL720 不同、bridge.py `handle_connect()` 對它們 fall-through 到 KL520會連不上
- .tar firmware 處理是 unknown 區、3 候選策略待 SDK 驗證
- 工時更新A 5 + B **10.5** = **15.5 人天**(原估 11-12、增加因「面向一般使用者」UX safety net + 多版本管理 + SDK 驗證)
### B 階段 4 個使用者決策2026-05-24
- **多版本目錄結構**:選 **C — CURRENT_VERSION metadata**`firmware/<chip>/{v2.2.0,v2.1.0,kdp1}/` + `CURRENT_VERSION` 單行檔;架構乾淨、跨平台無 symlink 風險)
- **Bundle 策略****保守策略 +7MB**KL520 提供 kdp1 + v2.1.0 兩個降版、KL720/KL630/KL730 current + 1 個舊版如有)
- **Kneron firmware redistribution 授權****先不管授權**(風險:發佈前可能要重新評估、與 R5-B4 同性質問題)
- **M9-6 SDK 驗證時機****跟 A 階段平行**(不拖完整進度)
### 翻案 R5-Q9 確認
- 透過 ADR-009-firmware-management 留紀錄
- 把「使用者按按鈕燒任意 model 到 device flash」與「升級到 Kneron 官方 KDP2」範圍切開
- 詳見 `00-research-summary.md` §1 R5-Q9 翻案分析
### 安裝包大小衝擊(最終)
- A 階段:+0KBKL520/KL720 firmware 已 bundle
- B 階段:+7MB保守策略 = KL520 +2 版降版 + KL720/KL630/KL730 各 +1 舊版)
- macOS dmg 預估從 163MB → **~170MB**
### 下一步L 級正規流程、三線平行)
**Track 1A 階段文件補寫)**
- [x] **PM PRD v2.2 完成**2026-05-24
- `.autoflow/02-prd/features/feature-firmware-management.md`523 行、14 章節)
- `.autoflow/02-prd/PRD-v2.md` 升 v2.2492 行、淨減 8、留 8 行 buffer / 未破 500
- Q9 翻案紀錄§2+ 範圍切割§3+ 4 個 US-FW user story§4+ user research 假設§5+ 成功指標含 Brick < 0.1%(§7+ R-FW-1~12 風險(§8+ Q-FW-1~7 未解問題(§9+ M9-1~13 工時表(§10
- **14 個待互審項**D-FW-1~6 Design / A-FW-1~6 Architect / O-FW-1~2 Orchestrator
- **最大不確定性 A-FW-4 / O-FW-2**KneronPLUS Python wheel KL630/KL730 update API 支援度 取決於 M9-6 SDK 驗證可能讓 AC-FW-3.5降級為 FW 偵測 only生效
- **PRD-v2.md 已卡上限**v2.3+ 任何補丁都會破 500PM 建議考慮 PRD 模組化拆分
- [x] **PM 修改完成**2026-05-25
- PRD-v2.md492 495 buffer 5未破 500
- feature-firmware-management.md523 599 +76
- MJ-A1 ADR 編號 ADR-009ADR-001 grep replace 完成
- MJ-A2 R5-Q9 行號改描述式引用
- MJ-D1 降版 framing 對齊 Design 中性立場
- MJ-D2 US-FW-2 US-FW-2a 一般使用者 / US-FW-2b 進階使用者
- A-MID-1 graceful shutdown AC-FW-1.9
- P-MID-1 體驗指標 §7.2.1 體驗指標 5 完成率 / 中途放棄率 / 重試率 / NPS / 客服問詢率
- 新增 R-FW-13wheel 三平台版本不一致 P1
- M9-0 35 分鐘強驗證 + AC-FW-3.5 延後 B 階段 + M9-8 策略 Y 註明
- 屬於 Architect Minor 全標「⏳ Architect 處理中」、不擅自修 TDD
- ** Orchestrator 追蹤**O-FW-1R5-B4 release blocker 合併追蹤+ O-FW-2M9-10 啟動時評估 AC-FW-3.5 生效條件
- [x] **Architect TDD v2.2 + ADR-001 完成**2026-05-24
- `.autoflow/04-architecture/v2/firmware-management.md`466 、< 500 不拆檔
- `.autoflow/04-architecture/adr/ADR-001-firmware-management.md`152 Accepted
- `.autoflow/04-architecture/TDD-v2.md` 版本 v2.1 v2.2、§0.0 差異速覽 + §2.10 子檔索引 + §2.11 ADR 索引 + §3 R-FW 風險引用 + §4 審閱紀錄
- ADR-001 編號正確adr/ 目錄掃描空不撞號
- 工時量化 15.5 人天M9-1=1.0 / M9-2=1.0 / M9-3=0.5 / M9-4=1.5 / M9-5=1.0 / M9-6=1.0 / M9-7=0.5 / M9-8=1.5 / M9-9=2.0 / M9-10=1.0 / M9-11=1.5 / M9-12=2.0 / M9-13=1.0
- **5 個給 PM 互審注意**Q9 範圍切割 / R5-B4 授權 P0 懸念 / 工時 +安裝包對下載率 / Error 15 回歸 AC / B2 一般使用者面板
- **5 個給 Design 互審注意**DOWNGRADE 嚴格輸入框 / 6 錯誤碼 + 4 失敗類型友善文案 / Stage 6 × 2 方向 i18n / R-FW-11/12 UI safety net 全落地 / 版本字串對齊 CURRENT_VERSION
- [x] **Architect 修改完成**2026-05-25
- `v2/firmware-management.md`604 823 +219
- `adr/ADR-001-firmware-management.md`210 218 +8
- `TDD-v2.md` 索引202 217 +15
- 7 必修 F1-F7 全完成stage 命名 / 對應表 / FirmwareProgress 補欄位 / graceful shutdown §8.6 / 工時採 PM 拆法 / R-FW 編號採 PM / bridge.py handler 同步
- 3 建議 O1-O3 全完成KL530/KL830 不做 / R-TAR TDD-only / 升級期間關 Wails 視窗測試
- MJ-A1 ADR-009ADR-001Architect 管轄 4 檔已修
- MJ-A2 R5-Q9 行號改描述式
- §3.4 完整 stage錯誤碼Reasoni18n 對應表
- §8.6 graceful shutdown 拒絕設計server lock + Wails force-quit modal+ 工時併 M9-11
- [x] **Design v2.2 完成**2026-05-24)→ `.autoflow/03-design/v2/firmware-management.md`920 明示不拆理由+ `design-spec-v2.md` 索引更新
- 5 分頁結構Settings 新增韌體插在硬體與模型之間
- Devices card FW badge//+ deep-link icon
- 二次確認 modal輸入DOWNGRADE嚴格比對
- 52 i18n keys中英雙語
- 6 個新 design tokens
- R-FW-11.5 / R-FW-11.6 自提風險Design 視角
- **6 個給 Architect 互審待確認**token 對比 / DOWNGRADE 字串 / 8 種失敗 stage / 狀態機名稱 / 降版中拒 graceful shutdown / 分頁順序
- **4 個給 PM 互審待確認**Q9 翻案章節 / 進階使用者 user story / 用語一致性 / 成功指標 Brick &lt; 0.1%
- [ ] 三方互審進行中
- [x] **PM 互審報告完成**2026-05-24)→ `.autoflow/02-prd/reviews/pm-review-of-tdd-and-design-v2.2-firmware.md`~250
- 結論**通過 with Major 修正**
- 3 MajorMJ-A1 ADR 編號PRD ADR-009 vs 實檔 ADR-001 不一致/ MJ-A2 R5-Q9 行號 cross-checkL776 vs L854/ MJ-D1+D2降版用詞 framing + US-FW-2 拆分涵蓋進階使用者
- 11 MinorArchitect 6 + Design 5
- 修改工作量Architect 0.2 + PM 0.55 + Design 0.1 人天
- [x] **Design 互審報告完成**2026-05-24)→ `.autoflow/03-design/reviews/design-review-of-prd-and-tdd-v2.2-firmware.md`
- 結論:🟢 整體一致可進 M9-12 Frontend但有 2 P0 必解
- **P0 A-MISMATCH-1 狀態機名稱不一致**Design preparing/loading/flashing/verifyingArchitect connecting/loading_loader/loading_firmware/verifyingPM 中文版又不同 建議以 Architect 為準Design 自己也要修內部不一致
- **P0 A-MISMATCH-2 錯誤碼 vs 失敗類型不對等**Architect 6 API 錯誤碼 vs Design 8 UI 失敗情境缺對應表Frontend 無法 map Architect 補對應表 + FirmwareProgress.Reason 欄位
- 4 P1graceful shutdown 拒絕 / token 對比實測 / 失敗復原表 4vs8 / PM 缺體驗指標+ 7 P2
- 對齊良好 6
- **不阻塞 A 階段 M9-1~M9-5**A 階段沒降版 + graceful shutdown
- [x] **Design 修改完成**2026-05-25
- `v2/firmware-management.md`916 948 +32未拆檔門檻調 8001000
- `v2/control-panel.md`466 652 +186新增 §6a 整節 graceful shutdown 攔截 modal + FORCE 二次確認 + 19 i18n keys
- P0 A-MISMATCH-1 stage 命名統一 preparing/loading/flashing/verifying(§5.3 / §7.1 / §8 / §9.6 / §9.8 全部對齊
- P0 A-MISMATCH-2 失敗對應表:§7.1 backend stage + Reason 細分scan_not_found/connect_failed/loader_write_failed/upgrade_mid_failed/verify_mismatch/timeout/disconnect_during_op
- P1 A-MID-1 graceful shutdown UI control-panel.md §6a 落地
- P1 A-MID-2 token 對比實測責任歸 M9-12 Frontend
- i18n 52 51 keys細分修改
- Discord 殘留查證**不存在**PM 互審那輪可能誤判
- ** Architect 銜接點**Reason enum 字串需 Architect TDD §3.4 照抄 + `HasActiveTask()` IPC 規格需提供 deviceName/stage/etaSeconds/direction 欄位
- [x] **Architect 互審報告完成**2026-05-24)→ `.autoflow/04-architecture/reviews/architect-review-of-prd-and-design-v2.2-firmware.md`438
- 結論PM/Design 技術上都可實現整體對齊度高無架構衝突
- PM通過 + 2 MajorM9 工時拆法 / R-FW 編號+ 4 Minor
- Design通過 + 2 Majorstage 命名 / graceful shutdown+ 4 Minor
- **Architect 自承下輪需修 7 必修 + 3 建議項**F1-F7 + O1-O3
- **⚠ Design 互審讓步衝突**
- Design stage 命名以 Architect 為準
- Architect stage 命名以 Design 為準
- ** Orchestrator/使用者裁決**
- [x] **M9-6 強驗證完成**2026-05-25)→ `.autoflow/04-architecture/research-kl520-fw-management/56-m9-6-strong-validation-result.md`246
- **🔴 推翻 1**macOS/Linux 2.0.0 wheel **沒有** KL630/KL730 enum確認+ KL730 product_id **0x732 不是 0x730**前兩輪研究假設值錯
- **🔴 推翻 2**KL630/KL730 .tar **不是 fw_scpu.bin + fw_ncpu.bin** **embedded Linux rootfs**ELF + 80+ .so / 5.8MB+32MB / 完全不同代設計)→ 弱驗證的策略 Y 唯一可行結論不成立需全新策略 Xspike M9-8
- **附帶發現**KneronPLUS 3.1.2 Python wrapper **沒有 `update_kdp_firmware_from_files`**warrenchen ctypes 直接打 .so C symbol繞過 Python wrapper TDD 必須明示 ctypes
- **A 階段不阻塞**KL520+KL720 2.0.0/3.1.2 wheel 一致AC-FW-3.5 確定延後 B 階段
- **B 階段工時 +2-3 人天**原估build script 解壓即可」、實際需 spike KL630/KL730 全新 SDK 機制
- [x] **M9-6 弱驗證完成**2026-05-24)→ `.autoflow/04-architecture/research-kl520-fw-management/55-m9-6-weak-validation-result.md`
- **關鍵發現 1**KneronPLUS 3.1.2 enum 確認 KL520/KL720/KL720_LEGACY/KL630/KL730/KL830 全存在
- **關鍵發現 2**`update_kdp_firmware_from_files(dg, scpu, ncpu, auto_reboot)` 簽名完整取得
- **關鍵發現 3**`KDP_MAGIC_CONNECTION_PASS = 0x1FF55B4F` KDP1 firmware check
- **關鍵發現 4**策略 Z .tar SDK確定不可行 必須走策略 Y解壓後傳 .bin path
- **🔴 關鍵發現 5**重大wheel 三平台版本不一致macOS/Linux = 2.0.0、Windows = 3.1.2 A 階段 KL630/KL730 升降版必先升 macOS/Linux wheel
- **關鍵發現 6**warrenchen KL630/KL730 升降版完全沒實作 reference code gap 不是 SDK 限制
- 10 unknown ~50%、足以開始 A 階段
- **A 階段啟動前唯一阻塞** backend agent 35 分鐘強驗證A-2 + C-1
- 建議 **AC-FW-3.5KL630/KL730 升降版)延後到 B 階段 M9-10**
**Track 2B 階段技術前置、與 Track 1 平行)**
- [x] **Architect M9-6 SDK 驗證 plan 完成**2026-05-24)→ `.autoflow/04-architecture/research-kl520-fw-management/50-m9-6-sdk-validation.md`~500
- 10 unknown 3 A SDK API / B 連線行為 / C tar 內容
- 強驗證 2 人天 / 弱驗證 1 人天
- 4 R-VAL 風險硬體 / wheel / 假設錯 / 環境
- 建議 A 階段 M9-3 M9-4 完成後啟動實機驗證避開 bridge.py 改檔衝突
- **派工前要確認**KL630/KL730 dongle 硬體狀態/多久能拿到/沒有)→ 決定走強驗證或弱驗證
**Track 3開發**
- [x] **M9-1 bridge.py firmware_upgrade handler 完成**2026-05-25
- `server/scripts/kneron_bridge.py`1207 1973 +767
- `server/scripts/test_kneron_bridge_firmware.py`622 新檔27 unit tests 0.069s 全綠
- `server/scripts/firmware/KL520/fw_loader.bin`90112 bytes warrenchen 複製MD5 `aef7cca17bc023abbd6152c46c18e774` 一致
- `server/scripts/firmware/{KL520,KL720}/VERSION`v2.2.0
- **完全對齊 TDD §6.1**stage Design 命名 / 8 reason enum 7 覆蓋disconnect_during_op M9-5 實機/ ctypes 走法 1:1 warrenchen / progress event schema percent/stage/message/elapsed_ms/eta_ms/extra
- **唯一邊界決定**KL720 legacy fw_loader.bin flash-based 模式不過 loading stage直接 `kp_update_kdp_firmware_from_files(scpu, ncpu)`)、保守 + 向前相容
- **既有功能無 regress**scan/connect/disconnect/reset/load_model/inference 一行沒改
- ** M9-2/3/5 解決**Go driver stderr 解析 / Service mutex / HasActiveTask / disconnect_during_op 實機 / 三平台 ctypes 實機驗證
- [x] **M9-1 Reviewer 第 1 輪審查完成**2026-05-25)→ `.autoflow/05-implementation/review/m9-1-bridge-firmware-upgrade-review.md`
- 結論**0 Critical / 3 Major / 4 Minor / 4 Suggestion**
- 建議**阻擋 M9-2 直到 M1+M2 修完**0.2 人天
- Major M1`_FwError` / `_FwTimeoutError` / `_fw_handle_failure` class 宣告在 handler 之後讀者邏輯流動問題5 分鐘可修
- Major M2`needs_loader` 控制流隱式需抽 `should_run_loader_stage` boolM9-2 Go driver 易誤判 stage 完成度
- Major M3`_fw_classify_legacy` substring match firmware 字串覆蓋不足
- 4 Minor + 4 Suggestion 可留 M9-1 修改一起處理或 M9-5 follow-up
- TDD §6.1 規格對齊度 98%
- 不升級給 Security Auditor5 security 重點 4 1 Minor
- [x] **M9-1 Backend 第 2 輪修改完成**2026-05-25
- `kneron_bridge.py`1973 2058 +85
- `test_kneron_bridge_firmware.py`622 840 +21827 36 tests0.076s0 regression
- 3 Major M1+M2+M3 全修
- 4 Minor m1+m2+m3+m4 全修
- s3 firmware 字串覆蓋擴展legacy_exact set + KDP3+ forward-compat
- s4 4 test case
- ** follow-up**s1 handler ~330 行抽 phase helper / s2 rescan exponential backoff
- [x] **M9-1 Reviewer 第 2 輪審查完成**2026-05-25)→ `.autoflow/05-implementation/review/m9-1-bridge-firmware-upgrade-review-round2.md`
- 結論**通過 with 1 Minor + 1 Suggestion解除 M9-2 阻擋**
- 1 8 issue 修了 8 M1/M2/M3/m1/m2/m3/m4-prod/s4 全到位
- ** 2 輪新發現**0 Critical / 0 Major / 1 Minor m5test `_firmware_upgrade_start_ts` 死碼/ 1 Suggestion s5test 註解
- **不需 backend 3 **、m5+s5 可在 M9-2 期間順手清
- TDD §6.1 對齊度維持 98%、M3 forward-compat 對未來 KDP3+ device brick 風險顯著改善
- 既有 6 handler 零改動驗證通過
- [x] **M9-1 整體完成**2026-05-25)→ 通過 with Suggestions可進 M9-2
- [x] **M9-2 Go driver + firmware service 完成**2026-05-25
- `server/internal/firmware/types.go`新檔 123 FirmwareVersion / FirmwareProgress / ActiveTaskInfo / UpgradeDriver interface / 8 reason const
- `server/internal/firmware/progress.go`新檔 141 仿 flash pattern Tracker
- `server/internal/firmware/service.go`新檔 346 核心 service
- `server/internal/firmware/service_test.go`新檔 517 11 tests
- `server/internal/driver/kneron/kl720_driver.go`697 948 +251UpgradeFirmware method + helpers + stderr route
- `server/internal/driver/kneron/kl720_driver_test.go`新檔 177 8 tests
- `test_kneron_bridge_firmware.py` M9-1 留下的 m5 + s5+9/-2
- **測試結果**Go 25/25 pass1.8s+1.0s)、 server `go test ./...` pass regressionrace-cleanPython 36/36 pass
- **TDD 對齊**:§4.2 / §4.3 / §5.1 / §6 / §6.1 / §8.6 / §3.4 全到位
- **2 Deviation**合理
- `guards.go` / `versions.go` 未建B2 階段 M9-11 才需要A 階段 service.go 已內嵌最簡實作
- SIGTERM handler 串接 main.go M9-3避免大改既有 shutdown flow
- ** M9-3 的銜接 API**`taskID, progressCh, err := svc.UpgradeFirmware(ctx, deviceID, chip)` + WebSocket room `firmware:<deviceID>` 廣播 + `HasActiveTask()` / `GetActiveTaskInfo()` control panel
- **M9-1 follow-up 順手清**m5 + s5 全做
- [x] **M9-2 Reviewer 第 1 輪完成**2026-05-25)→ `.autoflow/05-implementation/review/m9-2-go-driver-firmware-service-review.md`
- 結論**0 Critical / 2 Major / 5 Minor / 5 Suggestion不阻擋 M9-3不升 security**
- **Major 1**sendCommand goroutine d.mu 整段升級 60-200s + timeout deadlockkl720_driver.go:860-866+875
- **Major 2**fwProgressCh close-channel racekl720_driver.go:758-797 + service.go:197-203production 跑萬次會踩 panic
- 5 MinorbrickRiskReasons / 補測試 / needsReset 早退 / 註解順序 / fwMu atomic 缺口
- 5 Suggestiondone event 去重 / multi-device 測試 / ListBundledVersions Stat / struct 共用 / cancel field godoc
- M9-1 follow-upm5 + s5正面驗證通過
- **建議 backend 2 輪修 Major 1+2**production race修起來成本不高
- [x] **M9-2 Backend 第 2 輪修改完成**2026-05-25
- `kl720_driver.go`948 1054+106 sendCommandForUpgrade 60 snapshot pattern + fwUpgradeMu
- `kl720_driver_test.go`177 360+1833 testInfoNotBlockedDuringUpgrade / CtxCancelReleasesBridge / StderrEventAfterCtxCancel 100 round stress
- `service.go`346 373+27
- `service_test.go`517 676+1592 test + ListBundledVersions tempdir
- `progress.go`141 147+6Task.cancel godoc
- **Major 1 修法**方案 B 變體fwUpgradeMu 獨立鎖 + `sendCommandForUpgrade()` snapshot stdin/stdout pattern避開 d.mu field-level race
- **Major 2 修法**方案 A 變體tryRouteFirmwareEvent fwMu 整段配合 driver defer setFirmwareProgressCh(nil) 提供 happen-before 保證
- Minor 1-5 + Suggestion 1/2/3/5 全修
- ** follow-up**Suggestion 4bridgeFirmwareEvent / FirmwareProgress struct 合併可在 M9-3 wire WS 時順手做
- 測試go test ./... -race -count=1 全綠28s/ Python 36 tests + 22 subtests 全綠0.31s/ go vet / build 0 output / 0 regression
- [x] **M9-2 Reviewer 第 2 輪通過**2026-05-25)→ `.autoflow/05-implementation/review/m9-2-go-driver-firmware-service-review-round2.md`
- 結論**0 Critical / 0 Major / 2 Minor / 2 Suggestion不阻擋 M9-3不需 backend 3 **
- 1 11/12 issue 全修到位Suggestion 4 backend 明示留 follow-up
- **Major 1 snapshot pattern 驗證**stdin/stdout snapshot release d.muctx.Done 路徑自然 return panic leaklock 順序單向fwUpgradeMu d.mu無反序 deadlock
- **Major 2 happen-before 驗證**fwMu 保證 inflight tryRouteFirmwareEvent 必定 eitherset nil 前完成 sendorset nil 後讀到 ch==nil return false」、絕無 send on closed channel panic
- Minor R-1attemptedUpgrade 位置驗證後撤回降為 Suggestion R-3
- Minor R-2Disconnect/stopPython 沒主動清 fwProgressCh純防禦性目前無 bug
- **CI 建議**M9-3 PR `go test -race -count=3` 提升 race detection 強度
- [x] **M9-2 整體完成**2026-05-25)→ 通過可進 M9-3
- [x] **M9-3 API handler + WebSocket progress 完成**2026-05-25
- `server/internal/api/handlers/firmware_handler.go`新檔 404
- `server/internal/api/handlers/firmware_handler_test.go`新檔 632 26 subtests
- `server/internal/api/handlers/device_handler.go`197 238+414 firmware 衍生欄位
- `server/internal/api/router.go`236 259+23routes
- `server/main.go`381 391+10wire firmware service + handler
- **4 endpoint 全到位**
- `GET /api/devices` firmwareVer/firmwareIsLegacy/firmwareCanUpgrade/bundledFirmwareVersion
- `POST /api/devices/:id/firmware/upgrade` 202 + `{taskId}`
- `GET /api/firmware/active-tasks`
- WS room `firmware:<deviceID>` broadcast schema 對齊 §4.2
- 錯誤碼DEVICE_NOT_FOUND / FW_UNSUPPORTED_CHIP / FW_DEVICE_BUSY / FW_UPGRADE_FAILED / FW_UPGRADE_BRICK_RISK
- go test ./... -race 全綠
- **SIGTERM main.go 整合留 M9-4.5**合理 Wails OnBeforeClose 一起做會新增 M9-4.5 milestone
- [x] **M9-3 Reviewer 第 1 輪完成**2026-05-25)→ `.autoflow/05-implementation/review/m9-3-api-handler-ws-review.md`
- 結論**0 Critical / 1 Major / 3 Minor / 5 Suggestion不阻擋 M9-4不升 security backend 2 **
- **Major 1**JSON schema 雙鍵衝突`firmwareVersion` from DeviceInfo + `firmwareVer` from FirmwareDerivedFields 兩鍵同存
- 起因派任務時引用 TDD §3.1 line 131 寫了 firmwareVer但既有 device JSON 已有 firmwareVersion Orchestrator給的規格有錯
- **修法**deviceWithFirmware 顯式定義 3 個欄位移除重複 firmwareVer+ TDD line 131 改回 firmwareVersion + enrichDevices JSON 輸出測試
- 3 Minorctx.Background 註解 / bundledVersion cache 永不重試 / test device manager error format 沒鎖
- 5 Suggestionbridge.py classify 一致性 / forward goroutine done sleep grace / unknown error log / firmwareFeatureEnabled / goroutine leak 直接驗證
- 正面評價3 interface + DeviceManagerAdapter 解循環依賴乾淨26 subtests async-aware flakytasks nil→[] 體貼 frontend
- [x] **Architect TDD §3.1 修正完成**2026-05-25)→ firmware-management.md line 131 `firmwareVer` `firmwareVersion`grep 確認無殘留
- [x] **M9-3 Backend 第 2 輪修改完成**2026-05-25
- `device_handler.go`238 244+50/-2
- `firmware_handler.go`404 465+61
- `firmware_handler_test.go`632 938+306新增 5 test func / 19 test points
- **Major 1 修法** `FirmwareDerivedFields.FirmwareVer` 欄位frontend 直接讀 DeviceInfo 既有 `firmwareVersion` schema test grep 確認 `firmwareVer` 0 命中
- Minor M-2/3/4 全修ctx.Background godoc / bundledVersion cache success / test JSON 結構斷言不檢查 error message string
- Suggestion 1/3/5 全修isLegacyFirmware 對齊 bridge.py + parity 真值表 / unknown error log / goroutine leak 直接驗證 cleanupCalls==1
- ** follow-up**S-2forward done sleep grace分析無 race 不修/ S-4firmwareFeatureEnabled flagYAGNI
- 測試go test ./... -race -count=1 全綠handlers 2.489s / api 3.522s / ws 4.623s / device 1.931s / firmware 2.695s / driver/kneron 5.583s / model 5.022s
- go vet / build 0 output
- [x] **M9-3 Reviewer 第 2 輪通過**2026-05-25)→ `.autoflow/05-implementation/review/m9-3-api-handler-ws-review-round2.md`
- 結論**0 Critical / 0 Major / 0 Minor / 3 極小 Suggestion不阻擋 M9-4不需 backend 3 不升 security**
- 1 9 issue 處理8 + 1 合理 deferS-4 YAGNI
- Major 1 修法完全到位3 test 鎖定 regression
- S-1/S-2/S-4 backend 不修分析確認合理
- 3 個極小 Suggestion 全部 backend 不需處理純評估
- [x] **M9-3 整體完成**2026-05-25)→ 通過可進 M9-4
- [x] **M9-4.5 server SIGTERM + Wails OnBeforeClose 完成**2026-05-25
- **Server **
- `server/main.go`改寫 SIGTERM/SIGINT goroutine 整合 firmware-aware preamble
- `server/internal/firmware/shutdown.go`新檔 154 AwaitActiveTasksOrTimeout helper + 3 interface
- `server/internal/firmware/shutdown_test.go`新檔 258 6 tests
- **Wails **
- `visiona-local/main.go`OnBeforeClose inline closure 改為 app.OnBeforeClose
- `visiona-local/app.go`App struct firmwareCloseGuard
- `visiona-local/query_firmware_active_tasks.go`新檔 111 HTTP helperfail-open
- `visiona-local/query_firmware_active_tasks_test.go`新檔 250 7 tests
- `visiona-local/firmware_close_guard.go`新檔 244 CloseGuard struct + OnBeforeClose + ConfirmForceClose bindings
- `visiona-local/firmware_close_guard_test.go`新檔 280 8 tests
- **行為**
- Server SIGTERM active task broadcast `server:shutdown-pending` system room RequestShutdown + WaitForActiveTasks(180s) 走原本 shutdownFn
- Wails OnBeforeClose active task emit Wails event `app:firmware-in-progress` payload `{hasActive, tasks}` + return true 擋住關閉
- ConfirmForceClose binding frontend 第二層 FORCE 確認用
- 測試8 個檔合計新增 21 unit tests race-cleanfirmware pkg 5.305s + wails 11.326s 全綠
- ** M9-12 frontend**Design §6a 19 i18n keys force-close modal UI不影響 M9-4.5 功能屬未實作的下游
- [x] **M9-4.5 Reviewer 第 1 輪完成**2026-05-25)→ `.autoflow/05-implementation/review/m9-4.5-shutdown-integration-review.md`
- 結論**0 Critical / 1 Major / 3 Minor / 4 Suggestion不阻擋 M9-5不升 security建議 backend 2 0.2 人天範圍小**
- **Major 1spec/impl 雙重不對齊不是 code bug**
- TDD §8.6.18.6.4 `firmware:shutdown-rejected` vs 實作用 `server:shutdown-pending`
- Design §6a.5 SIGKILL bypass vs 實作走 graceful 7+1s
- Reviewer 推薦** spec 不改 code**實作更安全合理)→ Architect + Design 修文件
- 3 MinorMaxShutdownWait 180s vs KL720 230s 不對齊 / Second SIGTERM 不處理 / broadcast startTs 洩漏
- 4 Suggestioninterface 微過度抽象 / ConfirmForceClose race / 1s timeout production load 風險 / fakeLifecycle 不模擬 wait delay
- **正面評價**interface 乾淨 / 雙層保護 / 註解品質極高 / N=100 -race 並發測試 / fail-open 3 層一致 / Test 6 真接 Service wire-up
- [x] **M9-4.5 第 2 輪修改完成**2026-05-253 sub-agent 平行
- **Architect TDD §8.6**標題拒絕」→「延遲」+ event `firmware:shutdown-rejected` `server:shutdown-pending` + room `system` + payload schema 註明 tasks 不含 startTs + 命名取捨變更紀錄段grep `firmware:shutdown-rejected` 0 hit 流程描述 1 hit 變更紀錄
- **Design control-panel.md §6a**force-quit graceful 7+1s + 雙層防護對齊實作 + 為何刻意不採 SIGKILL5 點設計理由 + §6a.11 IPC 規格對齊grep SIGKILL 3 hits 全是為何不採 SIGKILL論述段
- **Backend shutdown.go**
- Minor 1`MaxShutdownWait` 180s 220sKL720 200s upgrade + 20s buffer+ 註解說明
- Minor 3新增 `shutdownBroadcastTask` minimal struct8 不含 startTs+ `toBroadcastTasks()` helper + broadcast 走過濾路徑
- shutdown.go +57 / shutdown_test.go +126 / 2 testfilter assertion + nil/empty helper
- 23 tests race-clean 全綠 / firmware/api/ws/handlers/visiona-local 全綠 / 0 regression
- [x] **M9-4.5 整體完成**2026-05-25)→ 不需 Reviewer 2 spec + 2 Minor影響範圍極小可直接進 M9-5
- [x] **M9-4 Frontend FW badge + 升級 modal 完成**2026-05-25
- 12 新增 + 4 修改 = 16 3052
- **新增 i18n**firmware.* 52 keys + devices.card.fwBadge.* 5 keys = 57 leaf × 2 lang = 114 翻譯字串
- **新元件**FirmwareBadge / FirmwareUpgradeButton / FirmwareUpgradeDialog4 phase/ FirmwareProgressView / FirmwareErrorView8 reason
- **Zustand store** + WS hookpattern 對齊 useFlashProgress+ 整合 DeviceCard
- **R-FW-11 緩解**upgrading phase modal 不可關onInteractOutside / onEscapeKeyDown preventDefault + 隱藏 X
- **多裝置隔離defense in depth**firmware-store activeDeviceId mismatch 直接 return
- **測試**51 tests pass32 + 19 既有)、pnpm build 4.6s 全綠tsc --noEmit 0 errorlint 對我的檔案 0 hit
- **發現問題**backend `/ws/devices/:id/firmware-progress` endpointM9-3 只實作 broadcast沒實作 WS handler)→ hot-fix
- **未做**範圍外Settings 韌體面板 / 降版 UI / 版本切換 dropdown M9-12
- [x] **M9-4-hotfix Backend WS endpoint 完成**2026-05-25
- `server/internal/api/ws/firmware_ws.go`新檔 50 對稱 flash_ws.go純改名
- `server/internal/api/ws/firmware_ws_test.go`新檔 165 2 smoke testsbroadcast / room isolation
- `server/internal/api/router.go`+2route 註冊
- go test -race 全綠1.964s/ go vet / build 0 output
- [x] **M9-4 Reviewer 第 1 輪完成**2026-05-25)→ `.autoflow/05-implementation/review/m9-4-frontend-firmware-review.md`
- 結論**0 Critical / 3 Major / 8 Minor / 5 Suggestion不阻擋 M9-5不升 security**
- **MJ1**i18n namespace Design §9 偏離structure mismatch)→ Frontend 2
- **MJ2**FirmwareErrorView `title` 硬編碼中文 一行修 i18n
- **MJ3**backend smoke test schema `phase` / `firmware:progress`應為 `stage` / `firmware_progress` M9-5 順手修
- 8 Minortoast 停留 / Dark mode token TODO / test fallback / RTL test / disabled ContactSupport UX
- 5 Suggestionmodal 寬度 480px / selector 優化 / helper
- ** Frontend 2 **MJ1+MJ2+部分 Minor)、**不需 Backend 2 **MJ3 M9-5
- [x] **M9-4 Frontend 第 2 輪修改完成**2026-05-25
- **MJ1 採方案 A**namespace `settings.firmware.*`對齊 Design §9 source of truth)— 而非 Reviewer 偏好的方案 Bflat
- 理由Design §9 是三方對齊後 SoTFrontend 不該自行翻案B 階段 M9-12 會加更多 settings.firmware keys現在不對齊將造成分散維護
- Reviewer 堅持 B Orchestrator Design 先改 spec
- **MJ2** 一行修 + 順手把 ContactSupport disabledenabled + mailto handler
- 8 Minor 全處理 M5 FirmwareErrorView 9 RTL tests
- 2 Suggestion S1 480px / S5 formatTechnicalInfo helper
- 3 Suggestion follow-upS2/S3/S4 Testing 範圍或 acceptable trade-off
- 行數變化13 檔案、+243/-1
- **測試**60 tests pass+9 FirmwareErrorView/ tsc 0 error / pnpm build 成功 / lint firmware/ 0 hit17 既有 lint 問題不屬 M9-4 範圍follow-up
- 0 regression
- [x] **M9-4 Reviewer 第 2 輪通過**2026-05-25)→ `.autoflow/05-implementation/review/m9-4-frontend-firmware-review-round2.md`
- 結論**0 Critical / 0 Major / 0 Minor / 2 Suggestion接受方案 A不阻擋 M9-5不需 frontend 3 不升 security**
- **MJ1 方案 A 接受**Frontend pushback 邏輯紮實Design SoT 優先 + B 階段必然要落 settings.firmware + cost )、Reviewer 同意
- 12 項應修全到位3 follow-up合理)、1 不適用MJ3 backend/testing
- 2 nice-to-have Suggestionmailto 地址抽常數 / handleCopy test)— 不阻擋
- ContactSupport mailto 安全性驗證通過RFC 6068 + encodeURIComponent
- [x] **M9-4 整體完成**2026-05-25)→ 通過可進 M9-5
- [x] **M9-5 Testing plan + e2e scripts 完成**2026-05-25
- `.autoflow/06-testing/m9-5-validation-plan.md`656 4 情境 × 3 平台 × 2 chip = 24 combo
- **4 e2e scripts**vitest + RTL + mock WS / mock fetch不引入 Playwright
- firmware-upgrade-happy-path.spec.ts357 4 cases
- firmware-upgrade-error-recovery.spec.ts356 4 cases + 8 reason it.each
- firmware-r-fw-11-modal-not-closable.spec.ts303 6 cases
- wails-onbeforeclose-firmware-active.spec.ts217 9 cases 5 todo 占位 M9-12
- **3 平台 manual checklist**給你下週手動跑macOS 264 / Windows 234 / Linux 243
- **MJ3 順手修**firmware_ws_test.go +16/-8`phase``stage` + `firmware:progress``firmware_progress` 對齊 TDD §4.2
- go test ./... -race 全綠server / wails / frontend 60 tests
- **執行建議** 1 P0macOS+Win+Linux × KL520+KL720 happy path、~3h/ 2 P1R-FW-11 + 失敗注入4h/ 3 P2SIGTERM + Wails OnBeforeClose2-3h
- **使用者下週執行**、跑完依結果決定 A 階段是否交付pass reviewer 收尾 B 階段 / fail 派對應 sub-agent
- [ ] M9-5 使用者下週實機執行macOS/Windows/Linux 三平台 × KL520+KL720
- [ ] M9-6 ~ M9-13B 階段擴展
---
## 2026-04-21 推論 bbox 標註不顯示 + KL520 Error 15S 級 bug fix
### 症狀
Mac app 上傳單張圖推論畫面上完全沒有 bbox 標註
### 根因兩層獨立問題疊加讓「bbox 完全不見」)
**Layer 1前端 canvas 尺寸)**
- `camera-inference-view.tsx` `renderedSize` 初始值硬寫 `{w:640, h:480}`
- ResizeObserver 理應在 `<img>` load fire 更新成實際顯示尺寸例如 516×640 直式圖 CSS 640×794但實測沒 fire fire 時機不對
- 結果 overlay canvas 永遠用 640×480 img 實際 DOM box 對不上 就算有 detectionbbox 位置會嚴重偏位甚至跑出 canvas
**Layer 2後端推論 Error 15**
- `kp.inference.generic_image_inference_send` `ApiKPException Error 15 SEND_DATA_TOO_LARGE`
- 試過image 尺寸516×640 / 640×794 / 640×640 pad)、 numpy vs bytes明確傳 width/height **全部都炸**
- Python bridge 直接測試`/tmp/test_bridge.py`做完整 `connect → reset → reconnect → load_model → inference` **11 個 detection 正常回傳**
- 對比 Go driver 實際路徑`connect → load_model → inference` **跳過了 reset**
### 兇手commit `ddf0eb8`2026-04-16
`KL520 首次 connect 跳過不必要的 device reset` 當時為解 Windows 60s HTTP timeoutLoader mode connect 不穩定 + firmware load 總耗 64s而加的優化 KL520 首次 connect 不再 restartBridge
副作用KL520 雖然是 USB Boot / RAM-based 裝置理論上每次 connect clean state但實測若 session firmware 殘留`fw=KDP2 Comp/U`**直接 load_model + inference 100% Error 15**。只有走完整 `reset → 退回 Loader → 重新載 firmware 到 Comp/U` 流程才能拿到能正常 inference session
### 修法
**前端(`camera-feed.tsx` + `camera-inference-view.tsx`**
- `<img>` `onLoad` handler圖片 decode 完立刻用 `getBoundingClientRect` 回報尺寸最可靠時機
- ResizeObserver effect 進來先檢查 `img.complete && naturalWidth > 0`是就立刻 reportcover HMR / cached image
- effect 依賴加 `streamUrl / batchImageUrl`換圖會重觀察
- `renderedSize` 初始值改 `null`overlay 改為 `isStreaming && renderedSize` render避免首次用預設值畫錯
- setState callback prev 比對同尺寸不觸發 render
**後端(`server/internal/driver/kneron/kl720_driver.go`**
- 移除 `ddf0eb8` KL520 跳過 reset特例 KL520 KL720 都走 `needsReset=true → restartBridge()`
- 註解記錄 trade-offKL520 connect 時間從 ~2s ~15-20smacOSWindows 可能 60s+
- 同步調整 `server/internal/api/handlers/device_handler.go` connect timeout`60s → 120s` Windows worst-case~65s buffer
**Python bridge`server/scripts/kneron_bridge.py`**
- 無實質改動試過 host-side letterboxnumpybytes明確傳 w/h 全部無效 還原回原版確認問題在 Go driver reset 流程
- 只加了 debug log`Inference: sending...` / `Inference: parse done, detections=N` / `Inference EXCEPTION with traceback` bug 時用commit 前會保留低成本高價值
### 驗證function 層)
`/tmp/test_bridge.py` 直接測試 bridge JSON-RPC
```
[5/5] inference (real 516x640) keys: ['taskType', 'timestamp', 'latencyMs', 'detections', 'classifications']
✅ inference OK — detections=11 classifications=0 latency=308.3ms
- person 0.705 bbox=(x=0.427, y=0.526, w=0.089, h=0.070)
- person 0.701 bbox=(x=0.360, y=0.438, w=0.227, h=0.246)
- tie 0.639 bbox=(x=0.351, y=0.573, w=0.011, h=0.107)
...
✅ 1920x1080 OK — detections=0
✅ 512x512 OK — detections=0
=== ALL TESTS PASSED ===
```
三種尺寸516×640 直式 / 1920×1080 横式 / 512×512 正方全通過
### 已驗證2026-04-21
- [x] Mac UI Comp/U 殘留路徑reset 後推論 11 bbox 正確
- [x] Mac UI Loader cold-boot 路徑拔插 USBskip reset 後推論 11 bbox 正確
- [x] Windows 實測首次 connect106s 成功< 120s timeout推論正確
### 後續優化Windows connect 106s → 預期 ~40s方案 C
Windows 實測發現即使 timeout 120s 夠用使用者要等 106s 體感太久拆解
瓶頸發現走了兩次 firmware load第一次 connect 進來 Loader load fw
Comp/U ~35s / reset Loader / reconnect load fw Comp/U ~30s
reset 流程中第二次 firmware load 是白做工
**條件性 reset方案 C**
- `kneron_bridge.py connect` 回報 `fresh_firmware_loaded` flag
- `True`本次 connect 內部剛做過 firmware load原本是 Loader
- `False`進來就是 Comp/U上次 session 殘留需要 reset 清乾淨
- `kl720_driver.go` flag 決定要不要做 restartBridge reset
**驗證兩條路徑都 OK2026-04-21**
- Loader cold-boot skip reset 推論 11 bbox
- Comp/U 殘留 reset 推論 11 bbox
**預期效益**
- Windows cold-boot最常見106s **~40s** 65s
- Mac session最常見~15-20s 不變
- 極少數情境Windows device 未斷電維持走完整 reset 流程
### 待驗證
- [ ] Linux 實測
- [ ] Windows 實測方案 C 效益預期 cold-boot 降到 ~40s
### 前端 debug log 去留
`camera-overlay.tsx` `console.log('[bbox-debug] ...')` 驗證完成後**可清可留**。保留成本低對未來 debug 有幫助
## 2026-04-20 macOS 掃不到 Kneron 裝置S 級 bug fix
症狀Mac app 啟動後前端顯示沒有裝置實際 KL520 透過 USB 連上)。
根因兩層
1. **主要**`PythonModeAuto` 預設 system bundled」,系統 python3 通常沒裝 KneronPLUS wheel `import kp` 失敗 bridge 降級 pyusb pyusb 找不到 libusb scan
2. **次要(潛在)**macOS hardened runtime 會剝掉 `DYLD_LIBRARY_PATH`若未來 bundle 架構變動 dyld 找不到 libkplus 的相依 libusb會再踩坑
修法
- `visiona-local/app.go` `PythonModeAuto` 語意翻轉 **先 bundled已預裝 kp wheel失敗才 fallback system**理由local-tool 整包內嵌 Python + wheels系統 python 不會裝 kp不該優先
- `server/scripts/kneron_bridge.py` `import kp` 前新增 `_preload_kneron_dylibs_macos()` `ctypes.CDLL` 絕對路徑預載 wheel `kp/lib/libusb-1.0.0.dylib` + `libkplus.dylib`避開 DYLD hardened runtime 砍的風險Windows/Linux 分支不動
- 同步 bridge payload/{darwin,linux,windows}/scripts/ + build bundle
驗證
- `go build` 兩個 module 都通過
- bridge script 直跑`{"cmd":"scan"}` 回傳 KL520 裝置 `kn_number 0xB906162C`
- rebuild wails app 後實測需要 `make wails-macos`
## 2026-04-20 macOS DMG 美化S 級)
需求Mac 端也要有 installer類比 Windows .exe)。走方案 Ccreate-dmg 美化 DMG + 背景圖 + Applications 捷徑)。
實作
- 新增 `installer/macos/{make-dmg-background.py, background.png, background@2x.png, README.md}`
- 動態生成 640×400 深色背景對齊 Wails 控制台 splash 配色 `#111827→#0B0F19` + `#38BDF8` accent
- 1x + 2x Retina 版本
- Makefile `dmg` 拆成三個 target
- `dmg`auto-detect create-dmg fancy沒有 fallback plainCI 無痛
- `dmg-fancy`強制美化版 `brew install create-dmg`
- `dmg-plain`原本的 hdiutil UDZO保留為 fallback
- Windows / Linux 流程零改動
驗證
- `brew install create-dmg` 成功
- `make dmg-fancy` 產出 157MB DMGmount 後內容app + Applications 捷徑 + .background/background.png + .DS_Store視窗樣式
- `hdiutil verify` 通過
## 🔴 2026-04-14 使用者提出 L 級重大方向變更
### 使用者原話
> 推論只需包含這三種 camera/image/上傳影片(avi, mpeg, mp4, 瀏覽器能吃的格式)
> 模型除了預設的幾種只能用上傳的
> 介面希望是用網頁而不是包在應用程式中
> 我想像中的是 visionA local 安裝完 啟動後 應用程式介面會有可以顯示 local server log 的地方
> 有可以啟動/停止 重啟 local server 的介面 有打開 localhost 網頁的介面
> 網頁上會有 scan/connect device 的介面 選模型/上傳模型 推論的介面
### 變更解讀
1. **推論來源範圍縮減**camera / image / 上傳影片砍掉 URL 推論 + yt-dlp + YouTube/Vimeo
2. **模型管理縮減**只保留預設幾種 + 只能上傳」,砍掉任何 URL 下載 / Model Zoo 類功能
3. **介面架構巨變**Wails 桌面 app 退化為Local Server 控制台」(Log 面板 + Start/Stop/Restart + Open browser真正的使用介面在**瀏覽器**scan/connect/model/inference 全在 Web UI
### 影響範圍(初判)
- yt-dlp 打包M6 部分)→ 依賴瘦身 -35MB
- `ResolveWithYTDLP` / `ytdlpHosts` / `StartFromURL` yt-dlp 路徑 / 前端 URL tab
- Wails 控制台是**全新 UI**和現有 splash + Next.js 完全不同
- 與第三輪決策 Q-A tray)、Q7關閉視窗=結束 app有潛在衝突可能要復議
- M1-M7 的工作**大部分仍可沿用**server / Next.js UI / 打包只是 Wails 視窗內容要重寫
- 延伸的 yt-dlp 跳頁 bug 問題**自動消失**功能直接砍
### 第一輪三方分析狀態
- PM 分析完成`01-requirements/pm-analysis-round2-refactor.md`419
- Design 分析完成`03-design/design-analysis-round2-refactor.md`537
- Architect 分析完成`04-architecture/architect-analysis-round2-refactor.md`798
### 三方共識(無分歧)
1. **技術可行**沿用率 85-95% ~10 人天
2. **砍 yt-dlp**vendor 35MB + resolver + URL tabdmg 220→~135-185MB
3. **ffmpeg 保留**上傳影片仍需解碼GPL blocker 延續可能 M8 LGPL
4. **Q-A 砍 tray 必須復議** 新方向下 tray 價值從可有可無核心
5. **Q7 關閉=結束必須復議** 否則關 Wails 視窗 = SIGTERM server = 瀏覽器 tab ECONNREFUSED
6. **Next.js UI 幾乎零改動**80-90% 沿用只砍 URL tab
7. **Wails 控制台走 vanilla HTML/JS/CSS**不新 Next.js mini app
8. **CORS 要限制為 127.0.0.1/localhost**瀏覽器模式新攻擊面
9. **綁定維持 127.0.0.1**不做 LAN mode
10. **watchServer 改為 Error state** os.Exit
### 三方立場差異(待使用者裁決)
- **C1 動機問題**PM 堅持前置條件必須先知道使用者為什麼要改架構PM 列出 9 種可能動機
- **首次啟動是否自動開瀏覽器**Design 建議預設自動Ollama )、PM 建議手動C6 A)— 輕微分歧
- **First-Run 搬家策略**PM 建議留瀏覽器端C8 A)、Design 沒強烈意見
### 下一步
- 使用者決策 R5 全部收齊見下方R5 第五輪使用者決策」)
- 三方依決策產出正式 PRD v2 / Design Spec v2 / TDD v2下一步
- 三方互審 使用者確認 進開發
### R5 第五輪使用者決策2026-04-14重構方向變更
| # | 題目 | 使用者決定 | 備註 |
|---|------|----------|------|
| R5-1 | 重構動機 | **A + B + G**多視窗便利 + 瀏覽器 devtools + 需求方就是這麼要求 | 127.0.0.1 LAN / 無背景 daemon 需求 |
| R5-2 | Wails 視窗關閉行為Q7 復議| **維持關閉=結束 server**瀏覽器網頁顯示local server 已離線覆蓋層 | 不改原 Q7 決策但前端要新增server 離線UI |
| R5-3 | Tray 復議Q-A 復議| **T1維持砍 tray** | R5-2 一致 1.5 人天 |
| R5-4 | 首次啟動自動開瀏覽器 | **A首次自動開之後可設定** | Ollama 式零摩擦 |
| R5-5 | Wails 控制台 scope | 同意 PM 清單**拿掉 Mock 模式切換** | |
| R5-5a | Mock 模式歸處 | **A完全砍掉 Mock 模式** | 使用者明確:「沒插硬體就讓它是空的不用 demo |
| R5-6 | ffmpeg 授權 | **LGPL 方案 B混合** | Windows/Linux BtbN 現成 LGPL binarymacOS build |
| R5-6a | macOS build 規模 | **A最小 decoder-only build~20MB** | 只含 mp4/avi/mov/mpeg/mpg 五種解碼器 |
| R5-6b | macOS binary 存放 | ** commit repo`vendor/ffmpeg/macos/`** | LGPL ffmpeg 幾乎不需更新直接進 git |
| R5-6c | 是否打包 ffprobe | **一起包** | BtbN 本來就都有0 成本 |
| R5-7 | M7 Windows build | **先不管,做完再驗** | 跳過 M7-B3 baseline 驗證 |
### 三方共識全部採納(無須使用者裁決)
1. 技術可行沿用率 85-95%~10 人天
2. yt-dlp 全套vendor 35MB + resolver + URL tab + handler
3. ffmpeg 保留 R5-6 LGPLGPL blocker 解除
4. Next.js Web UI 80-90% 沿用只砍 URL tab
5. Wails 控制台走 vanilla HTML/JS/CSS不新 Next.js mini app
6. CORS 限制為 127.0.0.1/localhost
7. 綁定維持 127.0.0.1不做 LAN mode
8. watchServer 改為 Error state不再 `os.Exit(1)`
9. 預設模型維持 8 .nef(「只能上傳」= 再次確認不做 Model Zoo
10. 批次影像上傳保留
11. 上傳影片副檔名`.mp4 / .avi / .mov / .mpeg / .mpg`
12. Server port資料目錄版本號 log 等工具資訊住 Wails 控制台
13. 硬體偵測結果上傳模型Settings > 語言 住瀏覽器 Web UI
14. Restart 期間瀏覽器 tab 用 `boot-id` + retry 重連(雖然 R5-2 選關閉=結束,此邏輯仍需做以支援 Restart Server 按鈕)
### 三方正式 v2 文件(已產出)
- ✅ PRD v2.0`02-prd/PRD-v2.md`484 行)— PM 5 個懸念見 §11
- ✅ Design Spec v2.0`03-design/design-spec-v2.md`99 行索引)+ `03-design/v2/*.md`5 子檔)
- ✅ TDD v2.0`04-architecture/TDD-v2.md`136 行索引)+ `04-architecture/v2/*.md`8 子檔,~3738 行)
### 三方 v2.1 補丁(已產出,吸收 R5-D + R5-E + 互審發現)
- ✅ PRD v2.1(原地更新 PRD-v2.md500 行,卡在上限)
- ✅ Design Spec v2.1:索引 127 行 + settings-update 239 + control-panel 465 + **新檔 startup-progress 417**
- ✅ TDD v2.1:索引 162 行 + control-panel 830 + server-lifecycle 961 + web-ui-offline-overlay 更新 + deletions 更新 + milestone-plan 更新 + **新檔 startup-pipeline 518**
- **新工時預估**10 → **12 人天**+M8-4 +0.5 / +M8-4b +1 / +M8-7 +0.3 / +M8-10 +0.2),建議對外回報 ~13 人天含 buffer
### v2.1 新增懸而未決問題彙總
**Design 新增3 題)**
- D-Q120 秒 retry hint 文案「正在重試…」vs「正在處理中…」Design 建議前者)
- D-Q2WebSocket 被安全軟體擋的提示Design 建議不做特殊偵測)
- D-Q3Retry 按鈕語意「重置整個啟動」vs「重試當前階段」Design 建議重置,需 Architect 確認 RestartStartupSequence 可行)
**Architect 新增5 題)**
- A-Q1階段 6 WebSocket 首次連線實作方式 long-poll endpoint vs sentinel file交 M8-4b 執行者)
- A-Q2watcher goroutine 和使用者在 Starting 中按 Stop 的 raceaction bar 禁用M8-4b 實測)
- A-Q3shutdownGracePeriod 7s/6s 對齊若實測常被 SIGKILL 則改 9+1 秒
- A-Q4Linux notify-send 不存在時的 fallbackM8-10 實測 Ubuntu minimal
- A-Q5N-R4 CI/E2E 測試分層blocked on testing agent
**PM 保留**
- §11-4 N-R4 CI/E2E 測試分層(同 A-Q5
- §11-7 R5-E 6 階段中英雙語文案定稿Design 已定版,使用者最後可 override
### 第二輪三方互審結果2026-04-14— 🟢 全員通過
-**Design 審 PRD v2.1**通過3 Minor 不阻擋Error 按鈕命名 / Linux OFF 階段描述 / v2.0 歷史字樣)
-**PM 審 TDD v2.1**通過2 Minor 不阻擋code-reuse-v2.md:92 殘留 / milestone-plan.md:6 工時數字不同步)
-**Architect 審 Design Spec v2.1**通過3 Minor 全在 TDD 側skipped status 枚舉 / WS sentinel file 決案 / 階段 6 soft timeout skip + Retry 機制)
### 第二輪關鍵仲裁
- **Error 按鈕命名分歧**Architect 仲裁為**兩個獨立動作**
- Startup error60s timeout 或階段失敗)→ 按鈕「**Retry**」= 呼叫 `RestartStartupSequence()` 重置整個啟動流程
- Running 階段 watchServer 失敗 → 按鈕「**Restart Server**」= 重 spawn server既有行為
- **D-Q3 RestartStartupSequence 可行性**:✅ 可行,新增 function5 步驟實作細節已定)
- 停 watcher → ForceKill server → 重置 state machine → 重建 pipeline → 重跑 Start
- 階段 1 直接 Complete 不重跑
- sentinel file 必須先清
- Retry 情境下 port 允許 fallbackcold start 行為)
- **階段 6 WebSocket 就緒偵測方案**:採 **sentinel file** `<dataDir>/.first-ws-connected`(不用 long-poll endpoint
- **D-Q2 WebSocket 被擋偵測**:不可行,不做特殊偵測
- **D-Q1 20s retry hint 文案**不影響技術Design 自由定稿
### Architect 自補 TDD 清單M8-4b 前補完,估 1-2 小時,不啟動新 Agent
第一輪遺留 4 項 + 第二輪新增 3 項:
1. offline-overlay 10s/2 次/3s active polling 參數
2. Gin SkipPaths + crypto/rand boot-id
3. Restart 強制同 port 規則
4. ExportLog binding
5. `StartupProgressEvent.Status` 新增 `"skipped"` 枚舉值
6. 階段 6 WebSocket sentinel file 決案寫入
7. 階段 6 Toggle OFF 時跳過 soft timeout + 新增 §9「Retry 機制」小節(含 RestartStartupSequence
### v2.1 殘留 Minor不阻擋開發M8 過程中順手修)
- `04-architecture/v2/code-reuse-v2.md:92` 殘留「新增 autoOpenedThisSession 欄位」字樣(轉版漏改)
- `04-architecture/v2/milestone-plan.md:6` 摘要「~11.5 人天」和 L491 合計「12.0」不一致
- PRD v2.0 變更紀錄列殘留「首次自動開瀏覽器」(歷史紀錄,不修)
### M8 開發進度2026-04-15— 🟢 程式碼全部完成,只差 M8-10 交付
| Milestone | 狀態 | 備註 |
|-----------|------|------|
| Architect 自補 TDD 7 項 | ✅ 完成 | 7 項落地 + 意外發現FAILURE_THRESHOLD 同步、ForceKill 缺失提醒、hard timeout skip|
| M8-1 砍 yt-dlp | ✅ 完成 | +222/-555 行18 檔案5 項 build 全綠 |
| M8-2 砍 Mock | ✅ 完成 | -528 行15 檔案5 項 build 全綠smoke test 通過 |
| M8-3 ffmpeg LGPL | ✅ 完成 | ffmpeg 5.7MB + ffprobe 5.6MB(比 GPL 版省 85% 空間LGPL 合規build 2m44s |
| M8-1+M8-2 Reviewer | ✅ 通過 | 親自 build/test/smoke0 誤刪 0 殘留 |
| M8-3 Reviewer | ✅ 通過 | 18 項驗證全過1 Minor + 2 Suggestion + 3 交付前事項 |
| M8-4 ServerController + log ring buffer | ✅ + Review 通過 + 4 Major 補丁 | 20 unit test + race -count=2 全綠 |
| M8-4b 啟動階段管線 | ✅ + Review 通過 + 3 Major 補丁 | 14+3 testHasFailedStage / IsInColdStart helpers |
| M8-5 Wails 控制台 UI | ✅ + Review 通過 + 2 Critical 補丁 + Stage 6 CTA 補丁 | 9 檔 ~2012 行wails build PASS |
| M8-6 source-selector 副檔名擴充 | ✅ 完成(未 Review 改動太小)| 4 檔案 ~4 行 |
| M8-7 Offline Overlay | ✅ + Review 通過 | role=alertdialog + focus trap + wsEverConnected 容錯 |
| M8-8 CORS middleware | ✅ + Review 通過 | 127.0.0.1/localhost + suffix attack 防護 |
| MAJ-4 shutdown broadcast | ✅ + Review 通過 | server/ws + visiona-local/notify helper15 test |
| M8-9 Boot-ID + tab 重連 | ✅ + Review 通過 | 9 test + SSR 相容 + reload loop guard |
| **M8-10 端到端 smoke test + 三平台 build** | 🔄 **進行中** | macOS build ✅ + P0 latent bug 修復 ✅(預設 15 模型載入),待 Reviewer + Windows/Linux 驗證 |
### M8-3 Reviewer 交付前必做事項M8-10 前)
1.**`vendor/ffmpeg/macos/` 4 檔 git add** — 已於 commit `8cd5751` 處理
2.**重跑 `make payload-macos`**2026-04-15— payload/darwin 204MB原 GPL 版 ~280MBLGPL 驗證通過ffmpeg 5.7MB + ffprobe 5.6MB,無 yt-dlp 殘留
3.**`vendor/yt-dlp/` 87MB 殘留** — 已清除
### M8-10a macOS build + smoke test 結果2026-04-15
**✅ 通過項**
- `make dmg` 成功:**163MB**GPL 版 220 → LGPL 版 163-57MB符合 PRD v2.1 預估)
- `.app` bundle 215MBcodesign verify OK
- LGPL ffmpeg config 驗證:`--enable-version3` + 無 `--enable-gpl` + 無 libx264/libx265只含 mp4/avi/mov/mpeg/mpg 所需 demuxer/decoder符合 R5-6a 最小 decoder-only build
- Server 從 bundle 正常啟動127.0.0.1:3799
- `VISIONA_BUNDLE_BIN_DIR` PATH 注入正確
- `deps/checker.go` **已檢查 ffprobe**progress.md 舊標「⏳ 待補」實際已做,標記更正)
- `[OK] ffmpeg: (bundled)`
- `[OK] ffprobe: (bundled)`
- `[OK] python3: Python 3.14.3`
- `GET /` → HTTP 200 size=24292Next.js 首頁)✅ splash regression 不再發生
- `GET /api/system/health``{"status":"ok"}`
- `GET /api/system/deps` → 三項全 available ✅
- `GET /api/devices` → 200空陣列無裝置
- SIGTERM 優雅關閉 ✅
- CORS middleware init 無錯 ✅
### 🔴 M8-10a 抓到的 P0 latent bug從 M1 就有,只是沒人測過)
**現象**`GET /api/models``{"data":{"models":null,"total":0},"success":true}`
啟動 log`Loaded 0 built-in models` + `Warning: could not load models from .../bin/data/models.json: no such file`
**根因**`server/main.go:42-51` + `:99-108`
- server 預設 `base = filepath.Dir(exe)` = `Contents/Resources/bin/`
- 預設 `dataDir = base + "/data"` = `Contents/Resources/bin/data/`(空目錄)
- 但 models.json + 8 個 .nef 實際住在 `Contents/Resources/data/`(上一層)
- Wails 端 `server_control.go:529` 明確傳 `--data-dir a.dataDir`,而 `a.dataDir = platformDataDir()` = `~/Library/Application Support/visiona-local/` — 使用者 dataDir也**沒有** models.jsonuser dataDir 只存 lock / ipc-port / logs / custom-models / preferences.json
- **結論**:正式啟動路徑下永遠載入 0 個預設模型
**為什麼 M1-M7 都沒抓到**:當時 smoke test 只測 `/api/health``/`、splash 跳轉,從沒跑過 `/api/models`
**這違反 R5 第 9 點共識**:「預設模型維持 8 個 .nef只能上傳 = 再次確認不做 Model Zoo」— 8 個預設模型必須能載入,使用者才有基本 demo 體驗。
**影響範圍**macOS / Windows / Linux 三平台都同樣這個 bugserver/main.go 是共用的)。
**採方案 B使用者批准+ 額外職責拆分**2026-04-15
實作:`server/main.go`
- 新增 `resolveBuiltInDataDir(base)` — 照 `resolveBridgeScript` 同款風格,依序試 `<base>/data``<base>/../data``<base>/../Resources/data`**以 `models.json` 存在為命中條件**
- `main()` 拆出兩個獨立變數:
- `builtInDataDir`read-onlybundle 內)— 給 `model.NewRepository(filepath.Join(builtInDataDir, "models.json"))``flash.NewService(deviceMgr, modelRepo, builtInDataDir)` 使用(因 flash 也要解析 model.filePath 相對路徑 `"data/nef/..."`
- `dataDir`writableuser home— 給 custom-models / sentinel file / logs 使用,語意不變
- `cfg.DataDir == ""` 時 fallback 成 `builtInDataDir`(保 dev mode `go run ./server` 繼續可跑)
**為什麼順便拆職責**:原本的 bug 不只影響 `modelRepo`,也影響 `flash.Service``flash.service.go:115-121``s.dataDir` 解析 `"data/nef/kl520/xxx.nef"` → 原本會指向 user dataDir 找不到檔案)。純 B 只修 main.go 一處還不夠,必須同時把 flash 切到 builtInDataDir。拆成兩個變數反而讓職責更清楚未來不會再混淆。
**驗證結果**
- `go build / vet / test -count=1 ./...` 全綠
- 重 build dmg 163MB大小不變
- Smoke test `/api/models``total: 15`(不是原估計的 8因為 models.json 有 15 個條目,部分 model 共用 nef
- 啟動 log`Built-in data dir: .../Contents/Resources/data` + `Loaded 15 built-in models` + 無 `could not load models` warning
- `/api/models/kl520-yolov5-detection` 回傳完整 metadata + filePath `data/nef/kl520/kl520_20005_yolov5-noupsample_w640h640.nef`
- flash 解析後指向的實體檔案在 bundle `.../data/nef/kl520/kl520_20005_yolov5-noupsample_w640h640.nef`7.2MB)與 `kl720/...` 10MB與 API 回傳的 modelSize 完全吻合 ✅
### Reviewer 第一輪2026-04-15 Major 1 / Minor 2 / Suggestion 2
報告:`.autoflow/05-implementation/reviews/review-m8-10a-builtin-data-dir-fix.md`
- **Major-1**Linux AppImage 布局(`usr/bin/<exe>` + `usr/lib/visiona-local/data/`三候選全不命中AppRun 已 export `VISIONA_BUNDLE_LIB_DIR` 但 server 沒讀。備註 `resolveBridgeScript` 先前就有同樣缺失。
- **Minor-1**fallback 沒 `filepath.Abs`
- **Minor-2**fallback 沒 log 試過的候選
- **Suggestion s-1**:抽公用 `findFirstExisting` helper
- **Suggestion s-2**dataDir dev mode fallback 註解
### Reviewer 第二輪修復2026-04-15Major + 所有 Minor + 兩個 Suggestion 一次全部處理
- 新增 `findFirstExisting(candidates, sentinel) (dir, tried)` helpers-1
- `resolveBuiltInDataDir` 候選 5 條①env `VISIONA_BUNDLE_LIB_DIR/data``<base>/data``<base>/../data``<base>/../Resources/data``<base>/../lib/visiona-local/data`
- `resolveBridgeScript` 比照修復(技術債一起清),候選 6 條
- fallback 全 `filepath.Abs`m-1+ `log.Printf("warn: ... Tried: %v", tried)`m-2
- `main()` dataDir fallback 加 5 行註解解釋 dev-only 語意s-2
### 第二輪 Review2026-04-15✅ 通過,可交付三平台
- 逐項驗證Major-1 ✅ / Minor-1 ✅ / Minor-2 ✅ / s-1 ✅ / s-2 ✅
- 獨立複驗build / vet / test 全綠AppImage 模擬env var 路徑AppImage 模擬FHS fallback 無 env全不命中情境 log + fallback + server 不 crash ✅;`os.Chdir` grep 零匹配(`./scripts` 相對候選無 cwd 漂移);候選順序對非 Linux 三平台零誤命中
- 新發現兩項**非阻擋**
- Minor m2-1resolve 函式用 std `log.Printf` 而非 `pkglogger.Warn`logger 尚未初始化前呼叫,合理),下次 logger 重構時統一
- Suggestion s2-1`findFirstExisting` 可改 `(dir, tried, ok bool)` 更 idiomatic非必須
### M8-10b/c 待使用者驗證
- **Windows**:使用者在 Windows 實機跑 bootstrap + make exe → 驗證 splash → Wails 控制台 6 階段啟動 → 瀏覽器 Web UI
- **Linux**Ubuntu 實機跑 bootstrap-linux.sh + make appimage → 驗證 xdg-open 預設 OFF + notify-send fallback
### M8-3 Minor + Suggestion低優先
- **Minor**BUILD.md §Verification §5 預期 `spctl --assess=accepted` 實測會被 reject改為 `codesign -v`
- **Suggestion 1**`vendor-ffmpeg` target 可補 sha256 對比防呆
- **Suggestion 2**`payload-windows` 授權檔 `skipifsourcedoesntexist` 若同時缺失會無授權交付
### 上一輪 Reviewer 提的 Minor已解決 / 懸而未決)
-`source-selector.tsx` accept 清單已擴充 mpeg/mpgM8-6 完成)
-`camera_handler.go` 後端副檔名白名單已擴充M8-6 完成)
-`deps/checker.go` 未加 ffprobe 檢查 — M8-3 後可補
-`api_e2e_test.go` 整檔刪後失去 HTTP 層 smoke — 建議 M8-10 前補一份不依賴 mock 的 read-only e2e
### M8-4 Reviewer 結果:⚠️ 需修 5 Major2026-04-15
**親跑驗證全綠**`go build/vet/test/test -race`、20 unit test、smoke test、SkipPaths 生效。
**5 個 Major**4 個 M8-4 Agent 回修、1 個留 M8-4b 包辦):
- **MAJ-1** `server_control.go:198-229 / 251-265` Stop/ForceKill 不 cancel watchCancel → 30s 後誤翻 Error + 發崩潰通知
- **MAJ-2** `server_control.go:269-291` handleWatchFailure 未取 txMu → 與 Stop race
- **MAJ-3** `server/main.go:166` shutdownFn timeout 仍 10sTDD §8.1 要求 6s破壞 7+1 modal UX
- **MAJ-4** 沒實作 `server:shutdown-imminent` WebSocket 廣播(阻擋 M8-9不阻擋 M8-4b/5/7**M8-4b 一起做**
- **MAJ-5** `server_control.go:579-608` logPump scanDone 不 drain lineCh → 丟最後 128 行崩潰 log
### M8-4 Reviewer 15 個 Minor技術債M8-5 後整理)
主要Snapshot 效率、ShouldEmit CAS micro-race、Restart 拆兩段 txMu、stopGraceful 與 logPump file handle race、scanner select default、notify timeout、v1/v2 重複碼
### v1/v2 並存策略
**合理但需立即標記砍除時程**。v1 路徑stopServer/stop()/kill()/watchServer/5s grace已 dead code 但仍存在易誤用 → **M8-5 完成後立即砍 v1**(含 MIN-10/11/12 併處理)
### 待使用者決策
- **commit 策略**Reviewer 建議分三個 commitM8-1 / M8-2 / M8-3或一個合併。使用者從未要求 commit保守做法是先不 commit 等使用者說。
### R5-Design 補充決策2026-04-14Design v2 產出後使用者回答)
| # | 題目 | 使用者決定 |
|---|------|----------|
| R5-D1 | Server 崩潰時除了控制台 Error banner 是否仍發 OS 原生通知 | **保留** OS 通知 |
| R5-D2 | Linux 預設「啟動時自動開瀏覽器」 toggle 狀態 | **預設 OFF**macOS/Windows 預設 ON避免 xdg-open 在極簡 WM 異常 |
| R5-D3 | R5-4 字面歧義「首次啟動」vs「每次啟動」 | **每次啟動**都自動開瀏覽器(修正 R5-4 原本「首次」的字面,實際意圖是「每次 Start Server 成功後」) |
### 三方交叉審閱階段(進行中)
- ⏳ PM 審 TDD v2驗證所有需求都有技術方案R5 / R5-D 全部落地
- ⏳ Design 審 PRD v2驗證體驗面沒遺漏R5-D1/D2/D3 有無落地
- ⏳ Architect 審 Design Spec v2驗證設計技術上可行
### 使用者授權
使用者已說「交互 review 完就進開發」— 審閱無衝突則直接進 M8不用另外確認。
### 三方互審結果2026-04-14
**Design 審 PRD v2**:❌ 不通過(`02-prd/reviews/design-review-of-prd-v2.md`
- Major 4 / Minor 4
- 核心問題R5-D1/D2/D3 都沒吸收PM 寫 PRD 時還不知道這三題)
- Major 4 auto-open toggle 位置分歧 → Design 仲裁「PRD 對(住 Wails 控制台Design Spec v2 settings-update.md 要自修
- §11-5 徽章決定:**不加**
- Architect Q6 Overlay close tab 決定:**不設**
**PM 審 TDD v2**:⚠️ 條件通過(`04-architecture/reviews/pm-review-of-tdd-v2.md`
- Major 4 / Minor 5
- 核心問題R5-D 三題 TDD 零匹配 + M8-9 驗收條件 `autoOpenedThisSession` flag 和 R5-D3 相反per-session-once 寫成成功條件Reviewer 會誤判)
- PM 自行回答 PM §11 技術懸念:
- **AC-1.3 10 秒預算不可達**(估 5.5-18 秒)→ 建議放寬到 **15 秒**
- **idle RAM ≤ 450MB 可達**(估 275-405MB
- **PM 對 Architect Q4 grace period 回答****7 秒 + 1 秒內顯示「停止中…」modal**(基於 Nielsen Norman 10 秒注意力臨界點)
- PM 判斷 M8-1/M8-2/M8-3 互不依賴,可在 Major 修復前先啟動(砍 yt-dlp / 砍 Mock / ffmpeg LGPL vendor
**Architect 審 Design Spec v2**:⚠️ 有條件通過(`03-design/reviews/architect-review-of-design-spec-v2.md`
- Major 2 / Minor 12
- Major 1Design settings-update.md §2.2 誤稱「走 Wails 既有 settings store」Wails v2 無此機制)+ 檔名不一致 → 採 TDD 的 `preferences.json @ <dataDir>/`
- Major 2R5-D2 Linux 預設 OFF 兩份 spec 都沒落地 → 新增 `DefaultPreferences()``runtime.GOOS`
- Architect 7 懸念自決:
- **Q1** grep 確認 `NewVideoSourceFromURL` 只有 `StartFromURL` + `videoIsURL`-guarded seek handler 呼叫 → 整組砍(含 `videoIsURL` field
- **Q3** `crypto/rand` 16 bytes → hex不引入 google/uuid
- **Q5** navigator.language fallbackzh* → zh-TW / en* → en-US / else → zh-TW
- **Q7** preferences JSON 用 write-rename atomic pattern
- **Architect 對 PM §11 回答**
- **§11-1** `preferences.json` @ `<dataDir>/`write-rename 原子寫fallback DefaultPreferences
- **§11-2** 樂觀 ~4s / 悲觀 ~8s 達標,但 **Windows + Defender 最壞 ~11s 可能超時**,建議 M8-10 實測,超時則 AC-1.3 放寬到 **12 秒**Architect 說 12PM 說 15差 3 秒)
- **§11-3** idle RAM 樂觀 ~370MB 達標,悲觀 ~500MB 超 50MB**建議 PRD clarify「450MB 不含 browser tab」**
- **關鍵發現 F-2**Restart Server port 保留 — TDD 允許 fallback 到 3722 會讓瀏覽器 tab URL 過期導致 Offline Overlay 永卡 → Restart 強制保留舊 port不可 fallback用不了就進 Error stateArchitect 自補)
- **關鍵發現 B-1**watchServer 改 Error state 時等於砍掉 OS 通知(違反 R5-D1→ 新增 `sendCrashNotification()` non-blocking toast新檔 `visiona-local/notify.go`Architect 自補)
### R5-E 追加決策2026-04-14互審結論後使用者追加
使用者把「AC-1.3 時間預算」問題從「要多快」翻轉成「**讓使用者感覺進度有在推動**」— 採 Nielsen Norman perceived performance 原則而非硬時間指標。
| # | 決定 |
|---|------|
| R5-E1 | **AC-1.3 時間上限放寬到 60 秒**(原 10 秒),原則是 perceived performance > 硬時間指標 |
| R5-E2 | **啟動全程必須有階段化進度顯示**:每個階段有編號 / 動作描述 / 視覺回饋 / 中英雙語文案 |
| R5-E3 | **任一階段卡超過 20 秒**要顯示「正在重試」類提示,不可白畫面 |
| R5-E4 | **超過 60 秒總上限仍未就緒** → 進 Error state和 watchServer 3 次失敗一致),顯示重試 / 回報 / 檢視 log 三按鈕 |
| R5-E5 | **階段文字由 Design Agent 決定**(使用者授權)— 使用者最後審 wireframe 時可以 override |
| R5-E6 | **瀏覽器就緒偵測採 WebSocket 連上訊號**(不做新 endpoint不做固定延遲WebSocket hub 收到第一個 client 連線視為第 6 階段「ready」 |
### 啟動階段建議6 階段Design 最終定版)
1. 初始化 Wails 控制台
2. 檢查 Python runtime + 驅動
3. 啟動本機伺服器port binding
4. 偵測 Kneron 裝置
5. 開啟瀏覽器
6. 瀏覽器就緒WebSocket 連上)
### 技術影響(三方 v2.1 補丁輪要吸收)
- 新增 Wails event`startup:progress {stage, label_zh, label_en, status}`
- 新增 Wails event`startup:stage-timeout {stage}`20 秒卡住觸發)
- `StartServer()` 改為階段化,每個階段 emit event
- Wails 控制台 vanilla JS 要訂閱 event 更新進度面板
- 新增啟動進度面板 UIDesign Spec v2.1 wireframe
- M8-4/M8-5 工時可能 +0.5-1 天
### 修正計畫v2.1 補丁輪)
- PM → PRD v2.1:補 R5-D1/D2/D3、Minor 1-4、AC-1.3 放寬到 12 秒、idle RAM 加註「不含 browser tab」
- Design → Design Spec v2.1
- settings-update.md 修 Major 1+2`preferences.json @ <dataDir>/` + `DefaultPreferences()` 平台差異)
- control-panel.md §4.4 log 1000→2000 / §6.2 補 OS notification + Report 按鈕 hold 註記 / §7.1 第 5 步「首次→每次」
- Architect → TDD v2.1
- R5-D1 sendCrashNotification 實作(新檔 notify.go
- R5-D2 DefaultPreferences 依 GOOS
- R5-D3 砍 autoOpenedThisSession flag每次 StartServer 都 trigger OpenInBrowser
- M8-9 驗收條件修正移除「Restart 不會二次開」條件)
- Restart 同 port 規則
- PM §11-1/2/3 寫入 TDD
- Q4 grace period 採 PM 7 秒 + 1 秒 modal 建議
- Q1/Q3/Q5/Q7 自決結果寫入
> 以下是 2026-04-12 之前的進度快照,保留備查。變更確認後需要全面更新。
## 🎉 M1 達成總結
- `dist/visiona-local.dmg` (70MB) 可雙擊安裝
- 全新環境下能 mount → 拖到任意位置 → 雙擊執行
- Mock 模式 server 子程序自動啟動Bundle 內 `Resources/bin/visiona-local-server`
- API endpoints 全部 200health、info、devices、models
- 乾淨退出SIGTERM → 5s → SIGKILL
- 資料目錄:`~/Library/Application Support/visiona-local/`lock + ipc-port + logs + custom-models
- 第三輪 P0 bugs 修復:(1) APFS case-insensitive 自我毀滅、(2) `--python` flag 不存在、(3) `Resources/bin/` 路徑漏 `bin/` 子目錄
## M1 收尾C 已完成)
-`GET /` 404 修復Makefile 加 `build-embed` target把 frontend/out → server/web/out 同步,再 build server binary。dmg 71MB 含完整主 UI21KB 首頁 + Next.js chunks
## M2-M6 任務清單(使用者選 Y全包macOS 為主)
### M2 — i18n + Settings 分頁調整
| # | 任務 | 狀態 |
|---|------|------|
| M2-1 | i18n 中英雙語切換 | ✅ |
| M2-2 | Settings 4 分頁重構 | ✅ |
| M2-3 | 清 cluster.* i18n keys | ✅ |
| M2-4 | sidebar Workspace 接 i18n | ✅ |
| M2-5 | rebuild dmg + smoke test | ✅71MB, root+settings 200, server 從 bundle Resources 起) |
### M3 — Python runtime 策略 A 內嵌 + KneronPLUS wheel
| # | 任務 | 狀態 |
|---|------|------|
| M3-1 | vendor-python (PBS 3.12.9, 15MB) | ✅ |
| M3-2 | vendor-wheels (9 wheels, 71MB) | ✅ |
| M3-3 | ensureBundledPython() 實作 | ✅ |
| M3-4 | payload-macos stage python + wheels | ✅ |
| M3-5 | dylib codesign | ✅ 不需要Gatekeeper 沒擋) |
| M3-6 | rebuild dmg + smoke test | ✅ 157MB, venv + 9 wheels + import kp 全通過 |
### M6 — ffmpeg + yt-dlp 內嵌(完整離線)
| # | 任務 | 狀態 |
|---|------|------|
| M6-1 | vendor-ffmpeg | ✅77MB **GPL** build, 由 VISIONA_ALLOW_GPL_FFMPEG flag 放行) |
| M6-2 | vendor-ytdlp | ✅35MB, yt-dlp 2026.03.17 |
| M6-3 | payload-macos stage ffmpeg + yt-dlp | ✅ |
| M6-4 | server internal/deps/ env var 偵測 | ✅VISIONA_BUNDLE_BIN_DIR |
| M6-5 | rebuild dmg | ✅ 220MB |
## 🔴 P1 release blockerffmpeg 授權
- macOS 上現成的 ffmpeg static binary 全部都是 GPL build含 --enable-gpl --enable-libx264
- 使用者決定 **B**:暫定使用 GPL build發佈前由法務 review
- 必須在 PRD 第三方授權頁明確標 `ffmpeg: GPL build (under legal review)`
- 替代方案保留:自 build LGPL需 build pipeline/ online download / 砍 ffmpeg 功能
### M4 / M5 — Windows / Linux無法在這台 Mac 驗證,僅寫 script
| # | 任務 | 狀態 |
|---|------|------|
| M4-1 | Inno Setup .iss script | ✅ installer/windows/visiona-local.iss |
| M4-2 | Makefile wails-windows / exe target | ✅ uname 守門 |
| M4-3 | payload-windows | ✅ 在 macOS 上跑通 vendor 部分378MB |
| M5-1 | build-appimage.sh | ✅ installer/linux/build-appimage.sh |
| M5-2 | Makefile wails-linux / appimage | ✅ uname 守門 |
| M5-3 | payload-linux + udev rule | ✅ installer/linux/99-kneron.rules + install-udev.sh在 macOS 上跑通 vendor317MB |
### lifecycle 補件M1+ TODO 移入 M2-M6 末尾)
| # | 任務 | 狀態 |
|---|------|------|
| L-1 | watchServer() 每 10s health check | ✅ 連續 3 次失敗 emit server:dead event |
| L-2 | Fatal 原生對話框 | ✅ macOS osascript / Win PS / Linux zenity-kdialog-stderr |
| L-3 | Wails /ipc/raise endpoint | ✅ 隨機 port + wails-ipc-port 檔案 |
| L-4 | stale process 清理 | ✅ macOS/Linux lsof+psWindows 留 TODO |
## M7 — Windows 實機 build + splash regression 修復2026-04-12
### M7-AWindows 一鍵 build 工具鏈(使用者在 Windows 機器上實機跑 bootstrap
新增 `local-tool/scripts/bootstrap-linux.sh` + `bootstrap-windows.ps1`,目標是使用者 clone repo 後一行指令完成依賴安裝 + vendor 下載 + payload 打包 + wails build + installer 產出。
| # | 任務 | 狀態 |
|---|------|------|
| M7-A1 | 統一專案目錄名為 `local-tool`(連字號),清掉所有 `local_tool` 殘留 | ✅ |
| M7-A2 | bootstrap-linux.shapt + go 1.22.5 + node 20 + pnpm + wails | ✅ 未在 Ubuntu 實機驗證 |
| M7-A3 | bootstrap-windows.ps1winget 安裝 git/go/node/python/msys2/inno setup + build | ✅ Windows 實機驗證通過 |
**Windows build 踩坑紀錄**(每個都修好並 push
1. **PowerShell 5.1 不支援 `&&`** → 改用陣列 + `-join ' && '`
2. **中文亂碼** → ps1 加 UTF-8 BOM
3. **`pip3: command not found`** → Makefile 偵測 pip/pip3/`python -m pip` + bootstrap `MSYS2_PATH_TYPE=inherit` 讓 bash 繼承 Windows PATH
4. **`unzip: command not found`** → Makefile 改用 Python zipfile 解壓,移除 unzip 依賴
5. **`server.exe` 沒 build** → 新增 `build-server-windows` cross-build target
6. **Microsoft Store `python3` stub** → Makefile 偵測時排除 `*WindowsApps*` 路徑bootstrap 主動找真實 Python 並以 `VISIONA_PYTHON` 環境變數傳入
7. **`/tmp/ffmpeg-win.zip` 路徑問題** → Windows 版 python.exe 不懂 MSYS2 的 `/tmp`,改用相對路徑 `vendor/ffmpeg/windows/ffmpeg-win.zip`
8. **Inno Setup `ISCC.exe` 找不到** → winget 裝到 user-scope `%LOCALAPPDATA%\Programs\Inno Setup 6\`,非傳統 `Program Files (x86)`。Find-Iscc 多層偵測 + 新增 `ISCC` 環境變數 override + user-scope 固定路徑 + 登錄檔 fallback
9. **`ChineseTraditional.isl` 不存在** → Inno Setup 6.3+ 官方移除繁體中文語系,改用 `#ifdef WITH_TRAD_CHINESE` 條件宏,預設只用英文 installer UI不影響 app 本身 i18n
10. **`make exe` 成功但 dist 空** → PS → bash quoting 問題,改寫 tmp `.visiona-build.sh` 檔再執行;另外拆出 `exe-only` target 讓使用者刪掉 dist 能快速重跑 iscc 不重 build wails
11. **Makefile `exe` recipe 診斷輸出** → 印 cwd / iscc exit code / dist 內容,避免靜默失敗
**成果**`E:\visionA\local-tool\dist\visiona-local-0.1.0-windows-x64.exe` 成功產出iscc 正常 compile 通過。
### M7-B🔴 splash regression 修復P0
**根因**`visiona-local/frontend/` 是 M1 階段從 edge-ai-platform 複製過來的 **installer wizard HTML/JS/CSS**,整組沒清理。`main.go``//go:embed all:frontend` 直接把這堆 wizard 當 Wails 主視窗內容,使用者開 app 看到的是 `Edge AI Platform Installer` 而不是 Next.js 主 UI。
**影響範圍**macOS dmg 也有同樣 bug只是 M1 驗收時是用瀏覽器連 `http://localhost:3721/` 驗證 server 回應,沒真的打開 Wails 視窗看 UI所以 regression 一路混過 M1-M6 直到 Windows 實機驗證才被發現。
**修法**commit `570e040`,刪 1248 行 / 新增 79 行):
- `visiona-local/frontend/index.html` → 極簡 splashlogo + spinner + status
- `visiona-local/frontend/app.js` → ES module輪詢 `GetServerStatus()` binding拿到 `running=true` + `url``window.location.replace(url + '/')` 跳到 Next.js 主 UI
- `visiona-local/frontend/style.css` → 深色 splash 樣式
Next.js 主 UI 完全不使用 Wails JS binding純 HTTP API`wails://` 跳到 `http://127.0.0.1:<port>/` 後功能完整可用。
| # | 任務 | 狀態 |
|---|------|------|
| M7-B1 | 清掉 frontend/ 的 edge-ai-platform wizard 殘留 | ✅ |
| M7-B2 | 改寫為 splash + redirect | ✅ |
| M7-B3 | Windows 實機重 build + 測試 splash → Next.js UI 跳轉 | ⏳ 待使用者驗證 |
| M7-B4 | macOS 重 build 驗證同樣修復有效 | ⏳ 待排程 |
## 專案概述
visionA-local 是 `/Users/jimchen/Innovedus/edge-ai-platform` 的 local 版本,目標是把原本要 deploy 到 EC2/staging Docker 環境的網頁工具,改造成可在本地單機執行的桌面應用,並打包成 GUI 安裝檔,支援 macOS / Windows / Ubuntu 三平台。
**任務等級**L 級(完整流程)
## 進度表
| 階段 | 狀態 | 完成時間 | 備註 |
|------|------|----------|------|
| 需求討論(三方聯合) | ✅ 已完成 | 2026-04-11 | 四輪討論 + 交叉審閱完成 |
| PRD | ✅ 已完成 | 2026-04-11 | v1.2 定稿 |
| 設計規格 | ✅ 已完成 | 2026-04-11 | 第四輪修訂定稿 |
| 系統架構 / TDD | ✅ 已完成 | 2026-04-11 | 第四輪修訂 + Plan B 補件 |
| 開發(增量式) | 🔄 進行中 | - | M1-M6 macOS ✅M7 Windows build 完成splash 修復待 Windows 驗證 |
| Review | ⏳ 待開始 | - | - |
| 測試 | ⏳ 待開始 | - | - |
| 打包 / 安裝檔 | 🔄 進行中 | - | macOS dmg ✅Windows exe 成功產出UI 待驗證Linux AppImage 待 Linux 機器驗證 |
| 交付 | ⏳ 待開始 | - | - |
## 當前待辦
- [x] 第一輪三方分析(已完成)
- [x] 使用者回答 15 個關鍵決策問題(已完成)
- [x] PM Agent 產出正式 PRD2026-04-11
- [x] Design Agent 產出正式設計規格2026-04-11
- [x] Architect Agent 產出正式 Design Doc + TDD2026-04-11
- [x] 第三輪使用者決策(砍 tray、B4、C2、D2、E1/E2/E3
- [x] Design Agent 依第三輪決策修訂設計規格2026-04-11
- [x] PM Agent 依第三輪決策修訂 PRD2026-04-11
- [x] Architect Agent 依第三輪決策修訂架構文件2026-04-11
- [x] bootstrap-linux.sh + bootstrap-windows.ps12026-04-12
- [x] Windows 一鍵 build 踩坑全清11 項修好 push2026-04-12
- [x] Windows `dist/visiona-local-0.1.0-windows-x64.exe` 成功產出2026-04-12
- [x] M7-B splash regression 修復commit 570e0402026-04-12
- [ ] **使用者 Windows 重 build + 驗證 splash → Next.js 主 UI 跳轉**
- [ ] **macOS 重 build 驗證 splash 修復有效**(之前 M1 驗收漏的 UI 環節)
- [ ] Ubuntu 端實機驗證 bootstrap-linux.sh
- [ ] 三方互相審閱PM↔Design↔Architect 交叉 review
- [ ] 使用者確認三份文件
## M1 開發進度(第二階段)
| # | 任務 | 狀態 |
|---|------|------|
| M1-1 | repo 骨架初始化 | ✅ 完成Review 通過) |
| M1-2 | 複製 server core跳過 cluster/tunnel/flash/update | ✅ 完成Review 通過) |
| M1-3 | 改寫 main.go / config.go / router.go | ✅ 完成Review 通過) |
| M1-4 | 複製 frontend | ✅ 完成 |
| M1-5 | build Go server binary | ✅ 完成Review 通過) |
| M1-6 | 複製 server/data 預置模型 | ✅ 已於 M1-2 併入8 個 .nef, 73MB|
| M1-7 | 清理前端 cluster/relay/tunnel UI | ✅ 完成Review 通過) |
| M1-8 | pnpm build 通過 | ✅ 已於 M1-7 併入驗收 |
| M1-9 | 複製 installer shell 改名 visiona-local | ✅ 完成Review 通過) |
| M1-10 | 改寫 installer + Python 雙策略空殼 | ✅ 完成Review 通過) |
| M1-11 | payload 打包 | ✅ 完成103MB含 server binary + 8 nef |
| M1-12 | wails build + ad-hoc sign + dmgbuild | ✅ 完成(.dmg 70MB 產出) |
| M1-13 | 全新 mac 端到端驗證 | ✅ 完成5 核心承諾全達成2 P0 + 1 路徑 bug 已修復) |
## 第二輪產出(進行中)
- Architect`/Users/jimchen/visionA/local-tool/.autoflow/04-architecture/`
- `design-doc.md`(索引)
- `TDD.md`(索引)
- `architecture-overview.md`
- `dependency-bundling.md`
- `packaging.md`
- `build-pipeline.md`
- `tray-and-lifecycle.md`
- `i18n.md`
- `risks-and-mitigations.md`
- `api-endpoints.md`
- `code-reuse-plan.md`
- `removed-code.md`
## 重要決策紀錄
### 來源與策略
- **參考原專案**`/Users/jimchen/Innovedus/edge-ai-platform`
- **程式碼策略**:重新建立 local-tool可從 edge-ai-platform 自由取用任何程式碼(不做 fork、不做 submodule
- **產品名稱**visionA-local
- **Bundle ID**(暫定):`com.innovedus.visiona-local`
### 產品定位
- 單機桌面應用,**不需要 proxy / nginx / relay / tunnel**
- Web UI 跑在 localhost沿用原本 3721 埠或視情況調整)
- 必須能打包成 GUI 安裝檔,支援 **macOS / Windows / Ubuntu**
- 目標是「裝起來像一般 app」的體驗類似 Docker Desktop / Ollama
### 功能取捨(全照建議)
| 功能 | 決定 |
|------|------|
| 裝置管理USB 連 Kneron | ✅ 保留 |
| 攝影機串流MJPEG + FFmpeg | ✅ 保留 |
| 模型管理(上傳/切換 .nef | ✅ 保留 |
| 推論引擎(分類/偵測/臉辨) | ✅ 保留 |
| Mock 模式 | ✅ 保留 |
| Tray系統列常駐 | ❌ 砍2026-04-11 改變Q7 選關閉=結束後 tray 價值降低) |
| Cluster多裝置叢集 | ❌ 砍 |
| Relay / Tunnel遠端連線 | ❌ 砍 |
### 技術決策
- **GUI 框架**Wails沿用 edge-ai-platform 的 `installer/`
- **依賴打包**:一鍵安裝所有依賴 — Python runtime + KneronPLUS SDK + ffmpeg + 預置模型 .nef 全部包進安裝檔,使用者不需要事先裝任何東西
- **前端清理**:清掉 relay 模式切換、cluster 管理等 UI
### 原專案技術堆疊(沿用)
- 前端Next.js 16 + React 19 + TypeScript + shadcn/Radix + Tailwind + Zustand
- 後端Go 1.26 + Gin + go:embed
- 硬體Python KneronPLUS SDK
- 儲存:本地 JSON + 記憶體(無 DB
### 第四輪使用者決策2026-04-11三方交叉審閱後
| # | 問題 | 決定 |
|---|------|------|
| R4-1 | Kneron 授權 | **繼續內嵌**(不主動問 KneronB4 延續,發佈前 gate 維持) |
| R4-2 | MJPEG 延遲指標 | **首次 ≤250ms / 穩定後 ≤150ms** |
| R4-3 | WCAG 2.2 AA | **不做**(改為「盡力而為」,明確 scope 外) |
| R4-4 | 安裝時間 / RAM 指標 | **放寬**:安裝上限 5 分鐘、Mock idle RAM ≤600MB |
| R4-5 | 資料目錄命名 | **全小寫 `visiona-local`**(符合 Bundle ID + Linux 慣例) |
| R4-6 | 快捷鍵 | ⌘R → ⌘Shift+R⌘Shift+W 取消⌘4 已涵蓋) |
| R4-7 | 首次推論時間 AC | 拆為 **首次 30s / 回訪 15s** 兩級 |
| R4-8 | OS 通知策略 | 裝置連/斷 → App 內 toastServer 崩潰 → shell out 原生通知 |
### 第三輪使用者決策2026-04-11三方第二輪文件後
| # | 問題 | 決定 |
|---|------|------|
| Q-A | Tray 角色衝突Q7 選關閉=結束後 tray 價值變低) | **A3 砍掉 tray**,省跨平台圖資產與 Wails tray 踩坑。從「保留功能」改為「不做」 |
| Q-B | Kneron 預置模型 re-distribution 授權 | **B4**:先假設可重新散布,開發時繼續內嵌,**發佈前必須再確認**(風險標記) |
| Q-C | M1 範圍 | **C2 不接受「M1 先不清前端」**M1 就要把前端 cluster/relay UI 清乾淨,一次到位 |
| Q-D | vendor/ 目錄管理 | **D2 不進 git**,用 `make vendor-sync` 下載 |
| Q-E1 | macOS 資料目錄 | 用 `~/Library/Application Support/visionA-local/`OS 慣例) |
| Q-E2 | Workspace 提升為 sidebar 一級 | OK |
| Q-E3 | Settings「外觀」分頁取消語言併入「一般」 | OK |
### 第二輪使用者決策2026-04-11
| # | 問題 | 決定 |
|---|------|------|
| Q1 | Python runtime 策略 | **A**(完全離線內嵌 python-build-standalone同時**保留 B**(偵測系統 Python作為 fallback 選項 |
| Q2 | 程式碼簽章 | **C** 都不買(內部工具接受警告) |
| Q3 | 最低 OS 版本 | 都最新兩版macOS 14/15、Windows 10/11、Ubuntu 22.04/24.04 |
| Q4 | ARM 支援 | 三平台都**只做 x86_64**,之後有需求再加(使用者是 Intel Mac |
| Q5 | 預置模型 | 全部打包(~73MB |
| Q6 | Auto-update | 先不做 |
| Q7 | 視窗關閉行為 | **B** 傳統式(關閉 = 結束程式) |
| Q8 | 預設執行模式 | 直接真實硬體模式(不預設 Mock |
| Q9 | 韌體燒錄 flash | **B** 砍掉 |
| Q10 | yt-dlp / media/url | **A** 保留(要打包 yt-dlp |
| Q11 | Bundle ID | `com.innovedus.visiona-local` 確認 |
| Q12 | Telemetry / 崩潰回報 | 預設不做 |
| Q13 | 多語系 | 中英雙語 |
| Q14 | Logo / 品牌 | 先沿用 edge-ai-platform之後有需要再換 |
| Q15 | 深色模式 | 跟隨系統 |
## M1-10 留下的 M2/M1+ TODO不阻斷 M1
- `ensureBundledPython()` 實作(解壓 python-build-standalone、建 venv、離線 pip install wheels— M2
- Wails `/ipc/raise` endpoint真正的 single-instance focus— M1+
- `watchServer()` 健康偵測 goroutine每 10s health check— M1+
- `isOurStaleServer` / `killByPort`stale process 清理)— M1+
- Fatal 錯誤的原生對話框(目前只 emit event— M1+
## 未解決問題
- **Kneron 預置模型 re-distribution 授權**B4 決策):開發階段先假設可用,發佈前必須跟 Kneron 官方確認。若不允許需改為首次啟動線上下載,會破壞「完全離線」承諾。
- **ffmpeg GPL 授權 release blocker**M6macOS 上的 ffmpeg static build 全是 GPL暫定用 `VISIONA_ALLOW_GPL_FFMPEG=1` 放行,發佈前需法務 review 或改走自 build LGPL / 線上下載 / 砍 ffmpeg 三條路。
- **內部 Gitea Releases / GitHub Releases 基礎設施**:發佈策略假設有此通路,待確認。
- **CI runner 三平台是否齊備**macOS / Windows / Linux runner 狀況待確認。
- **M1 驗收流程漏看 Wails 視窗內容**M1-13 當初是用瀏覽器連 `http://localhost:3721/` 驗證 server 回應,沒真的開 Wails window 看 UI導致 edge-ai-platform installer wizard 殘留一路混過 M1-M6 到 Windows 實機驗證才發現。後續 M 任務的驗收 checklist 必須強制「開 app window 確認主 UI 是 Next.js 而非 splash / wizard / 白畫面」。
## 第一輪三方分析產出(已完成)
- PM`/Users/jimchen/visionA/local-tool/.autoflow/01-requirements/pm-analysis-round1.md`
- Design`/Users/jimchen/visionA/local-tool/.autoflow/03-design/design-analysis-round1.md`
- Architect`/Users/jimchen/visionA/local-tool/.autoflow/04-architecture/architect-analysis-round1.md`