背景: 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>
802 lines
53 KiB
Markdown
802 lines
53 KiB
Markdown
# 專案進度 — 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 15(S 級 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 對不上 → 就算有 detection,bbox 位置會嚴重偏位甚至跑出 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 timeout(Loader 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`,是就立刻 report(cover 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-off:KL520 connect 時間從 ~2s 變 ~15-20s(macOS),Windows 可能 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 letterbox、numpy→bytes、明確傳 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 路徑(拔插 USB):skip reset 後推論 11 個 bbox 正確
|
||
- [x] Windows 實測首次 connect:106s 成功(< 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
|
||
|
||
**驗證兩條路徑都 OK(2026-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)。走方案 C(create-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 plain(CI 無痛)
|
||
- `dmg-fancy`:強制美化版(需 `brew install create-dmg`)
|
||
- `dmg-plain`:原本的 hdiutil UDZO(保留為 fallback)
|
||
- Windows / Linux 流程零改動
|
||
|
||
驗證:
|
||
- `brew install create-dmg` 成功
|
||
- `make dmg-fancy` 產出 157MB DMG,mount 後內容: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 tab),dmg 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 binary,macOS 自 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 走 LGPL,GPL 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.md,500 行,卡在上限)
|
||
- ✅ 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-Q1:20 秒 retry hint 文案「正在重試…」vs「正在處理中…」(Design 建議前者)
|
||
- D-Q2:WebSocket 被安全軟體擋的提示(Design 建議不做特殊偵測)
|
||
- D-Q3:Retry 按鈕語意「重置整個啟動」vs「重試當前階段」(Design 建議重置,需 Architect 確認 RestartStartupSequence 可行)
|
||
|
||
**Architect 新增(5 題)**:
|
||
- A-Q1:階段 6 WebSocket 首次連線實作方式 long-poll endpoint vs sentinel file(交 M8-4b 執行者)
|
||
- A-Q2:watcher goroutine 和使用者在 Starting 中按 Stop 的 race(action bar 禁用,M8-4b 實測)
|
||
- A-Q3:shutdownGracePeriod 7s/6s 對齊若實測常被 SIGKILL 則改 9+1 秒
|
||
- A-Q4:Linux notify-send 不存在時的 fallback(M8-10 實測 Ubuntu minimal)
|
||
- A-Q5:N-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 error(60s timeout 或階段失敗)→ 按鈕「**Retry**」= 呼叫 `RestartStartupSequence()` 重置整個啟動流程
|
||
- Running 階段 watchServer 失敗 → 按鈕「**Restart Server**」= 重 spawn server(既有行為)
|
||
- **D-Q3 RestartStartupSequence 可行性**:✅ 可行,新增 function(5 步驟實作細節已定)
|
||
- 停 watcher → ForceKill server → 重置 state machine → 重建 pipeline → 重跑 Start
|
||
- 階段 1 直接 Complete 不重跑
|
||
- sentinel file 必須先清
|
||
- Retry 情境下 port 允許 fallback(cold 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/smoke,0 誤刪 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 test,HasFailedStage / 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 helper,15 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 版 ~280MB),LGPL 驗證通過,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 215MB,codesign 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=24292(Next.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.json(user 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 三平台都同樣這個 bug(server/main.go 是共用的)。
|
||
|
||
**採方案 B(使用者批准)+ 額外職責拆分**(2026-04-15)
|
||
|
||
實作:`server/main.go`
|
||
- 新增 `resolveBuiltInDataDir(base)` — 照 `resolveBridgeScript` 同款風格,依序試 `<base>/data` → `<base>/../data` → `<base>/../Resources/data`,**以 `models.json` 存在為命中條件**
|
||
- `main()` 拆出兩個獨立變數:
|
||
- `builtInDataDir`(read-only,bundle 內)— 給 `model.NewRepository(filepath.Join(builtInDataDir, "models.json"))` 與 `flash.NewService(deviceMgr, modelRepo, builtInDataDir)` 使用(因 flash 也要解析 model.filePath 相對路徑 `"data/nef/..."`)
|
||
- `dataDir`(writable,user 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-15):Major + 所有 Minor + 兩個 Suggestion 一次全部處理
|
||
|
||
- 新增 `findFirstExisting(candidates, sentinel) (dir, tried)` helper(s-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)
|
||
|
||
### 第二輪 Review(2026-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-1:resolve 函式用 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/mpg(M8-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 Major(2026-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 仍 10s,TDD §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 建議分三個 commit(M8-1 / M8-2 / M8-3),或一個合併。使用者從未要求 commit,保守做法是先不 commit 等使用者說。
|
||
|
||
### R5-Design 補充決策(2026-04-14,Design 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 1:Design settings-update.md §2.2 誤稱「走 Wails 既有 settings store」(Wails v2 無此機制)+ 檔名不一致 → 採 TDD 的 `preferences.json @ <dataDir>/`
|
||
- Major 2:R5-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 fallback:zh* → 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 說 12,PM 說 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 state(Architect 自補)
|
||
- **關鍵發現 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 更新進度面板
|
||
- 新增啟動進度面板 UI(Design 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 全部 200:health、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 含完整主 UI(21KB 首頁 + 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 blocker:ffmpeg 授權
|
||
- 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 上跑通 vendor(317MB) |
|
||
|
||
### 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+ps;Windows 留 TODO |
|
||
|
||
## M7 — Windows 實機 build + splash regression 修復(2026-04-12)
|
||
|
||
### M7-A:Windows 一鍵 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.sh(apt + go 1.22.5 + node 20 + pnpm + wails) | ✅ 未在 Ubuntu 實機驗證 |
|
||
| M7-A3 | bootstrap-windows.ps1(winget 安裝 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` → 極簡 splash(logo + 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 產出正式 PRD(2026-04-11)
|
||
- [x] Design Agent 產出正式設計規格(2026-04-11)
|
||
- [x] Architect Agent 產出正式 Design Doc + TDD(2026-04-11)
|
||
- [x] 第三輪使用者決策(砍 tray、B4、C2、D2、E1/E2/E3)
|
||
- [x] Design Agent 依第三輪決策修訂設計規格(2026-04-11)
|
||
- [x] PM Agent 依第三輪決策修訂 PRD(2026-04-11)
|
||
- [x] Architect Agent 依第三輪決策修訂架構文件(2026-04-11)
|
||
- [x] bootstrap-linux.sh + bootstrap-windows.ps1(2026-04-12)
|
||
- [x] Windows 一鍵 build 踩坑全清:11 項修好 push(2026-04-12)
|
||
- [x] Windows `dist/visiona-local-0.1.0-windows-x64.exe` 成功產出(2026-04-12)
|
||
- [x] M7-B splash regression 修復(commit 570e040,2026-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 授權 | **繼續內嵌**(不主動問 Kneron,B4 延續,發佈前 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 內 toast;Server 崩潰 → 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**(M6):macOS 上的 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`
|