jim800121chen 8cd5751ce3 feat(local-tool): M8 重構 — Wails 控制台 + 瀏覽器 Web UI(R5 決策)
依 R5 五輪決策把 visionA-local 從「Wails 內嵌 Next.js」重構為「Wails
本機伺服器控制台 + 瀏覽器 Web UI」模式(類比 Docker Desktop / Ollama)。

程式碼變動
  - M8-1 砍 yt-dlp 全套(後端 resolver / URL handler / 前端 URL tab /
    Makefile vendor / installer / bootstrap / CI workflow,-555 行)
  - M8-2 砍 Mock 模式全套(driver/mock、mock_camera、Settings runtimeMode、
    VISIONA_MOCK 環境變數,-528 行)
  - M8-3 ffmpeg 從 GPL 切換到 LGPL 混合方案:Windows/Linux 用 BtbN 現成
    LGPL binary,macOS 自 build minimal decoder-only 進 git
    (vendor/ffmpeg/macos/ffmpeg 5.7MB + ffprobe 5.6MB,比 GPL 版省 85% 空間)
  - M8-4 Wails Server Controller:state machine、log ring buffer 2000 行、
    preferences.json atomic write、boot-id、Gin SkipPaths、shutdown 7+1 秒、
    notify_*.go 三平台 OS 通知、watchServer 改 Error state 不 os.Exit
  - M8-4b 啟動階段管線 R5-E:6 階段進度 event、20s soft / 60s hard timeout、
    stage 5/6 skip 規則、sentinel file、RestartStartupSequence 5 步驟
  - M8-5 Wails 控制台 vanilla HTML/JS/CSS(9 檔 ~2012 行)取代 M7-B splash:
    state 視覺、log panel、startup progress panel、Stage 6 manual CTA
    pulse、shutdown modal、Settings、Dark Mode、i18n 中英雙語
  - M8-6 上傳影片副檔名擴充(mp4/avi/mov/mpeg/mpg)
  - M8-7 Web UI Server Offline Overlay(role=alertdialog + focus trap +
    wsEverConnected 容錯 + Page Visibility)
  - M8-8 CORS middleware(127.0.0.1/localhost only + suffix attack 防護)+
    ws/origin.go 獨立 WebSocket CheckOrigin 避 package cycle
  - MAJ-4 server:shutdown-imminent WebSocket broadcast 機制
    (/ws/system endpoint + notifyShutdownImminent helper)
  - M8-9 Boot-ID + 瀏覽器 tab 自動重連(sessionStorage loop guard)

品質
  - ~105+ 新 unit test + race detector (-count=2) 全綠
  - 10 個 milestone 全部通過 Reviewer 審查
  - 三方 v2 + v2.1 文件(PRD / Design Spec / TDD)+ 交叉互審紀錄
    收錄在 .autoflow/

交付前待處理(M8-10)
  - 重跑 make payload-macos 把舊 GPL 77MB binary 換成新 LGPL
  - 三平台 end-to-end build 驗證

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:57:54 +08:00

17 KiB
Raw Permalink Blame History

TDD v2 — visionA-localRound-2 refactor 後)

作者Architect Agent 版本:v2.1 日期2026-04-14v2.0 → v2.1 吸收 PM 審閱 + R5-D + R5-E 狀態Draftv2.1 由 Architect 根據 PM 互審 + R5-D/E 新決策產出,待三方再次 cross-review + 使用者確認) 取代:TDD.md v1.02026-04-11四輪修訂 + Plan B 補件) 決策來源:progress.md §「R5 第五輪使用者決策」+ 「R5-D 補充決策」+「R5-E 階段化啟動新指標」2026-04-14


0. 版本資訊與變更摘要

0.0 v2.0 → v2.1 差異速覽2026-04-14 本次更新)

