jim800121chen b71ff4cd3c perf(local-tool): Windows KL520 cold-boot connect 106s → ~40s(跳過多餘 reset)
背景:
Windows 實測 KL520 首次 connect 耗時 106 秒,原因是 reset 流程內部重複
firmware load:
  1. 進來 Loader → load firmware (35s) → Comp/U
  2. reset 退回 Loader → bridge 重啟
  3. reconnect 進來又是 Loader → load firmware (30s) → Comp/U
  4. Loader reconnect 第一次常 fail(15s timeout)
總共 ~65s 花在「砍掉剛載好的 firmware、再載一次」的白工上。

根因:先前修的 needsReset 邏輯不管 firmware 新舊一律 reset。但 Error 15
只發生在「Comp/U 是上次 session 殘留」的情境;「本次 connect 內部剛載的
Comp/U」session 是乾淨的,不需要 reset。

修法(條件性 reset):
- server/scripts/kneron_bridge.py:connect handler 新增追蹤本次有無走
  firmware load flow,return 多帶 `fresh_firmware_loaded` bool
- server/internal/driver/kneron/kl720_driver.go:Connect 讀 flag,若為
  true 就 skipReset(firmware 剛載的,session 已乾淨)

驗證(2026-04-21):
- `/tmp/test_bridge.py` 拔插 USB 後跑 `connect (fw=Loader) →
  fresh_firmware_loaded=True → skip reset → load_model → inference`
  → 11 detections(person×8, tie×3, latency 332ms)
- Mac UI Comp/U 殘留路徑:reset → 11 bbox ✓
- Mac UI Loader cold-boot 路徑(拔插後):skip reset → 11 bbox ✓

預期效益:
- Windows cold-boot(常見):106s → ~40s(省 65s)
- Mac 跨 session(常見):~15-20s 不變
- 極少數(Windows device 未斷電但跨 server session):走完整 reset

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 11:09:25 +08:00

53 KiB
Raw Blame History

專案進度 — visionA-local

目的:全新專案(從 edge-ai-platform 衍生的 local 版本)

當前階段:🔴 第一階段回溯 — L 級重大方向變更Wails 內嵌 → Wails 控制台 + 瀏覽器 Web UI

當前狀態: 使用者決策全部收齊R5 第五輪決策),待三方產出正式 PRD v2 / Design Spec v2 / TDD v2

最後更新2026-04-21

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_sendApiKPException 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 → inference11 個 detection 正常回傳
  • 對比 Go driver 實際路徑:connect → load_model → inference 跳過了 reset

兇手commit ddf0eb82026-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 初始值改 nulloverlay 改為 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 timeout60s → 120s,為 Windows worst-case~65s留 buffer

Python bridgeserver/scripts/kneron_bridge.py

  • 無實質改動(試過 host-side letterbox、numpy→bytes、明確傳 w/h 全部無效 → 還原回原版,確認問題在 Go driver 的 reset 流程)
  • 只加了 debug logInference: 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

  • Mac UI Comp/U 殘留路徑reset 後推論 11 個 bbox 正確
  • Mac UI Loader cold-boot 路徑(拔插 USBskip reset 後推論 11 個 bbox 正確
  • 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.tsxconsole.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.pyimport 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
    • dmgauto-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.md419 行)
  • Design 分析完成:03-design/design-analysis-round2-refactor.md537 行)
  • Architect 分析完成:04-architecture/architect-analysis-round2-refactor.md798 行)

