visionA/docs/autoflow/04-architecture/adr/adr-008-tunnel-client-reuse.md
jim800121chen fb7da5d180 chore(autoflow): migrate .autoflow/ 共享層文件至 docs/autoflow/
依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類
共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git),
讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等
per-branch 筆記。

- 02-prd/        21 個檔(PRD、features、market-analysis 等)
- 03-design/     18 個檔(design-spec、wireframes、flows 等)
- 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等)
- 07-delivery/   3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup)

合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv,
但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
2026-05-04 16:55:55 +08:00

8.1 KiB
Raw Permalink Blame History

ADR-008Tunnel Client 採「程式碼複製」策略在多個專案間共享

狀態

Accepted — 2026-04-22v1 Updated — 2026-04-22v2補充 §「visionA-backend 端 tunnel package 應刪除」)

背景 (Context)

Tunnel clientWebSocket + yamux邏輯會在以下地方出現

  1. POC edge-ai-platformserver/internal/tunnel/client.go(原始來源)
  2. visionA-backendinternal/tunnel/(雛形 Q2 決策:從 POC 複製到此,獨立演進)
  3. visionA Agent(本次規劃):也需要一份 tunnel client連雲端 remote-proxy

問題visionA Agent 的 tunnel client 要從哪來?有三個選項:

  • A. 從 POC 再複製一份(獨立於 visionA-backend
  • B. 從 visionA-backend 複製(已經搬過一次,從新的來源搬)
  • C. 共用同一份git submodule、go module replace、或抽成獨立 library

決策 (Decision)

v2 修正:採方案 A從 POC 直接複製到 local-agent同時刪除 visionA-backend/internal/tunnel/

v1 原決策:方案 B 從 visionA-backend 複製到 local-agent已撤銷原因見下方「v2 修正visionA-backend 端 tunnel package 應刪除」)

具體做法:

  • cp -r edge-ai-platform/edge-ai-platform/server/internal/tunnel/ local-agent/internal/tunnel/
  • 調整 import pathedge-ai-platform/pkg/wsconnvisiona-agent/server/pkg/wsconn(因為 agent 內已經從 local-tool 複製了 server/pkg/wsconn
  • 參數化 localAddr:讓 NewClient() 可接受 httpserver.Controller.LocalAddr() 回傳的 random port
  • 同步刪除 visionA-backend/internal/tunnel/ 整個 package見下方說明

與 ADR-007 一致fork 後獨立演進,不主動 sync。

v2 修正visionA-backend 端 tunnel package 應刪除(事實上已於 2026-04-21 刪除)

查證結論

經查證 visionA-backend 程式碼2026-04-22

  1. 目錄已不存在visionA-backend/internal/tunnel/ 在 2026-04-21 已被刪除(見 visionA-backend/README.md §Known Issues
  2. 歷史上從未被 import:刪除前的版本,沒有任何 visionA-backend 程式碼 import 過這個 package
  3. README 已記錄此決策,但 v0.1 版的 visiona-agent-tdd.md 仍然提到「從 visionA-backend 複製 tunnel client」需要更正
$ ls visionA-backend/internal/ | grep tunnel
(無任何結果 — 目錄不存在)

$ grep -r "internal/tunnel" visionA-backend/cmd/
(無任何結果 — 沒有任何 import

$ grep "internal/tunnel" visionA-backend/README.md
324: ... 已於 2026-04-21 刪除 ...

本 ADR v2 的角色

本次 v2 更新不是觸發刪除動作(刪除已發生),而是:

  1. 把這個事實正式記錄進架構決策(之前只在 README
  2. 修正 v1 ADR 的方向(原 v1 寫「從 visionA-backend 複製到 local-agent」現改為「從 POC 直接複製到 local-agent」
  3. 提供「為什麼當初會被複製進去 / 為什麼後來要刪」的完整脈絡,避免未來有人想加回去

為什麼當初會被複製進來

B3 階段cmd/remote-proxy 實作時),為了「預留給未來 local-agent 用」而從 POC 複製到 visionA-backend。但實際上

  1. visionA-backend 自己不需要 tunnel client — visionA-backend 是 tunnel 的雲端伺服器端,用 internal/relay/ 接受 agent 的入站連線,職責與 tunnel clientagent 出站連線)完全相反
  2. local-agent 沒必要繞道 — local-agent 直接從 POC 複製就好,多一層 visionA-backend 中轉沒有任何好處POC 才是原始來源)
  3. 保留會誤導未來維護者 — 看到 visionA-backend/internal/tunnel/ 會以為 visionA-backend 有 tunnel client 角色,實際上沒有

決策

刪除 visionA-backend/internal/tunnel/ 整個 package在 AB1 階段執行)。

職責對照(避免再混淆)

Package 位置 職責 連線方向
internal/relay/ visionA-backend tunnel server:接受 agent 入站 WSS 連線,管理 yamux session pool inbound雲端被動接受
internal/tunnel/ local-agent新位置,從 POC 複製 tunnel client:主動撥 WSS 到雲端 relayaccept 反向 stream 並轉發到本機 HTTP server outboundagent 主動連出)
internal/tunnel/ visionA-backend 已於 2026-04-21 刪除從未被使用B3 預留誤導)