# 變更面向 v2.0 v2.1 觸發
1 AC-1.3 啟動時間 10 秒硬指標 60 秒 + 6 階段進度顯示20 s soft timeout / 60 s hard timeout R5-E1~E6
2 自動開瀏覽器行為 首次啟動開一次(autoOpenedThisSession flag 控制 per-session-once 每次 Start/Restart 成功都開(砍 flagR5-D3 R5-D3
3 AutoOpenBrowser 預設值 三平台統一 true macOS/Windows true、Linux false(分平台 default R5-D2
4 Server 崩潰通知 僅控制台 Error banner Error banner + OS 原生通知並存notify.go 三平台實作) R5-D1
5 Shutdown grace period 5 s → 10 sArchitect Q4 提議) 7 s + 1 s 顯示「停止中…」modalPM Q4 定案) PM Q4
6 Shutdown race condition 靠 polling 3 次失敗0.5-15 s 後顯示 overlay WebSocket server:shutdown-imminent 廣播(秒內觸發 overlay PM Minor 4
7 Restart port 處理 允許 fallback3721 → 3722 …) 強制保留舊 port,被佔用進 Error state Architect F-2
8 Preferences 持久化 提及 atomic write 但未定位置 <dataDir>/preferences.json + write-rename(完整 spec PM §11-1
9 videoIsURL field 處置 「視情況保留或刪除」 明確刪除grep 證實是 dead code PM Minor 5
10 idle RAM 450 MB 目標 未澄清範圍 澄清不含瀏覽器 tab(只計 Wails + Go server + Python PM §11-3
11 日常啟動時間估算 新增估算 ~3.8 s,符合 AC-2.1 ≤ 5 s PM Minor 2
12 boot-id 生成方式 建議 google/uuid crypto/rand 16 bytes → hex(不引依賴) Architect Q3
13 navigator.language fallback 基本 startsWith('zh') 強化:處理 C/POSIX/空字串 + hardcoded 英文 fallback Architect Q5
14 milestone 數量 M8-1 ~ M8-1010 個) +M8-4b 階段化啟動11 個) R5-E
15 總工時估算 ~10 人天 ~12 人天R5-E +1 天、R5-D1 +0.3 天、PM Q4 +0.2 天、Minor 4 +0.3 天 + 其他 0.2 天) 以上
16 新增檔案 v2/startup-pipeline.mdR5-E 實作細節) R5-E

未變更v2.0 其餘設計保持不動Wails 視窗 = 控制台 UI、瀏覽器 tab 載業務、yt-dlp/Mock 砍除、ffmpeg LGPL 方案 B、CORS 白名單、boot-id 機制、LogBuffer 2000 行 ring buffer、state machine 5 個狀態。

0.1 v1 → v2 差異速覽