三方共識(無分歧)

  1. 技術可行,沿用率 85-95%,估 ~10 人天
  2. 砍 yt-dlpvendor 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 到 repovendor/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.002-prd/PRD-v2.md484 行)— PM 5 個懸念見 §11
  • Design Spec v2.003-design/design-spec-v2.md99 行索引)+ 03-design/v2/*.md5 子檔)
  • TDD v2.004-architecture/TDD-v2.md136 行索引)+ 04-architecture/v2/*.md8 子檔,~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-macos2026-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 成功:163MBGPL 版 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 已檢查 ffprobeprogress.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} 啟動 logLoaded 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/datamodels.json 存在為命中條件
  • main() 拆出兩個獨立變數:
    • builtInDataDirread-onlybundle 內)— 給 model.NewRepository(filepath.Join(builtInDataDir, "models.json"))flash.NewService(deviceMgr, modelRepo, builtInDataDir) 使用(因 flash 也要解析 model.filePath 相對路徑 "data/nef/..."
    • dataDirwritableuser home— 給 custom-models / sentinel file / logs 使用,語意不變
  • cfg.DataDir == "" 時 fallback 成 builtInDataDir(保 dev mode go run ./server 繼續可跑)

為什麼順便拆職責:原本的 bug 不只影響 modelRepo,也影響 flash.Serviceflash.service.go:115-121s.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/modelstotal: 15(不是原估計的 8因為 models.json 有 15 個條目,部分 model 共用 nef
  • 啟動 logBuilt-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.nef7.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-1Linux AppImage 布局(usr/bin/<exe> + usr/lib/visiona-local/data/三候選全不命中AppRun 已 export VISIONA_BUNDLE_LIB_DIR 但 server 沒讀。備註 resolveBridgeScript 先前就有同樣缺失。
  • Minor-1fallback 沒 filepath.Abs
  • Minor-2fallback 沒 log 試過的候選
  • Suggestion s-1:抽公用 findFirstExisting helper
  • Suggestion s-2dataDir 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.Absm-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.Warnlogger 尚未初始化前呼叫,合理),下次 logger 重構時統一
    • Suggestion s2-1findFirstExisting 可改 (dir, tried, ok bool) 更 idiomatic非必須

M8-10b/c 待使用者驗證

  • Windows:使用者在 Windows 實機跑 bootstrap + make exe → 驗證 splash → Wails 控制台 6 階段啟動 → 瀏覽器 Web UI
  • LinuxUbuntu 實機跑 bootstrap-linux.sh + make appimage → 驗證 xdg-open 預設 OFF + notify-send fallback

M8-3 Minor + Suggestion低優先

  • MinorBUILD.md §Verification §5 預期 spctl --assess=accepted 實測會被 reject改為 codesign -v
  • Suggestion 1vendor-ffmpeg target 可補 sha256 對比防呆
  • Suggestion 2payload-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 個 Major4 個 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/7M8-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 狀態 預設 OFFmacOS/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-2Restart Server port 保留 — TDD 允許 fallback 到 3722 會讓瀏覽器 tab URL 過期導致 Offline Overlay 永卡 → Restart 強制保留舊 port不可 fallback用不了就進 Error stateArchitect 自補)
  • 關鍵發現 B-1watchServer 改 Error state 時等於砍掉 OS 通知(違反 R5-D1→ 新增 sendCrashNotification() non-blocking toast新檔 visiona-local/notify.goArchitect 自補)

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 eventstartup:progress {stage, label_zh, label_en, status}
  • 新增 Wails eventstartup: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+2preferences.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 + urlwindow.location.replace(url + '/') 跳到 Next.js 主 UI
  • visiona-local/frontend/style.css → 深色 splash 樣式

Next.js 主 UI 完全不使用 Wails JS binding純 HTTP APIwails:// 跳到 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 機器驗證
交付 待開始 - -

當前待辦

  • 第一輪三方分析(已完成)
  • 使用者回答 15 個關鍵決策問題(已完成)
  • PM Agent 產出正式 PRD2026-04-11
  • Design Agent 產出正式設計規格2026-04-11
  • Architect Agent 產出正式 Design Doc + TDD2026-04-11
  • 第三輪使用者決策(砍 tray、B4、C2、D2、E1/E2/E3
  • Design Agent 依第三輪決策修訂設計規格2026-04-11
  • PM Agent 依第三輪決策修訂 PRD2026-04-11
  • Architect Agent 依第三輪決策修訂架構文件2026-04-11
  • bootstrap-linux.sh + bootstrap-windows.ps12026-04-12
  • Windows 一鍵 build 踩坑全清11 項修好 push2026-04-12
  • Windows dist/visiona-local-0.1.0-windows-x64.exe 成功產出2026-04-12
  • 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 / killByPortstale process 清理)— M1+
  • Fatal 錯誤的原生對話框(目前只 emit event— M1+

未解決問題

  • Kneron 預置模型 re-distribution 授權B4 決策):開發階段先假設可用,發佈前必須跟 Kneron 官方確認。若不允許需改為首次啟動線上下載,會破壞「完全離線」承諾。
  • ffmpeg GPL 授權 release blockerM6macOS 上的 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