jim800121chen d7cddf364b fix(local-tool): 拆 built-in / user data dir — 修預設模型永遠載入 0 個
M8-10a smoke test 抓到 M1 就潛藏的 P0 latent bug:server 預設 dataDir =
<binary>/data,bundle 內解析成 Contents/Resources/bin/data/(空目錄),
實際 models.json + 8 個 .nef 住在 Contents/Resources/data/(上一層)。
Wails 又傳 --data-dir 成 user home(writable),同樣沒 models.json。
結果任何正式啟動路徑下 /api/models 都回 total: 0,M1-M7 smoke 從沒
跑過這個 endpoint 才漏抓。

修法:把「read-only bundle 內資料」和「writable user 資料」語意拆開。

- 新增 findFirstExisting(candidates, sentinel) helper
- 新增 resolveBuiltInDataDir:①VISIONA_BUNDLE_LIB_DIR/data(AppImage)
  ②<base>/data ③<base>/../data ④<base>/../Resources/data
  ⑤<base>/../lib/visiona-local/data(Linux FHS)。命中條件是
  models.json 存在為 regular file,避開 Wails build artifact 留下的
  空 data/ 目錄
- main() 拆 builtInDataDir(modelRepo + flash.Service 用)與 dataDir
  (custom-models / sentinel / logs 用),職責分明
- flash.NewService 改吃 builtInDataDir — 它要解析 models.json 裡的
  相對路徑 "data/nef/kl520/xxx.nef",來源是 bundle 不是 user home
- resolveBridgeScript 同步修(同樣的技術債一起清),候選 env var 優先
  + FHS fallback,避免 Linux AppImage 上 kneron_bridge.py 也找不到
- fallback 全 filepath.Abs 化,log.Printf 印嘗試過的路徑清單便於除錯

驗證(build / vet / test + smoke)全綠:
- macOS bundle:/api/models → 15 models 
- dev mode(server/ 下 go run):15 models 
- Linux AppImage 模擬 + env var:命中候選 1 
- Linux AppImage 模擬 + 無 env var:命中候選 5(FHS)
- 全不命中:log 印完整 tried 清單 + server 不 crash 

Reviewer 兩輪通過。第一輪抓到 Linux AppImage 未覆蓋(Major-1)+ 2 Minor
+ 2 Suggestion;第二輪確認全部處理到位、新發現兩項非阻擋風格建議列為
技術債。報告見 .autoflow/05-implementation/reviews/review-m8-10a-*.md。

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

672 lines
45 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 版本)
## 當前階段:🔴 **第一階段回溯** — L 級重大方向變更Wails 內嵌 → Wails 控制台 + 瀏覽器 Web UI
## 當前狀態:✅ 使用者決策全部收齊R5 第五輪決策),待三方產出正式 PRD v2 / Design Spec v2 / TDD v2
## 最後更新2026-04-14
## 🔴 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`