面向 v12026-04-11 v22026-04-14 觸發決策
Wails 視窗載入內容 Splash → window.location.replace 跳到 Next.js 主 UIM7-B Splash 路徑完全移除Wails 永遠停在「控制台 UI」server 狀態 / log panel / 啟停控制 / Open in Browser R5-1 A+B+G
使用者業務 UI 承載者 Wails WebViewM7-B 是純 HTTP但仍在 WebView 內) 瀏覽器 tabChrome / Safari / Edgehttp://127.0.0.1:<port>/ R5-1
yt-dlp 全套 保留M6 vendor 35 MB + handler + 前端 URL tab 完全砍除vendor + handler + frontend UI + i18n + bootstrap + installer payload R5-7 前置M8-1
Mock 模式 保留(--mock flag + VISIONA_MOCK env + UI hint + 兩份 i18n 完全砍除Go mock driver / mock camera / env var / flag / UI 元件 / i18n keys R5-5a
ffmpeg 授權 GPLevermeet / BtbN / johnvansickleVISIONA_ALLOW_GPL_FFMPEG=1 release blocker LGPL 方案 B混合Win/Linux 換 BtbN LGPLmacOS 自 build decoder-only ~20 MBbinary commit 到 vendor/ffmpeg/macos/ R5-6 / R5-6a / R5-6b
ffprobe 未打包 三平台都一起包ffmpeg + ffprobe R5-6c
Tray 第三輪 Q-A 砍掉 維持砍,不復議 R5-3
關閉視窗行為 Q7=B 傳統式(關 = 結束 app 維持 Q7=B但新增關閉前先 StopServer() 優雅結束;瀏覽器 tab 偵測 server 離線後顯示全域 Offline Overlay R5-2
Server 控制 bindings 只有 GetServerStatus / GetServerURL / OpenBrowser(隱式 start 補齊 StartServer / StopServer / RestartServer / GetRecentLogs / ClearLogs / GetSystemInfo 及完整 state machine R5-1
watchServer 失敗行為 3 次失敗 → reportFatal + os.Exit(1)app 一起死) 3 次失敗 → 切 ServerStateErrorWails app 保留讓使用者手動 Restart 或查 log 三方共識 #10
自動開瀏覽器 首次 server 就緒自動開一次Settings 的 openBrowserOnStart 可關 R5-4
CORS 政策 寬鬆(Access-Control-Allow-Origin: <任意 Origin> 嚴格 whitelist http://127.0.0.1:* + http://localhost:*;其他 Origin → 不回 ACAO header / OPTIONS 405 三方共識 #5
綁定 interface --host 127.0.0.1 維持不動(不做 LAN R5-1
上傳影片副檔名 .mp4 / .avi / .mov .mp4 / .avi / .mov / .mpeg / .mpg(瀏覽器能吃 + Kneron pipeline 吃得到的交集) 三方共識 #11
Boot-ID 機制 新增 GET /api/system/boot-idserver 啟動時產生 UUID瀏覽器每 5 s pollboot-id 變更 → force reload 三方共識 #14
控制台 UI 技術選型 vanilla HTML/JS/CSS從現有 visiona-local/frontend/ splash 改寫 三方共識 #7
沿用率 85-95%(詳見 v2/code-reuse-v2.md 三方共識 #1
總工時 ~10 人天,拆成 10 個 milestone詳見 v2/milestone-plan.md 三方共識 #1

0.2 v1 未變的決策v2 繼續沿用)

  • 三層程序模型Wails 殼 + Go server 子行程 + Python sidecar— D1
  • Python runtime 雙策略bundled / system / auto— D2
  • 完全放棄程式碼簽章macOS ad-hoc、Windows 無 Authenticode、Linux 無簽章)— D3
  • x86_64 only三平台都不做 ARM — D4
  • 預置模型全部打包(~73 MB不做 auto-update、不收 telemetry — D5
  • 資料目錄位置、single-instance lock、舊資料目錄遷移、IPC raise 機制全部保留
  • 首次安裝 ≤ 5 分鐘、首次推論 ≤ 30 s / 回訪 15 s 等 NFR 目標不變
  • Ubuntu 與 Windows 打包流程AppImage / Inno Setup不變
  • 中英雙語(前端)機制不變,控制台 UI 使用同一份 en-US / zh-TW JSON詳見 v2/control-panel.md

1. 新架構總覽

1.1 三層進程模型

Wails 殼(桌面控制台 UI
  └─spawn─▶ Go server 子行程Gin HTTP + WebSocket on 127.0.0.1:random_port
              ├─spawn─▶ python3 kneron_bridge.pyKneronPLUS SDKsidecar
              └─spawn─▶ ffmpeg / ffprobeon-demand 解碼)

Wails 殼 ←── IPC / Wails bindings ── Wails 視窗內的控制台 UIvanilla HTML/JS/CSS
Go server ←── HTTP / WebSocket over loopback ── 瀏覽器 tabNext.js Web UI業務操作全在這裡

關鍵差別於 v1Wails 視窗載入業務 UI它是獨立的控制台status / log / start-stop / open-in-browser / preferences。業務 UI 在瀏覽器 tab 跑 http://127.0.0.1:<port>/ 的 Next.js SPA。

完整 ASCII 架構圖詳見:v2/control-panel.md §3控制台 UI wireframearchitect-analysis-round2-refactor.md §A1v1→v2 資料流對照)。

1.2 ServerController State Machine

五個狀態:Stopped / Starting / Running / Stopping / Error。轉換由 ServerController.txMu + mu 雙 mutex 保護,不可跳過中間狀態。

  • StartStopped|Error → Starting → Running(失敗走 Error
  • StopRunning → Stopping → Stopped
  • RestartRunning → Stopping → Stopped → Starting → Running
  • watchServer 3 次失敗Running → Errorv1 是 os.Exitv2 改為 Error state 讓使用者手動復原)

完整細節見 v2/server-lifecycle.md §5。

1.3 資料流摘要

情境 簡述
冷啟動 + R5-4 自動開瀏覽器 Wails OnStartup → 常規 seed / lock / IPC → ServerController.Start() → spawn server + logPump × 2 → 健康檢查 → state = Running → 若 openBrowserOnStart 且本 session 首次 → OpenInBrowser("")
Log 推送到控制台 server stdout/stderr → cmd.StdoutPipe/StderrPipelogPump goroutinebufio scanner + 10 ms micro-batch→ 同時 (1) 寫 logs/server.{stdout,stderr}.log + (2) append 到 LogBufferring 2000 行)+ (3) EventsEmit("log:append", []LogLine) → Wails JS 訂閱 EventsOn('log:append', ...) → log panel 增量 render
Restart 期間瀏覽器 tab 自動重連 使用者按 Restart → Stop → Start → server 新 boot-id → 瀏覽器 polling /api/system/boot-id5 s intervalPage Visibility API偵測到 id 變 → window.location.reload()
關 Wails 視窗 (R5-2) OnBeforeClose return false → OnShutdown → watchCancel → ServerController.Stop()SIGTERM → 10 s → SIGKILL→ releaseLock → Wails quit。瀏覽器 tab 的 polling 連續 3 次失敗15 s<ServerOfflineOverlay> 顯示「Server 已離線」

詳細時序與 Go 實作見 v2/server-lifecycle.md §2-9瀏覽器端實作見 v2/web-ui-offline-overlay.md


2. 子檔案地圖

# 子檔 目的 對應 R5 決策 / M8 milestone
2.1 v2/control-panel.md Wails 控制台 UI + Go App bindings + LogBuffer + log pump + 狀態機 + PreferencesR5-D2/D3+ OS 通知觸發點 R5-1, R5-5, R5-D1/D2/D3, R5-EM8-4, M8-4b, M8-5
2.2 v2/ffmpeg-lgpl.md 三平台 LGPL ffmpeg vendor 策略Makefile patch + macOS build script + 授權檔管理) R5-6, R5-6a, R5-6b, R5-6cM8-3
2.3 v2/server-lifecycle.md Server 生命週期細節state machine、port 分配F-2 強制保留、pipe 捕捉、7+1 秒 graceful shutdown、boot-id、OS 通知§10、Preferences 持久化§11 R5-2, R5-4, R5-D1, PM Q4, F-2, PM §11-1/11-3M8-4, M8-9
2.4 v2/cors-security.md CORS whitelist middleware、WS origin check、資料驗證邊界 三方共識 #5M8-8
2.5 v2/deletions.md yt-dlp / Mock 模式全清單(檔案 / 函式 / 行號 / i18n key / vendor / installerv2.1 修正 videoIsURL / NewVideoSourceFromURL 明確刪除 R5-5a, R5-7, PM Minor 5M8-1, M8-2
2.6 v2/web-ui-offline-overlay.md 瀏覽器 tab 的 <ServerOfflineOverlay> 實作polling + WebSocket shutdown-imminent 雙管道、重試、SSR 相容 R5-2 三方共識 #14, PM Minor 4M8-7
2.7 v2/milestone-plan.md M8-1 ~ M8-10 + M8-4b 階段化啟動;總工時 ~12 人天 整體M8-*
2.8 v2/code-reuse-v2.md 逐模組沿用 / 改寫 / 新寫比例表 整體
2.9 v2/startup-pipeline.md v2.1 新增R5-E 6 階段啟動管線實作event schema、StartupPipeline struct、watcher goroutine、60 s hard timeout / 20 s soft timeout、前端 startup-panel.js R5-E1~E6M8-4b

3. 風險清單v2 更新)

繼承 v1 risks-and-mitigations.md 全部風險,以下為 v2 新增或升級的條目:

# 風險 等級 新增/升級 緩解
R-v2-1 M7-B M1 驗收漏看 Wails 視窗 的教訓 — v2 控制台是全新 UI重複踩同樣坑的風險 🟠 新增 M8-10 驗收 checklist 強制三個檢查:(1) 雙擊 .app / .exe / .AppImage 打開後 Wails 視窗顯示的是控制台 UI不是 splash / wizard / 白畫面);(2) 點 Open in Browser 後瀏覽器確實載入 Next.js(3) 點 Stop 後瀏覽器 tab 能看到 Offline Overlay。每個平台都要做
R-v2-2 macOS 自 build ffmpeg 的可重現性 — LGPL 合規稽核時必須能證明 vendor/ffmpeg/macos/ffmpeg 是我們在特定 configure flags 下從特定 ffmpeg commit build 出來的 🔴 新增 vendor/ffmpeg/macos/BUILD.md 必須記錄ffmpeg release tagn7.1、source tarball sha256、完整 ./configure line、build hostmacOS version + Xcode CLT version、build date、binary sha256。未來升級 ffmpeg 時要重跑並更新 BUILD.md不能「手改一下再傳」。同步把 configure flags 寫進 Makefilevendor-ffmpeg-macos-build target而非埋在 BUILD.md 內)讓其可 reproduce
R-v2-3 Wails EventsEmit 在高頻 stdout 下丟事件或延遲 — server boot 時 Gin / logger 一次可能噴 200+ 行;推論 frame log 若誤進 stdout 會產生秒級 30-100 行 🟠 新增v1 F-4 升級) (1) logPump 加 micro-batch緩存 10 ms window 內的行,一次 emit 一個 log:append eventpayload 為陣列);(2) server 推論 frame 狀態禁止用 logger.Info改用 debug levelline-rate 測試會檢查此條);(3) 若 LogBuffer 滿 > 80% 時 logPump 降為只寫檔不 emit event控制台看到「…skipped N events…」提示使用者可 Clear Logs。實作細節見 v2/control-panel.md §4
R-v2-4 Wails 關閉視窗 → StopServer 過程中瀏覽器 tab 的 race condition — 使用者按 × → Wails OnBeforeClose → ServerController.Stop() → SIGTERM → wait → SIGKILL → Wails 退出,整段 ~0.5-5s 內瀏覽器 tab 的 polling 可能看到 ECONNREFUSED 但 Overlay 還沒觸發(需連續 3 次失敗15 s 才顯示) 🟡 新增 實務上:使用者關了 Wails 視窗通常也會關瀏覽器 tabrace 不構成實際問題。若使用者真的沒關瀏覽器15 s 後 Overlay 會出現使用者看到「Server 已離線」訊息即可理解。不做額外優化Wails 關閉前主動告知瀏覽器 — 需要 Wails → Browser 的 push channel成本太高
R-v2-5 macOS 自 build 的 ffmpeg 需要 codesign — Gatekeeper 會擋未簽章的執行檔 🟠 新增 (1) 在 macOS build script 最後做 codesign --force --sign - ...ad-hoc sign(2) wails-macos target 的 codesign --force --deep --sign - visiona-local.app 已覆蓋 Resources/bin 下的執行檔,沿用即可;(3) 驗收時用 spctl --assess --verbose vendor/ffmpeg/macos/ffmpeg 確認不會被 Gatekeeper 拒絕
R-v2-6 boot-id polling 對瀏覽器 tab 休眠的影響 — Chrome 會把背景 tab 的 setInterval 降頻到 1 次/分鐘,可能讓使用者切回 tab 時 60 s 才偵測到 server 重啟 🟡 新增 用 Page Visibility APItab visible → 5 s intervaltab hidden → 停 pollingtab 再次 visible → 立即 probe 一次再恢復 5 s interval。實作細節見 v2/web-ui-offline-overlay.md §3
R-v2-7 砍 Mock 後空白 UI 體驗 — 使用者第一次打開沒插硬體時Devices 頁會是空的 🟡 新增 (1) Devices 頁顯示友善 empty state「未偵測到 Kneron 裝置。請連接 KL520/KL720 後按『掃描』。」附安裝 driver 按鈕Windows(2) 這是 R5-5a 明示接受的結果PRD v2 也會記錄為預期行為

