jim800121chen c54f16fca0 Initial commit: visionA monorepo with local-tool subproject
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>
2026-04-11 22:10:38 +08:00

12 KiB
Raw Permalink Blame History

M1 End-to-End Verification Report

測試環境

  • macOS: 14.7.6 (BuildVersion 23H626)
  • 架構: x86_64
  • 檔案系統: APFScase-insensitive預設
  • 日期: 2026-04-11
  • 測試方式: 複製 .app/tmp/ 模擬全新機器,清空 ~/Library/Application Support/visiona-local

結論TL;DR

M1 未通過。 .dmg 本身可正常 mount、.app bundle 結構正確、codesign 驗證通過,但應用程式一啟動就失敗。發現 2 個 P0 阻斷 bug

  1. 資料目錄遷移邏輯在 case-insensitive APFS 上壞掉 — 預設 Mac 根本啟動不了。
  2. Wails 端呼叫 server binary 時用了不存在的 flag --python — 就算繞過 bug 1server 子行程也會立刻 crash。

Mock 模式無法在全新 Mac 上運作,瀏覽器無法看到主 UIM1 的核心承諾全部跳票。


前置清理

啟動前發現系統上 legacy edge-ai-server 持續被 launchd 重啟(~/Library/LaunchAgents/com.innovedus.edge-ai-server.plist),已執行:

launchctl unload ~/Library/LaunchAgents/com.innovedus.edge-ai-server.plist
pkill -9 -f edge-ai-server

之後 port 3721 淨空。注意:這是測試環境前置清理,不是 M1 驗收的一部分。但這提醒:使用者第一次安裝若曾裝過 edge-ai-platform升級 visiona-local 可能 port 衝突。


驗證步驟結果

Step 1: .dmg 檔案

  • 路徑:/Users/jimchen/visionA/local-tool/dist/visiona-local.dmg
  • 大小70 MB
  • 檔案類型:zlib compressed data

Step 2: Mount

hdiutil attach 成功,檢查碼全部通過:

  • 掛載點:/Volumes/visionA-local
  • 可見 visiona-local.app

Step 3: .app 結構

  • Contents/Info.plist
  • Contents/MacOS/visiona-local9.8 MBMach-O 64-bit x86_64
  • Contents/Resources/bin/visiona-local-server30 MB
  • Contents/Resources/data/models.jsondata/nef/
  • Contents/Resources/iconfile.icnsscripts/
  • codesign --verify --verbosevalid on disk, satisfies Designated Requirement

Step 4: 全新機器模擬

.app 複製到 /tmp/visiona-local.appDMG 已 detach。

Step 5: 資料目錄清理

確認 ~/Library/Application Support/ 下無任何 vision* 殘留。

Step 6 + 7: 啟動 → Server 起來

第一次啟動(走正常流程)

$ /tmp/visiona-local.app/Contents/MacOS/visiona-local
[visiona-local] 遷移 /Users/jimchen/Library/Application Support/visionA-local
               → /Users/jimchen/Library/Application Support/visiona-local 失敗:
               rename ...: no such file or directory
visiona-local already running

Wails app 直接 exit(0)。 沒有 server、沒有 UI、什麼都沒有。

根因分析P0 Bug #1 visiona-local/app.go:572 migrateOldDataDirs 的邏輯在 case-insensitive APFS 上會自我毀滅。

