致命發現(grep MC + FAA source 確認):
- MC source 沒有 issue delegated download token endpoint
- MC source 沒有 validate delegated download token endpoint
- FAA MemberCenterDelegatedDownloadTokenValidator.cs 假設的 MC introspection endpoint 不存在
- ADR-014 §2 從 5/2 寫完到現在這條鏈一直是斷的、只是因為從未實際 e2e 跑通過所以沒被發現
使用者拍板硬約束:不動 MC + 不動 FAA
新增 ADR-016:
- visionA download 改用 converter GET /api/v1/jobs/{id}/result(新 endpoint)
- visionA backend 用既有 ConverterAPIKey 認證(不需新增 secret)
- 維持 T4 已實作的 stream proxy 結構(io.CopyN + Content-Disposition + size cap)
- promote 仍 PUT FAA(converter 內部用自己的 OAuth、與 visionA 無關)
- 不需動 MC + FAA + warrenchen
- 6 個替代方案逐一說明排除理由
修訂既有文件:
- ADR-014 v1.1 → v1.2:§2 download flow 標註被 ADR-016 部分 supersede
- ADR-015 v2.0 → v2.1:§2 visionA → FAA delegated token 設計(v2.0 從 v1.x 撤回的設計)再次撤回;§9 env 表撤回 v2.0 加回的 OIDC ServiceClient* / TenantID / FAABaseURL;visionA 端 server-to-server 只剩 ConverterAPIKey 一把
- conversion.md v0.5 → v0.6:§1 sequence diagram 重畫(移除 MC node)、§2 模組設計(mc_token_client.go 整檔刪除確認、faa_client.go 改名 converter_result_client.go)、§3.2 visionA → FAA 整段標撤回、§4.1 download handler 改 converter.GetResult、§6 錯誤碼撤回 mc/faa 三個 code 加 result_not_found / result_expired
- api-conversion.md v0.5 → v0.6:檔頭 Auth 段落改寫、§4 download endpoint 改述、error code 表撤回 mc_token_unavailable / download_token_failed
- oidc-tdd.md v0.3 → v0.4:§13.1 環境變數表 OIDC ServiceClient* / TenantID / FAABaseURL 從「重新啟用」改回「再次廢棄」、§13.1.1 stage env 範例移除 service client / tenant_id / FAA URL、§13.1.3 改寫為「v0.4 單線設計」說明
整體影響:
- 不需復活 mc_token_client.go(commit 86b7175 砍除狀態維持)
- 不需復活 OIDCConfig.ServiceClientID/Secret/TenantID(commit 86b7175 移除狀態維持)
- visionA backend faa_client.go 要改名為 converter_result_client.go、改呼叫 converter.GetResult
- visionA backend flow.go DownloadStream / PromoteToModels 改用 converter.GetResult
- jimchen 跨 repo 任務:converter scheduler 加 GET /api/v1/jobs/{id}/result endpoint
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
254 lines
19 KiB
Markdown
254 lines
19 KiB
Markdown
# ADR-014:visionA 端轉檔功能架構(Phase 0.8)
|
||
|
||
## 狀態
|
||
Accepted — 2026-04-30 / **§2 download flow 部分 supersede — 2026-05-16(ADR-016)**
|
||
|
||
> **2026-05-16 更新**:§2「Download — FAA delegated token(browser 直連 / v1.1 後 server-side proxy)」整段被 [ADR-016](./adr-016-download-via-converter.md) 部分 supersede。原因:對 MC source 完整驗證後發現「MC issue + validate delegated download token」endpoint **從未存在**——本 §2 從 2026-05-02 寫定起即為 broken design,只是因從未 e2e 跑通 visionA → FAA download 而未被發現。
|
||
>
|
||
> **新設計**:visionA download 改走 converter 新增的 `GET /api/v1/jobs/{id}/result` + visionA stream 中轉(jimchen 可單方控制兩端、不必動 MC / FAA / warrenchen)。詳見 ADR-016。
|
||
>
|
||
> **本 ADR 仍有效的段落**:§1(upload streaming proxy)、§3(半自動分流的「加到模型庫」原則,但 server-side pull 的 FAA 部分改走 converter)、§4(模組劃分)、§5(service token cache 僅 converter 部分已被 ADR-015 §1 supersede;FAA 部分被 ADR-016 supersede)、§6(user_id trust boundary,**核心原則完全不變**)、§7(FAA / MC 相關 row 被 ADR-016 supersede;converter row 維持)、§8(active job 衝突處理不變)。
|
||
|
||
## 上位 / 同層 ADR
|
||
- 沿用:[ADR-006](./adr-006-no-redis-in-prototype.md)(in-memory state)、[ADR-010](./adr-010-oidc-bff.md)(OIDC BFF + confidential client)、[ADR-011](./adr-011-supersede-adr-005.md)(OIDC 取代 StaticAuth)、[ADR-013](./adr-013-public-client.md)(user OIDC client 為 public + PKCE-only;service client 仍為 confidential)
|
||
- 並存:本 ADR 規範「visionA-backend 同時當 multipart streaming proxy(upload)+ delegated download token broker(download)」
|
||
|
||
## 背景 (Context)
|
||
|
||
Phase 0.8 要把 kneron_model_converter(以下簡稱 converter)整合進 visionA Cloud。雙方為各自獨立部署的後端:
|
||
|
||
- **converter** 仍在公司內網 `192.168.0.130`:`POST /api/v1/jobs`(multipart, 500MB cap)/ `GET /api/v1/jobs/{id}` poll / `POST /api/v1/jobs/{id}/promote` 推 NEF 到 File Access Agent (FAA)
|
||
- **visionA-backend** 將部署到 AWS(stage 已上 `https://stage-9527.innovedus.com:9527/`)
|
||
- **FAA** 是 ASP.NET Core stateless 服務,存放 NEF;支援 `GET /files/{key}?access_token=<delegated>` browser 直連
|
||
- **Innovedus Member Center (MC)** 是 OAuth/OIDC IdP,同時負責簽 service-to-service token 與 delegated download token
|
||
|
||
整合上必須回答兩個問題:
|
||
|
||
1. **Upload(轉檔 input)** 怎麼進 converter?browser 直連 vs visionA backend 中轉?
|
||
2. **Download(轉檔結果)** 怎麼出 FAA?browser 直連 vs visionA backend 中轉?
|
||
|
||
並存的設計約束:
|
||
|
||
- visionA-backend 是 user 身份 / OIDC sub 注入 converter `user_id` 表單欄位的**唯一可信任點**(converter 完全信任 caller 帶來的 user_id,見 converter openapi.yaml `## user_id 與 trust boundary`)
|
||
- converter 一個 `user_id` 同時間只能有 1 個 active job(`409 user_has_active_job`)
|
||
- FAA delegated download token TTL 短(5–15 分鐘),可給 browser 直連
|
||
- Member Center service client(`23605e14a2c64660abd97e29963d8d58`)已配置,需 4 個 scope:`converter:job.write/read`、`files:download.read/delegate`
|
||
- `internal/config.OIDCConfig.ServiceClientID/Secret` 鉤子在 ADR-013 / Phase 0.7 已預埋但未啟用(A1 階段)
|
||
|
||
Phase 0.8 MVP 範圍:上傳 → 轉檔 → 半自動處理(user 完成後選「加到模型庫」or「下載」)。**Non-Goals**:歷史 / 取消 / SSE 進度推送 / 同 user 多個 active job / 多 chip 同時轉。
|
||
|
||
## 決策 (Decision)
|
||
|
||
採 **Upload 走 visionA backend streaming proxy + Download 走 FAA delegated token(browser 直連)** 的非對稱設計,並把 visionA-backend 同時當 **multipart streaming proxy** + **delegated download token broker**。
|
||
|
||
### 1. Upload — 一次性 → visionA backend 中轉
|
||
|
||
```
|
||
Browser ──multipart──► visionA backend ──multipart streaming──► converter
|
||
(io.Pipe + multipart.Reader/Writer)
|
||
```
|
||
|
||
- 每個檔案只上傳「一次」,跨 internet 一次成本可接受(500MB × 1 次 vs 500MB × N 次下載)
|
||
- 用 `io.Pipe` + goroutine:一邊讀 client、一邊寫 converter — **不暫存 disk、不 buffer 全 RAM**
|
||
- visionA-backend 在這條路徑做的事:
|
||
1. 從 cookie session 取 `user_id`(OIDC sub),灌進 converter request 的 `user_id` 表單欄位
|
||
2. 跟 MC 取 service token(scope `converter:job.write`),帶在 `Authorization: Bearer`
|
||
3. 透傳 model file + ref_images[] + 其他 form fields(target_chip / 各 enable_* flag)
|
||
4. converter response 整形後回 frontend(不直接洩 converter response shape)
|
||
- converter **零修改** — 沿用既有 `POST /api/v1/jobs` multipart endpoint
|
||
|
||
### 2. Download — 多次性 → FAA delegated token(server-side 302 redirect → browser 直連 FAA)
|
||
|
||
> ⚠️ **2026-05-16:本節整段被 [ADR-016](./adr-016-download-via-converter.md) supersede**。
|
||
>
|
||
> 致命發現(2026-05-16):
|
||
> 1. MC source 沒有 `POST /file-access/download-tokens` endpoint(無法 issue delegated token)
|
||
> 2. MC source 沒有 `IDelegatedDownloadTokenValidator` 對應的 introspection endpoint(即使有 token 也無法 validate)
|
||
> 3. FAA `GET /files/{key}` 強制只接 delegated token、不接 service token(visionA 即使能拿 service token 也打不進去)
|
||
>
|
||
> 本節下方描述的「server-side 302 redirect」/「跟 MC 換 delegated token」/「FAA 跟 MC validate」整條鏈從 2026-05-02 起即為 broken design、從未 e2e 跑通。
|
||
>
|
||
> **新設計(ADR-016)**:visionA download 改走 converter 新增的 `GET /api/v1/jobs/{id}/result` + visionA stream 中轉。不再有 visionA → MC、visionA → FAA 任何 server-to-server 路徑。
|
||
>
|
||
> 本節以下內容**僅作歷史保留**、實作以 ADR-016 為準。
|
||
|
||
|
||
```
|
||
Browser ──GET /api/conversion/{job_id}/download──► visionA backend
|
||
↓
|
||
ownership 檢查
|
||
↓
|
||
MC POST /file-access/download-tokens
|
||
↓
|
||
Browser ◄─── HTTP 302 Found, Location: https://faa/files/{key}?access_token=<delegated>
|
||
↓
|
||
browser 自動 follow redirect
|
||
↓
|
||
Browser ──直連 FAA──► GET /files/{key}?access_token=<delegated>
|
||
```
|
||
|
||
- 同 NEF 可能被同一 user 多次下載到不同 device,N 次跨 internet 流量燒不起
|
||
- FAA 收到 token 後線上跟 MC validate(FAA 自己跟 MC 對打,visionA-backend 不參與)
|
||
- visionA-backend 在這條路徑做的事(單一 GET endpoint 內完成):
|
||
1. 既有 OIDC AuthMiddleware 驗 cookie session 拿 user_id
|
||
2. 確認該 user 對該 job 有權(從 visionA 內部記錄查 ownership,**禁止讓 client 直接傳 object_key**)
|
||
3. server-to-server 跟 MC 換 delegated token(scope `files:download.delegate`)
|
||
4. 組 download URL 後直接 `c.Redirect(http.StatusFound, downloadURL)` — 把 token 放在 Location header
|
||
- visionA-frontend 不需處理 token:用 `<a href="/api/conversion/{job_id}/download" download>下載</a>` 或 `window.location.href = '/api/conversion/{job_id}/download'` 即可,browser 自動 follow 302
|
||
- **Pattern 對齊**:仿 FAA TestSite `DownloadFileDirect` action(`FileAccessAgent.TestSite/Controllers/HomeController.cs:255-282`)— 也是 server 端組 URL 後 `return Redirect(directUrl)`,token 不過 frontend JS
|
||
|
||
**為什麼 302 redirect 比「frontend 拿 token + navigation」更安全**
|
||
|
||
| 面向 | 方案 X(frontend 拿 token JSON)| 方案 ✓(server 302 redirect)|
|
||
|------|-----|-----|
|
||
| Token 在 fetch response body | ✗ 在(JS 看得到、可能進 console.log / Sentry / 第三方分析)| ✓ 不在(沒有 JSON response)|
|
||
| Token 在 URL bar | ✗ 在(`window.location.href = url` 之後 URL bar 會短暫顯示)| △ 短暫(302 的 final URL 仍會出現,但 browser navigation 完成後通常立即被 FAA download 流程取代;且 navigation 期間 history entry 可被 `Cache-Control: no-store` + 短 TTL 緩解)|
|
||
| Token 在 localStorage / sessionStorage | △ 視 frontend 實作(容易誤存)| ✓ 結構性不可能(沒入口)|
|
||
| 受 frontend XSS 影響 | ✗ XSS 可竊取 token | ✓ XSS 看不到(302 在 fetch 場景會自動 follow,response body 為 FAA 內容;但 anchor / navigation 場景 JS 完全看不到)|
|
||
| 需要 FAA CORS 設定 | ✗ 需要(fetch / XHR 受 CORS 限制) | ✓ 不需要(CORS 只管 JS fetch / XHR;server-side 302 + browser navigation 走「navigation request」,完全不適用 CORS)|
|
||
| 跟 visionA OIDC cookie session 整合 | △ 額外 endpoint + JSON 流程 | ✓ 自然整合(GET endpoint 走既有 AuthMiddleware)|
|
||
| Frontend 程式碼複雜度 | 中(fetch → 取 url → navigation) | 低(一個 anchor tag / 一行 navigation)|
|
||
|
||
**Token 仍需 4 個 scope**:visionA-backend 為了跟 MC 換 delegated token,service token 仍需 `files:download.delegate` scope(沒變)。302 redirect 是「換到 token 後怎麼把它送進 browser」的差異,不影響 token issuance 路徑。
|
||
|
||
### 3. 半自動 — converter 完成後使用者選擇路徑
|
||
|
||
job `completed` 後 frontend 詢問 user:
|
||
|
||
| 動作 | 路徑 | 說明 |
|
||
|------|------|------|
|
||
| 「加到模型庫」 | visionA backend 跟 FAA pull NEF(server-to-server,scope `files:download.read`)→ 走既有 `/api/models/init` + `/api/models/finalize` 三段式 upload flow → 寫進 visionA `model.Model.Source="converted"` + `SourceJobID=<converter-job-id>` | 進 visionA storage 給後續 device load 用,這次走 backend 因為終點是 visionA storage |
|
||
| 「下載」 | 上述 §2 流程 | browser 直連 FAA |
|
||
|
||
兩者都先呼叫 converter `POST /api/v1/jobs/{id}/promote`,promote response 含 `target_object_key`。
|
||
|
||
### 4. 模組劃分 — 新增 `internal/conversion/`
|
||
|
||
不擴 `model.Model` schema(`Source` / `SourceJobID` 欄位 ADR-005 / database.md 已預埋)。新增獨立 package:
|
||
|
||
```
|
||
internal/conversion/
|
||
├── conversion.go # 對外 interface (Service)
|
||
├── converter_client.go # converter scheduler API client
|
||
├── faa_client.go # FAA API client(delegated token + server-to-server pull)
|
||
├── mc_token_client.go # MC client_credentials grant + token cache
|
||
└── flow.go # 整體 flow 協調(init / poll / promote / pull / persist)
|
||
```
|
||
|
||
`internal/conversion/` 依賴 `internal/model.Repository`(沿用既有 `/api/models/init+finalize` 邏輯,不繞過)。
|
||
|
||
### 5. Service token cache — 仿 converter scheduler 模式
|
||
|
||
- visionA backend 啟動時不主動取(lazy),第一次需要時才打 MC `POST {issuer}/oauth/token` (`grant_type=client_credentials`)
|
||
- token cache(記憶體 + `sync.RWMutex`),`exp - 15s` 重取
|
||
- token request 失敗:4xx 不重試(log + 5xx response 給 client);5xx 指數退避 max 2 次
|
||
- visionA-backend 預設 service-to-service token 共用(converter:job.write / read / files:download.read / delegate 同一 client + 同一個 cache)— MC 端發單一 token 含所有 4 個 scope
|
||
|
||
### 6. user_id 注入 + trust boundary
|
||
|
||
- **visionA backend 是唯一灌 user_id 的點**:從 cookie session 拿 OIDC sub → POST /jobs 時帶 user_id
|
||
- converter 信任 visionA backend 帶來的 user_id(converter 端的 trust boundary 設計詳見 converter openapi.yaml)
|
||
- visionA-backend 必須確保:
|
||
1. 任何呼叫 converter 的 endpoint 一律先過 OIDC AuthMiddleware(既有)
|
||
2. job_id → user_id 的 mapping 記在 visionA 內部(in-memory 或之後 DB),每次 status / promote / download token 操作前 ownership 檢查
|
||
3. **絕不接受 client 直接傳 user_id / object_key** — 一律從 session 反查
|
||
|
||
### 7. 失敗模式 retry 矩陣
|
||
|
||
| 操作 | 重試策略 | 失敗回 frontend |
|
||
|------|---------|---------------|
|
||
| `POST /api/v1/jobs`(init) | 4xx 不重試;5xx / network 退避 max 2 次 | 4xx 透傳 converter error code;5xx 一律 `502 converter_unavailable` |
|
||
| `GET /api/v1/jobs/{id}`(poll) | 5xx / network 退避 max 3 次;各次 2s 內 timeout | 持續失敗 → frontend 視為 stuck,提示重試 |
|
||
| `POST /promote` | 5xx / network 退避 max 2 次 | 失敗回 `502 promote_failed`,job 留在 completed 狀態,user 可重試 |
|
||
| FAA pull(加到模型庫)| 5xx / network 退避 max 2 次 | 失敗回 frontend `502 faa_unavailable`,model record 不寫入 |
|
||
| MC token endpoint | 4xx fatal;5xx 退避 max 2 次 | 失敗回 frontend `503 idp_unavailable` |
|
||
| MC delegated token | 4xx 透傳;5xx 退避 max 2 次 | 失敗回 frontend `502 download_token_failed` |
|
||
|
||
### 8. 同 user active job 衝突(409)
|
||
|
||
converter 回 `409 user_has_active_job` → visionA-backend 透傳 `409 active_job_exists` + 既有 job 詳情給 frontend,由 frontend 提示「你已有進行中的轉檔任務」。
|
||
|
||
## 考慮過的替代方案
|
||
|
||
### 方案 A:Upload 也走 browser 直連(converter 開放 CORS + 公網)
|
||
|
||
| 評估 | 內容 |
|
||
|------|------|
|
||
| 優點 | visionA-backend 不需處理 500MB streaming,省記憶體與頻寬 |
|
||
| 缺點 | (1) converter 必須開公網或開 CORS,安全表面變大;(2) user_id trust boundary 失守(browser 自己灌 user_id 等於沒驗);(3) converter 要新增 OIDC delegated upload token 機制(converter 團隊額外工作量) |
|
||
| 排除原因 | **user_id 信任邊界守不住**;converter 端要新增工作量。Upload 一次性,跨 internet 成本可接受 |
|
||
|
||
### 方案 B:Download 也走 visionA backend 中轉
|
||
|
||
| 評估 | 內容 |
|
||
|------|------|
|
||
| 優點 | visionA-backend 看得到所有下載流量、易做 audit |
|
||
| 缺點 | (1) 跨 internet 流量 N 倍(同 NEF 多次下載);(2) visionA-backend 變成 streaming bottleneck;(3) FAA delegated token 機制(已實作)白做 |
|
||
| 排除原因 | **流量成本**;FAA 已具備 delegated token,不用浪費 |
|
||
|
||
### 方案 C:Upload + Download 都走 backend 中轉(對稱設計)
|
||
|
||
| 評估 | 內容 |
|
||
|------|------|
|
||
| 排除原因 | 同方案 B 的流量成本問題 |
|
||
|
||
### 方案 D:擴 `model.Model` schema 加轉檔狀態
|
||
|
||
| 評估 | 內容 |
|
||
|------|------|
|
||
| 排除原因 | (1) 違反 SRP — model 應該只代表「已就緒可載入 device 的模型」;(2) job 狀態屬於 conversion 領域,不該污染 model 領域;(3) `model.Model.Source="converted" + SourceJobID` 已足夠表達來源關聯 |
|
||
|
||
## 後果 (Consequences)
|
||
|
||
### 正面影響
|
||
|
||
- **converter 零修改**:沿用既有 multipart endpoint
|
||
- **user_id 信任邊界乾淨**:visionA-backend 是唯一灌入點,從 OIDC cookie session 拿,不可被偽造
|
||
- **流量成本最佳**:upload 1× / download N× 的不對稱反映物流現實
|
||
- **Service token cache 可重用**:之後接 MC 其他 API(user 組織查詢 / push 通知)零成本擴展
|
||
- **不破壞既有 model store**:沿用 `/api/models/init+finalize`,conversion 只是「來源不同」
|
||
|
||
### 負面影響(接受的取捨)
|
||
|
||
- **visionA-backend 多一塊 streaming proxy 責任**:要寫好 `io.Pipe` + multipart streaming + context cancellation;錯誤處理複雜
|
||
- **跨網路依賴增加**:visionA-backend 失能 → 轉檔功能整個壞;MC 失能 → token 無法簽,轉檔不可用
|
||
- **MVP 不做進度推送**:user upload 完看 converter polling status,沒 SSE → UX 較粗(PRD Phase 0.8 接受)
|
||
- **Service token 集中失敗**:所有 4 個 scope 共用一個 cache,token 失效會同時影響轉檔與下載(MVP 階段可接受;後續可拆 cache)
|
||
- **取消 job 不做**:user 一旦 init 就要等到 converter 自己跑完或 timeout(converter 端 expires_at 7 天)
|
||
|
||
### 風險
|
||
|
||
| 風險 | 緩解 |
|
||
|------|------|
|
||
| visionA-backend 處理 500MB upload 時記憶體爆 | 嚴格 streaming(io.Pipe,不暫存);上線前壓測 1 個 + 2 個併發 upload;若有問題降到 200MB cap |
|
||
| Service token endpoint 被打爆(過度頻繁取 token)| token cache 確保 exp - 15s 內只取一次;log 記每次 cache miss |
|
||
| FAA CORS 還沒加 | **不再阻擋**:採用 server-side 302 redirect 後,browser navigation 不適用 CORS。Phase 1+ 若要改 fetch + Blob + a.click() 才需要 CORS(例如要顯示下載進度條) |
|
||
| MC `usage=webhook_outbound` 命名不對(同 ADR-010)| 不影響 visionA 程式碼;MC 改 `web_app` 後只需改 admin 註冊欄位 |
|
||
| converter 在 visionA 上 AWS 後不可達(網路) | Phase 0.8 範圍:visionA stage 仍可走 VPN 到 192.168.0.130;prod 上線前需 converter 也上 AWS 或開 VPN tunnel — 列入 prod 上線 blocker |
|
||
| 同 user 多 tab 各 init 一個 job → converter 409 | frontend 在 init 前先打 visionA backend 查當前 user 有無 active job;backend 直接拒絕第二次 init(不打到 converter)|
|
||
|
||
## 合規性
|
||
|
||
- [x] 與 PM Agent 確認:對齊 PRD Phase 0.8 範圍(半自動 / 模型 ≤500MB / ref_images ≤100×10MB / 同 user 1 active job / 不做歷史/取消/SSE/多 chip)
|
||
- [x] 與 Architect 確認:模組切分(`internal/conversion/`)、不擴 model schema、沿用 `/api/models/init+finalize`
|
||
- [x] 使用者裁決:upload 走 backend、download 走 delegated、半自動分流、不擴 schema
|
||
- [ ] DevOps 待確認:visionA stage → 192.168.0.130 的網路可達性(VPN / 直通)
|
||
- [x] FAA CORS:Phase 0.8 採 server-side 302 redirect,**不需要** CORS 設定(仿 FAA TestSite `DownloadFileDirect` pattern)
|
||
- [ ] MC 待確認:service client `23605e14a2c64660abd97e29963d8d58` 已授權 4 個 scope
|
||
|
||
## 相關文件
|
||
|
||
- 上位:`prd.md`(Phase 0.8 轉檔功能 PRD,PM 領地)
|
||
- 同層:`adr-006-no-redis-in-prototype.md`(in-memory token cache 沿用)、`adr-010-oidc-bff.md`(OIDC BFF)、`adr-011-supersede-adr-005.md`、`adr-013-public-client.md`(service client 仍為 confidential)
|
||
- 詳細實作:`conversion.md`(本 ADR 實作 spec)、`api/api-conversion.md`(對 frontend 的 API 規格)
|
||
- 安全:`security.md` §service-to-service token 流程(本次新增)
|
||
- 跨團隊整合:`/Users/jimchen/kneron_model_converter/docs/TODO-visionA-integration.md`
|
||
|
||
## 版本記錄
|
||
|
||
| 日期 | 版本 | 變更 |
|
||
|------|------|------|
|
||
| 2026-04-30 | 1.0 | 初版 — Phase 0.8 轉檔整合架構決策 |
|
||
| 2026-04-30 | 1.1 | Download flow 改為 server-side HTTP 302 redirect(仿 FAA TestSite `DownloadFileDirect`),token 不過 frontend JS、不需 FAA CORS |
|
||
| 2026-05-16 | 1.2 | §2 download flow 整段標 supersede by [ADR-016](./adr-016-download-via-converter.md):致命發現 MC source 沒有 issue / validate delegated download token endpoint、§2 從 2026-05-02 起即為 broken design。新設計 visionA download 改走 converter `GET /api/v1/jobs/{id}/result` + visionA stream 中轉。其他段落(§1 upload streaming proxy / §3 半自動分流 / §4 模組劃分 / §6 user_id trust boundary / §8 active job 衝突處理)維持有效;§5 / §7 中 FAA / MC 相關部分連帶 supersede。 |
|