原則明確化

程式碼不複製到不會用的地方。 「未來可能會用到」不是複製的理由 — 真正要用時再從原始來源複製即可。預先複製會:

  • 增加維護負擔(要 sync POC 變更到一個沒人用的 package
  • 誤導維護者對架構的理解
  • 違反 YAGNI

這個原則應該在所有 visionA 子專案間遵守(特別是 fork / 複製模式下)。

考慮過的替代方案

方案 優點 缺點 排除原因
A. 從 POC 直接搬 POC 是原始來源 代表 POC 要同時支援兩個下游,更新更分歧 visionA-backend 已成為「雛形基準版」,從它搬更新
C1. git submodule 真共享、單一改動點 submodule 在 monorepo 中難管理、CI 綁定複雜、協作者 clone 需額外步驟 過度複雜
C2. go module replacereplace directive 語法乾淨 要求有共同的 moduleagent 和 backend module 不同;跨 go.mod 易造成 ghost dependency 不自然
C3. 抽成獨立 visiona-tunnel repo 最乾淨的 library 模式 需要設計 stable API、發版、文件維護 3 個 repo 的版本矩陣;只有 2 個 consumer不值得 過度工程
C4. Monorepo root 放 pkg/tunnel/agent + backend 皆 import 真共享且管理集中 整個 visionA monorepo Go module 結構需要重組(目前是每個子專案一個 module對 local-tool 本來獨立的狀態有風險 風險大
B本決策code copy 簡單、無新工具鏈、符合既有 fork 模式 未來 tunnel 協定要升版時要改 3 個地方POC / backend / agent tunnel 協定已穩定,改動頻率低,維護成本可接受

後果 (Consequences)

正面影響

  • 無新工具鏈:純 go.mod + 普通目錄複製,沒有 submodule / replace 黑魔法
  • 一致性風格:與 ADR-007 的 fork 策略一致
  • 改動自由visionA Agent 的 tunnel client 可以加專屬功能Agent-Version header、重連事件 hook而不影響 backend

負面影響(接受的取捨)

  • 三份程式碼共存POC / visionA-backend / visionA Agent 各一份 tunnel client
  • Bug 修復需要 3 處同步:例如修了 reconnect 退避演算法3 邊都要 review 是否跟進
  • 未來的協定升級(如 HTTP/3需要 3 次修改

風險

  • 遺忘同步導致差異累積:明確的 mitigation — tunnel 協定變更必須寫新 ADR同時 review 是否 cherry-pick
  • POC 已經是死分支:預期 POC 不會再大改;主要 sync 路徑是 visionA-backend ↔ visionA Agent

Phase 1 重新評估條件

若出現以下任一情形,重新評估(可能抽成 library

  • tunnel 協定有 breaking changeHTTP/3 / gRPC 切換 / E2E 加密)
  • Consumer 增加到 4+ 個(例如出現 headless tunnel-only agent
  • 實際出現 2+ 次的「bug 只修了 A 忘了修 B」事件

合規性

  • 與 Q2 決策POC → visionA-backend 複製)一致
  • 與 ADR-007fork 模式)一致
  • 建立 tunnel 變更 checklistTODO在第一次實際跟進 local-tool 或 backend 變動時產出)

相關文件

  • .autoflow/04-architecture/tunnel.md
  • ADR-002沿用 POC tunnel 協定)
  • ADR-007visionA Agent 架構)
  • 相關程式碼:
    • /Users/jimchen/Innovedus/edge-ai-platform/edge-ai-platform/server/internal/tunnel/client.goPOC 原始 — 唯一存活來源)
    • visionA-backend/internal/tunnel/v2 決策刪除,從未被使用)
    • visionA-backend/internal/relay/(保留 — 這是 tunnel server 端,與 client 不同職責)
    • local-agent/internal/tunnel/(本次新增 — 從 POC 直接複製)