實際流程:

  1. startup()MkdirAll(newDir) 建立 visiona-local/
  2. migrateOldDataDirs 迭代舊候選路徑,其中包含 visionA-local(大寫 A
  3. 在 case-insensitive APFS 上,os.Stat(visionA-local) 會 hit 剛建立的 visiona-local(同 inode所以 Stat 成功。
  4. 程式碼接著檢查 newDir 是否存在(成功)、是否為空(剛建出來的,當然是空)→ 執行 os.Remove(newDir)
  5. os.Remove(newDir) 把那個目錄刪了,但在 case-insensitive FS 上這等於同時刪掉 visionA-local(同 inode
  6. os.Rename(visionA-local → newDir) → 來源不存在 → ENOENT
  7. 更致命newDir 已經被刪掉了,後續 acquireSingleInstance 嘗試在裡面建 lock 檔,os.OpenFileENOENT(父目錄不存在)。
  8. ENOENT 不是 IsExist,函式直接回錯。外層的 handler 把「任何錯誤」都當成「already running」→ os.Exit(0)

我用 Python 實測確認:

os.makedirs('~/Library/Application Support/visiona-local')
os.path.exists('~/Library/Application Support/visionA-local')  # → True

這個 bug 在任何預設設定的 Mac 上都會觸發APFS 預設 case-insensitive等於 M1 在大部分 Mac 上根本跑不起來。

第二次啟動workaround預先建立非空 dataDir

為了繼續測後面的步驟,我先 mkdir visiona-local && touch .keep。這樣 migrateOldDataDirs 的「newDir 已有內容 → 拒絕遷移」分支會 kick in繞過 bug 1。

結果Wails app 進程活著PID 77828lock file 建好了,寫了 visiona-local.ipc-port = 3721

port 3721 並沒有人 listen

$ lsof -i :3721
(空)
$ curl http://127.0.0.1:3721/api/system/health
(連線失敗)

檢查 ~/Library/Application Support/visiona-local/logs/server.stderr.log

flag provided but not defined: -python
Usage of .../visiona-local-server:
  -python-mode string ...
  (列出所有合法 flag其中沒有 -python

P0 Bug #2 visiona-local/app.go:234 組 server 參數時:

args := []string{
    "--host", "127.0.0.1",
    "--port", strconv.Itoa(port),
    "--data-dir", a.dataDir,
    "--python-mode", string(pyMode),
}
if pyBin != "" {
    args = append(args, "--python", pyBin)   // ← server 沒有這個 flag
}

Server binary 只認 --python-mode不認 --python。當系統有 python3我這台 /usr/local/bin/python3)時,ensurePythonRuntime 會回傳 pyBin於是 --python /usr/local/bin/python3 被加到參數列。Server 一啟動就 flag provided but not defined: -pythonos.Exit(2)

Wails app 沒有偵測到 server 掛了(沒有 health check wait或者 check 失敗但沒有 abort繼續掛在前景。

組合效應: 就算使用者手動繞過 bug 1只要這台 Mac 上有安裝過 python3絕大多數開發者 / macOS 自帶),就會撞到 bug 2。Mock 模式理論上不該碰 python但目前的 code path 還是會把 pyBin 傳下去。

Step 8: 主 UI 可訪問

因為 server 沒起來,curl http://127.0.0.1:3721/ 得到 HTTP 000(連線拒絕)。瀏覽器看不到任何東西。

Step 9: 資料目錄 ⚠️

在 workaround 下資料目錄內容:

.keep我手動建的
logs/
  server.stderr.log     ← 記錄 server 啟動失敗
  server.stdout.log     ← 空
visiona-local.ipc-port  ← 內容 "3721"(但 port 根本沒在 listen
visiona-local.lock      ← 有 PID

.ipc-port 檔寫的是預期 port,不是實際 port,這本身也是潛在問題(但相比前兩個 bug 是次要的)。

Step 10: 乾淨退出

pkill -9 -f visiona-localpgrep -fl visiona-local 為空,.app 複本、資料目錄都清除乾淨。

Step 11: make clean + make dmg 從頭跑 ⏭️ 略過

前面已經是阻斷級問題,重新 build 也不會修掉這兩個 bug。略過本步驟等修好再驗。


M1 驗收結論

核心承諾 結果
1. 雙擊 .dmg → mount → .app 存在
2. 啟動 → Mock 模式跑起來 Wails app 秒退bug 1或 server 立刻 crashbug 2
3. API (/api/system/health/api/system/info) 可訪問 port 沒有人 listen
4. 主 UI 可在瀏覽器看到 同上
5. 乾淨退出 (但因為根本沒真的「運作」過,退出也沒什麼好清的)

核心承諾 2 / 3 / 4 全部沒達成。M1 未通過。


發現的問題

🔴 P0 阻斷

P0-1: migrateOldDataDirs 在 case-insensitive APFS 上自我毀滅

  • 位置visiona-local/app.go:572-598 + oldDataDirCandidates 清單中的 visionA-local
  • 影響:任何預設 APFS 的 Mac絕大多數使用者第一次啟動必失敗
  • 修法建議
    • (A) 用 os.Stat 比對 inode如果舊路徑和新路徑指向同一個 inode代表 case-insensitive FS 且實際上是同一個目錄,直接 continue,不要嘗試遷移。
    • (B) 或者,在 candidates 裡移除 visionA-local — 反正新版是 visiona-local,如果之前用的也是 visiona-localcase-insensitive FS 下等效),根本不需要遷移。只有在真正 case-sensitive FS少數使用者自選才需要這個 candidate那就用 FS 類型偵測決定要不要加。
    • (C) 額外修正:startup() 裡對 acquireSingleInstance 的錯誤處理要分辨「真的有別的 instance 在跑」vs「其他錯誤例如資料目錄不見了」。現在任何錯誤都印 "already running" 會誤導 debug。

P0-2: Wails 傳給 server 的 --python flag 不存在

  • 位置visiona-local/app.go:234
  • 影響:只要系統上有 pythonmacOS 幾乎都有server 子行程啟動即死。Mock 模式也一樣會死。
  • 修法建議
    • 改成 --python-bin(並在 server 端加對應 flag
    • 把 pyBin 放在環境變數 VISIONA_PYTHON_BIN 傳,或
    • 在 Mock 模式下完全不傳 pyBin 相關參數(根本用不到)。
  • 額外Wails 端應該在 startServer() spawn 後做一次 health check例如 500ms 內 poll /api/system/health 幾次),失敗就 reportFatal,不要讓 app 繼續假裝有在運作。

🟡 需修(非阻斷)

M-1: .ipc-port 寫的是預期 port 不是實際 port

Server 啟動前就寫好 port file但如果 server 實際沒起來bug 2 的情況),前端或 IPC 呼叫者會連到一個不存在的 port。應該在 server 真的 LISTEN 成功後才寫 ipc-port 檔(或由 server 自己寫)。

M-2: 系統級 launchd agent 殘留

~/Library/LaunchAgents/com.innovedus.edge-ai-server.plist 會自動重啟 legacy daemon搶 3721 port。M1 uninstall / migration 文件應提醒使用者:若曾安裝 edge-ai-platform需要手動 launchctl unload + 刪 plist。或在首次啟動時偵測到同 port 衝突時給出提示。

🟢 建議

  • 測試環境 matrix:目前所有開發測試都在同一台有 python、有舊 edge-ai-platform、有 legacy launchd 的 Mac 上跑。這兩個 bug尤其 bug 1在「乾淨 Mac」上才暴露。建議納入「新 macOS VM / 沒有 python 的 Mac / case-sensitive APFS 的 Mac」三種情境做 CI-level 驗證。
  • codesign 目前是 ad-hoc/self-signed上線前需要 Apple Developer ID 簽章 + notarize否則 Gatekeeper 會擋。本次測試沒觸發這個議題(因為 .app 是本機 build 直接 run但那是因為本機沒有 quarantine attr。從 .dmg 真的雙擊打開時會有不同行為,建議補 Gatekeeper 測試。
  • Legacy LaunchAgent 議題(上方 M-2也要在交付文件 (07-delivery/launch-checklist.md) 列成「升級安裝注意事項」。

結論:M1 未通過,需修

兩個 P0 bug 都必須在 ship 前修掉。建議流程:

  1. Architect Agent 決定 bug 1 的修法inode 比對 vs. 移除候選 vs. FS 偵測),以及 bug 2 的 flag 契約(改名 / 環境變數 / mock 模式不傳)。
  2. Backend Agent 實作修復(app.go 兩處改動 + 可能的 server flag 新增)。
  3. Reviewer 審查。
  4. 重新 make clean && make dmg我再跑一次完整 E2E
  5. 本次的前置清理步驟unload launchd也應寫進 launch-checklist提醒升級使用者。

附錄:本次使用的清理指令

# 卸下 legacy launchd agent若有
launchctl unload ~/Library/LaunchAgents/com.innovedus.edge-ai-server.plist
pkill -9 -f edge-ai-server
pkill -9 -f visiona-local

# 清資料目錄(模擬全新使用者)
rm -rf "$HOME/Library/Application Support/visiona-local"
rm -rf "$HOME/Library/Application Support/visionA-local"  # 注意APFS 上這會和上一行等效

# 卸載 .dmg若還掛著
hdiutil detach /Volumes/visionA-local