local-tool/: visionA-local desktop app
- M1: Wails shell + Go server + Next.js UI + Mock mode (macOS dmg ready)
- M2: i18n (zh-TW/en) + Settings 4-tab refactor
- M3: Embedded Python 3.12 runtime (python-build-standalone) + KneronPLUS wheels
- M4: Windows Inno Setup script (build on Windows runner)
- M5: Linux AppImage script + udev rule (build on Linux runner)
- M6: ffmpeg (GPL, pending legal review) + yt-dlp bundled
- Lifecycle: watchServer health check, fatal native dialog,
Wails IPC raise endpoint, stale process cleanup
.autoflow/: full PRD / Design Spec / Architecture / Testing docs
(4 rounds tri-party discussion + cross review)
.github/workflows/: macOS / Windows / Linux build CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
153 lines
14 KiB
Markdown
153 lines
14 KiB
Markdown
# Design 交叉審閱報告
|
||
|
||
> 審閱者:Design Agent|日期:2026-04-11(第三輪文件完成後)
|
||
> 審閱對象:PM PRD(`02-prd/`)、Architect TDD + Design Doc(`04-architecture/`)
|
||
> 立場:從 UX 角度檢視產品需求與技術方案是否會衝擊使用者體驗
|
||
|
||
---
|
||
|
||
## ✅ 對齊的項目
|
||
|
||
1. **三方都確認砍掉 tray**:PRD §3 非目標、TDD §8(`tray-and-lifecycle.md`)、Design §5 一致。
|
||
2. **macOS 資料目錄統一為 `~/Library/Application Support/visiona-local/`**(Q-E1):PRD `user-flows.md`、Architect `architecture-overview.md §4.2`、Design `04-first-run.md §4.1` 路徑一致。
|
||
3. **Workspace 升 sidebar 一級**(Q-E2):PRD feature-inventory、Design IA、Architect code-reuse-plan 三處都標註。
|
||
4. **Mock 模式預設關閉、需明確選擇**:PRD AC-2.1、Design `04-first-run.md §4.4`、Architect `deviceMgr.mockMode` flag 一致。
|
||
5. **Settings 4 分頁結構**(Q-E3):PRD、Design 一致採「一般/硬體/模型/進階」。
|
||
6. **首次啟動體驗可跳過**:PRD `user-flows.md §5.2`、Design `04-first-run.md §4.2` 都明確三步皆可略過。
|
||
|
||
---
|
||
|
||
## ⚠️ 發現的問題
|
||
|
||
### 對 PM 的問題
|
||
|
||
- **D→PM-01 [🟡]** **「首次推論 ≤ 15 秒」的 AC 與 First-Run 三步流程衝突。**
|
||
PRD `nonfunctional.md §6.1` 的驗收值寫「app 啟動 → Mock 第一幀 ≤ 15 秒」,但 Design `04-first-run.md` 的首次流程是**歡迎 → 模式選擇 → 硬體偵測(真實模式時要掃 10 秒)**,在真實模式下光硬體掃描 timeout 就 10 秒,不可能 15 秒到第一幀。建議:把 AC 拆成「首次(含 First-Run)≤ 30 秒」與「回訪(跳過 First-Run)≤ 15 秒」兩個指標,否則測試會一律 fail。
|
||
|
||
- **D→PM-02 [🟡]** **`user-flows.md §5.3.1` 步驟 3「啟動 Go server < 2 秒」與 TDD `tray-and-lifecycle.md §4.1` 的 `waitHealthy(10s)` 不一致。**
|
||
PM 寫「< 2 秒」,Architect 給自己留了 10 秒 timeout。Design 需要知道真實值才能決定 First-Run 是否要插 skeleton/splash。建議 PM 與 Architect 對齊,Design 才能設計正確的 loading 體驗。
|
||
|
||
- **D→PM-03 [🟢]** **Error flow `5.4.1` Port 3721 被佔用的文案與 Architect 不符。**
|
||
PRD 寫「請關閉占用該埠的程式,或在 Settings 變更 port」,但 TDD `tray-and-lifecycle.md §3.2` 的 `pickPort()` 會**自動挑 3722、3723...**,根本不需要使用者介入。兩邊敘述衝突,Design `08-states.md` 我暫時沿用 PRD 版,但若採 Architect 自動挑 port,應改成 info toast「已改用 port 3722」。
|
||
|
||
- **D→PM-04 [🟢]** **`nonfunctional.md §6.8.3` 寫「不做額外無障礙驗證」,與 Design `09-accessibility.md` WCAG 2.2 AA 承諾衝突。**
|
||
Design 規格第三輪明確寫 WCAG 2.2 AA、鍵盤 tab 順序、ARIA label。建議 PRD 在非功能需求同步把 a11y 拉到「必達」等級,或 Design 降級;兩邊必須一致。
|
||
|
||
- **D→PM-05 [🟢]** **Non-goals 文字微矛盾。** PRD `feature-inventory.md` 寫「原生 menu bar(File → New Device / Upload Model)」,但 `vision-and-non-goals.md` 暗示「不做 quick action」。menu bar 其實就是 quick action 的一種,建議 PM 把「不做 tray」與「有 menu bar」的界線在 non-goals 裡寫清楚,避免未來有人誤解成「連 menu bar 都不能做」。
|
||
|
||
### 對 Architect 的問題
|
||
|
||
- **D→Arch-01 [🔴]** **完整冷啟時間沒有明確預算,影響 First-Run AC。**
|
||
Architect `TDD.md §5` 只寫「冷啟 < 5 秒」,但三層程序(Wails → Go server → 首次 scan 才 spawn Python sidecar)的疊加時間沒拆開。Design 需要知道:
|
||
- Wails WebView 載入 Next.js:? 秒
|
||
- Go server `waitHealthy`:最多 10 秒
|
||
- Python sidecar 首次 spawn + KneronPLUS import:? 秒(實測一下,KneronPLUS 通常要 2-3 秒)
|
||
|
||
若總和超過 5 秒,Design `04-first-run.md §4.5 Phase 1` 的「掃描 USB 進度條」需要改成「**準備硬體子系統中...**」以免使用者誤以為 USB 掃描很慢。**請 Architect 在 M1 後補上實測數字。**
|
||
|
||
- **D→Arch-02 [🔴]** **Port picking 導致使用者書籤/歷史的 URL 不穩。**
|
||
`tray-and-lifecycle.md §3.2` 決定「3721 被佔 → 往上挑 3722、3723」,但 WebView 是 `http://127.0.0.1:{port}/`。使用者**不會**直接開瀏覽器書籤 localhost(Wails 殼包住 WebView),這對一般使用者 OK,但對「進階使用者想用 Chrome DevTools 連」「Settings 要顯示實際 port」的情境要設計出口。
|
||
**Design 建議**:在 Settings > 進階新增唯讀欄位「目前 Server Port:3722」+「複製」按鈕;Dashboard Server Status 卡片也顯示實際 port。TDD 目前只寫「Server logs 印 warning」不夠。
|
||
|
||
- **D→Arch-03 [🟡]** **Single-instance 第二次雙擊的 UX 未定義。**
|
||
`tray-and-lifecycle.md §2.3` 說收到 `/ipc/raise` 會 `WindowShow(ctx)` 浮到前面。Design 需要確認:
|
||
1. 第二個程序啟動是**完全靜默**(只 raise,沒有任何提示)還是**顯示 toast**(「已有另一個 visionA-local 在執行中」)?
|
||
2. macOS 從 Dock click 的行為由 Cocoa 處理,但 Linux AppImage 每次都是新程序,體驗不一致——Design 建議三平台統一走「靜默 raise + 可選 toast」。
|
||
3. 如果 `raiseExistingInstance()` 失敗(例如 IPC port 讀不到),目前邏輯是覆寫 lock、視為新程序——這會造成使用者看到「兩個視窗」,**非常糟糕**。建議改為「失敗即顯示錯誤 modal + 退出」。
|
||
|
||
- **D→Arch-04 [🟡]** **Python sidecar crash 的 UI 呈現缺失。**
|
||
`tray-and-lifecycle.md §4.3` 寫「Python sidecar 空閒 N 分鐘自動 kill、崩潰時 Go server 自動重啟最多 3 次」。Design 需要知道:
|
||
1. 重啟那 1-2 秒使用者會看到什麼?目前的推論畫面會卡住/白屏?
|
||
2. 3 次失敗後呈現的錯誤頁需要內容(Design `08-states.md` 只有通用 Critical error template,沒有 sidecar-specific)。
|
||
3. 自動 kill(閒置省記憶體)後下一次使用者點推論,Design 需要顯示「正在重新啟動推論引擎...」spinner,否則 Start 按鈕按下後會有 2-3 秒無回應。
|
||
**建議 Architect 新增事件 `POST /ws/server-logs` 類型 `sidecar.state` 讓前端訂閱**,Design 才能做對應 UI。
|
||
|
||
- **D→Arch-05 [🟡]** **Python 雙策略(A 內嵌 / B 系統)切換沒有 UI 入口。**
|
||
`dependency-bundling.md §1.5` 寫可透過 `--python-mode=bundled|system` CLI flag 切換,但使用者不會跑 CLI。Design 規格 Settings > 進階 應該有「Python 執行模式:內嵌(目前)/ 系統/ 自動」的唯讀顯示 + 重設按鈕。**Design 這邊會補 wireframe,但需要 Architect 確認前端能讀 `.installed` meta 的 API(目前 `/api/system/deps` 是否涵蓋?)。**
|
||
|
||
- **D→Arch-06 [🟡]** **資料目錄遷移/升級情境未處理。**
|
||
Architect 第三輪把 macOS 資料目錄從 `~/.visiona-local/` 改到 `~/Library/Application Support/visiona-local/`(Q-E1),但 **`edge-ai-platform` 舊使用者**可能已有 `~/.visiona-local/` 的資料。雖然 visionA-local 是新專案沒有「升級」概念,但**如果使用者曾經裝過 edge-ai-platform 的 local 模式**(有些人可能有),啟動時要不要偵測舊路徑、詢問是否遷移?Design 建議:**M1 範圍內不處理,但 First-Run 增加一段說明「若你之前用過舊版 visionA,請手動搬移 `~/.visiona-local/` 到新路徑」**。Architect 是否同意不自動遷移?
|
||
|
||
- **D→Arch-07 [🟢]** **`watchServer()` 健康檢查頻率 10 秒對 UX 太慢。**
|
||
`tray-and-lifecycle.md §4.2` 寫每 10 秒 healthcheck、失敗 3 次才判定掛掉,代表使用者最糟要等 **30 秒**才看到錯誤。建議縮短為「每 3 秒、失敗 2 次」= 6 秒內可感知。
|
||
|
||
- **D→Arch-08 [🟢]** **macOS 第二個程序的 `⌘N`「新增視窗」語意未定義。**
|
||
砍掉 tray 後,`feature-inventory.md §4.5` 提到 menu bar 有「File → New Device」。但 macOS 慣例 File menu 常有「New Window (`⌘N`)」——我們是單例 app,這個快捷鍵要完全拿掉還是改成「新增裝置」?Design 建議改成「New Device (`⌘N`)」避免衝突。
|
||
|
||
---
|
||
|
||
## 📋 R9 Plan B 的 First-Run 下載流程草案
|
||
|
||
若 Kneron 不允許 re-distribution,預置 `.nef` 無法內嵌,首次啟動必須線上下載 ~73MB 模型。Design 設計以下 fallback UX:
|
||
|
||
**流程修改:** 在現有 First-Run Step 2(模式選擇)與 Step 3(硬體偵測)之間**插入一個新步驟 2.5「下載預置模型」**。
|
||
|
||
```
|
||
Step 1 歡迎 → Step 2 模式選擇 → [NEW] Step 2.5 下載模型 → Step 3 硬體偵測 → Dashboard
|
||
```
|
||
|
||
**Step 2.5 Wireframe:**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ [略過 →] │
|
||
│ │
|
||
│ 下載預置 AI 模型 │
|
||
│ 首次使用需要下載約 73 MB 的範例模型 │
|
||
│ │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ 📦 fd_mask.nef 14 MB ✓ │ │
|
||
│ │ 📦 yolov5s.nef 18 MB ⟳ │ │
|
||
│ │ 📦 resnet50.nef 22 MB … │ │
|
||
│ │ 📦 mobilenet.nef 8 MB … │ │
|
||
│ │ 📦 tiny_yolo.nef 11 MB … │ │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ 整體進度:[■■■■□□□□□□] 40% (29 MB / 73 MB) │
|
||
│ 預估剩餘:45 秒 │
|
||
│ │
|
||
│ 來源:Innovedus 內部 CDN │
|
||
│ │
|
||
│ ⓘ 若要完全離線使用,請聯絡 IT 取得完整安裝包 │
|
||
│ │
|
||
│ [取消下載] [暫停] │
|
||
│ │
|
||
│ 2.5 / 3 │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**互動規則:**
|
||
1. **離線偵測**:Wails app 啟動時先 ping Innovedus 內部 CDN(HEAD 請求,5 秒 timeout)。無法連線 → 顯示「離線提示卡」:「目前無法連線到模型伺服器。你可以 (A) 稍後重試 (B) 改用 Mock 模式 (C) 手動放置 .nef 到 `<APPDATA>/data/nef/`」。
|
||
2. **斷線續傳**:每個模型用獨立 HTTP request,失敗可單獨重試,不重新下載已完成的。
|
||
3. **跳過**:「略過」→ 直接進 Step 3,但 Models 頁會顯示空狀態「還沒有預置模型,請到 Settings > 進階 下載」。
|
||
4. **完成條件**:至少 1 個模型下載成功即可繼續;0 個則強制退回 Step 2 選 Mock。
|
||
5. **Settings 入口**:Settings > 進階 新增「重新下載預置模型」按鈕,使用者可隨時補。
|
||
|
||
**連帶影響(給 PM):** PRD §6.4「離線可用」承諾必須加註解:「**例外**:若 R9 Plan B 觸發,首次啟動需一次性下載 ~73 MB 預置模型。後續使用仍完全離線。」
|
||
|
||
**Design 估計**:Plan B 的 First-Run UI 工作量約 0.5 人週(只新增 1 個 step + 1 個 Settings 按鈕 + 文案)。
|
||
|
||
---
|
||
|
||
## ❓ 需要使用者或三方討論的問題
|
||
|
||
1. **D→PM-01 的 AC 拆分**:使用者想要「15 秒」適用於所有情境,還是接受「首次 30 秒 / 回訪 15 秒」?
|
||
2. **D→Arch-02 的實際 port 顯示位置**:Settings > 進階?還是 Dashboard Server 狀態卡?
|
||
3. **D→Arch-06 舊專案資料目錄遷移**:要不要自動偵測 `~/.visiona-local/` 並提示使用者?
|
||
4. **R9 Plan B 觸發時**,內部 CDN 是否可用?PM 需要與 Innovedus IT 先確認,避免 Plan B 時再花時間找 host。
|
||
5. **macOS `⌘N` 快捷鍵**(D→Arch-08):要綁什麼動作?
|
||
|
||
---
|
||
|
||
## 結論
|
||
|
||
**整體結論:三方文件一致性高,砍 tray、Workspace 升一級、Settings 4 分頁、macOS 資料路徑這幾個第三輪決策都有落實到各自的文件裡。**
|
||
|
||
主要風險在於**三層程序的啟動時間沒有被端到端量化**(D→Arch-01),這會直接影響到 First-Run 與 NFR §6.1 的 AC 能不能達成,建議 Architect 在 M1 結束時就實測並回填。
|
||
|
||
次要問題是一些**邊界情境的 UX 未被 Architect 顯式處理**:port picking 的 UI 呈現、second-instance 的提示、sidecar crash/auto-kill 的 loading 狀態、Python 雙策略的切換入口。這些不是 blocker 但會影響「順手、穩定」的使用體驗,建議 Architect 在 M3 前補齊 API 事件(`sidecar.state`、實際 port 查詢)讓 Design 能接著做 UI。
|
||
|
||
R9 Plan B 的 First-Run 下載流程已草擬,Design 工作量可控(~0.5 人週),但**內部 CDN 可用性需要 PM 先確認**,避免真的觸發 Plan B 時措手不及。
|
||
|
||
**Design Agent 對 PRD 與 TDD 第三輪版的整體結論:原則上可進入實作階段,但上述 🔴 問題 2 條(D→Arch-01、D→Arch-02)建議在 M1 開始前先補,避免後續返工。其餘 🟡 🟢 可在 M2-M3 迭代解決。**
|