依 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)。
148 lines
8.1 KiB
Markdown
148 lines
8.1 KiB
Markdown
# ADR-008:Tunnel Client 採「程式碼複製」策略在多個專案間共享
|
||
|
||
## 狀態
|
||
Accepted — 2026-04-22(v1)
|
||
**Updated** — 2026-04-22(v2,補充 §「visionA-backend 端 tunnel package 應刪除」)
|
||
|
||
## 背景 (Context)
|
||
|
||
Tunnel client(WebSocket + yamux)邏輯會在以下地方出現:
|
||
|
||
1. **POC edge-ai-platform**:`server/internal/tunnel/client.go`(原始來源)
|
||
2. **visionA-backend**:`internal/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 path:`edge-ai-platform/pkg/wsconn` → `visiona-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」,需要更正
|
||
|
||
```bash
|
||
$ 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 client(agent 出站連線)完全相反
|
||
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 到雲端 relay,accept 反向 stream 並轉發到本機 HTTP server | outbound(agent 主動連出)|
|
||
| ~~`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 replace(`replace` directive)** | 語法乾淨 | 要求有共同的 module,agent 和 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 change(HTTP/3 / gRPC 切換 / E2E 加密)
|
||
- Consumer 增加到 4+ 個(例如出現 headless tunnel-only agent)
|
||
- 實際出現 2+ 次的「bug 只修了 A 忘了修 B」事件
|
||
|
||
## 合規性
|
||
|
||
- [x] 與 Q2 決策(POC → visionA-backend 複製)一致
|
||
- [x] 與 ADR-007(fork 模式)一致
|
||
- [ ] 建立 tunnel 變更 checklist:TODO(在第一次實際跟進 local-tool 或 backend 變動時產出)
|
||
|
||
## 相關文件
|
||
|
||
- `.autoflow/04-architecture/tunnel.md`
|
||
- ADR-002(沿用 POC tunnel 協定)
|
||
- ADR-007(visionA Agent 架構)
|
||
- 相關程式碼:
|
||
- `/Users/jimchen/Innovedus/edge-ai-platform/edge-ai-platform/server/internal/tunnel/client.go`(POC 原始 — 唯一存活來源)
|
||
- ~~`visionA-backend/internal/tunnel/`~~(**v2 決策刪除**,從未被使用)
|
||
- `visionA-backend/internal/relay/`(保留 — 這是 tunnel **server** 端,與 client 不同職責)
|
||
- `local-agent/internal/tunnel/`(本次新增 — 從 POC 直接複製)
|