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

148 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# ADR-008Tunnel Client 採「程式碼複製」策略在多個專案間共享
## 狀態
Accepted — 2026-04-22v1
**Updated** — 2026-04-22v2補充 §「visionA-backend 端 tunnel package 應刪除」)
## 背景 (Context)
Tunnel clientWebSocket + 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 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 replace`replace` 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」事件
## 合規性
- [x] 與 Q2 決策POC → visionA-backend 複製)一致
- [x] 與 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.go`POC 原始 — 唯一存活來源)
- ~~`visionA-backend/internal/tunnel/`~~**v2 決策刪除**,從未被使用)
- `visionA-backend/internal/relay/`(保留 — 這是 tunnel **server** 端,與 client 不同職責)
- `local-agent/internal/tunnel/`(本次新增 — 從 POC 直接複製)