v1 已列、v2 解除的風險:

  • ffmpeg GPL release blockerF-5, R9 — R5-6 LGPL 方案 B 解除
  • F-1 Wails tray 在 Linux GNOME 可能壞掉 — R5-3 維持砍 tray風險消失
  • F-3 使用者找不回 app — R5-2 維持關閉 = 結束,風險消失

4. 審閱紀錄

日期 審閱者 結論
2026-04-14 Architect Agent v2.0 Draft 產出
2026-04-14 PM Agent v2.0 互審完成(見 reviews/pm-review-of-tdd-v2.md)— Major × 4 / Minor × 5
2026-04-14 Architect Agent v2.1 產出:吸收 PM Major/Minor 修正 + R5-D 三條補充決策 + R5-E 階段化啟動 + Architect 自補清單F-2 port 保留 / B-1 OS 通知 / Q1/Q3/Q5/Q7 自決)。新增 v2/startup-pipeline.md;總工時 ~10 → ~12 人天
2026-04-14 PM Agent v2.1 待再次審閱(確認 Major × 4 都已落地 + R5-D/E 理解一致)
2026-04-14 Design Agent v2.1 待審重點R5-E5 啟動階段文案、7+1 秒 stopping modal 文案、startup-panel.js 視覺對齊 Design Spec v2.1
2026-04-14 使用者 待確認