致命發現(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>
22 KiB
API — Conversion(轉檔功能,Phase 0.8 / Phase 0.8b v0.6)
base URL:
https://stage-9527.innovedus.com:9527/(stage) /http://localhost:3721(dev) Auth(user → visionA):OIDC cookie session(visiona_session),參見oidc-tdd.md— 與 Phase 0.8 完全一致,未變 服務間認證(visionA → converter):Phase 0.8b v1.0 改為 pre-shared API key(取代 OAuth client_credentials)— 對 frontend 透明;v0.6 新增同一把 key 也用於 download 路徑(converterGET /api/v1/jobs/{id}/result)。詳見conversion.md§3.1 + ADR-015 §1 服務間認證(visionA → FAA / MC):Phase 0.8b v0.6 整段撤回(v1.x 加的 API key 撤回 / v2.0 改回 MC service token + delegated download token 也撤回)— visionA 端不再有任何 visionA → FAA / visionA → MC 路徑。download 改走 converter 中轉(converter 從自己的 MinIO stream NEF 回 visionA)。詳見 ADR-016 +conversion.md§3 同層:api/api-spec.md(總覽)、conversion.md(內部設計)、adr/adr-014-conversion-integration.md(§2 被 ADR-016 supersede;§1 upload / §3 半自動分流原則 / §4 模組劃分 / §6 user_id trust boundary 仍有效)、adr/adr-015-server-to-server-api-key.mdv2.1(§2 被 ADR-016 supersede;§1 visionA → converter API key 仍有效)、adr/adr-016-download-via-converter.md(v0.6 上位) 角色:給 visionA-frontend 實作時的 API 契約
通用約定
| 項目 | 值 |
|---|---|
| 通用回應格式 | { "success": true, "data": {...} } / { "success": false, "error": {code, message, details?} } |
| Auth | 走 cookie;frontend 用 credentials: "include" |
| Request ID | header X-Request-Id(visionA-backend 沒收到會自動產生) |
| Content-Type | 除 init 用 multipart/form-data 外,其他 JSON |
1. POST /api/conversion/init
啟動轉檔 job — 把 multipart body streaming proxy 到 converter。
Request
POST /api/conversion/init HTTP/1.1
Cookie: visiona_session=...
Content-Type: multipart/form-data; boundary=----xyz
multipart fields(注意:不要帶 user_id,backend 會從 cookie 灌):
| Field | Type | 必填 | 說明 |
|---|---|---|---|
model |
file | ✓ | .onnx / .tflite,≤ 500MB |
ref_images[] |
file × N | — | 可 0–100 張,每張 ≤ 10MB |
model_id |
text | ✓ | 1–65535,使用者自訂編號(converter 要求) |
version |
text | ✓ | 例 v1.0.0 |
platform |
text | ✓ | 520 / 720 |
enable_evaluate |
text | — | true/false,預設 false |
enable_sim_fp |
text | — | 同上 |
enable_sim_fixed |
text | — | 同上 |
enable_sim_hw |
text | — | 同上 |
Response 200
{
"success": true,
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "running",
"stage": "onnx",
"progress": 0,
"stage_progress": 0,
"created_at": "2026-04-30T12:00:00Z",
"expires_at": "2026-05-07T12:00:00Z"
}
}
expires_at=created_at + 7d(converter 7 天 GC 截止時間)。frontend 用於顯示倒數與切「已過期」狀態。詳見conversion.md§2.6.2。
錯誤
| HTTP | code | 來源 | 處理建議 |
|---|---|---|---|
| 400 | validation_failed |
converter | 顯示 details.fields |
| 401 | unauthorized |
visionA | redirect /login |
| 409 | active_job_exists |
visionA pre-check / converter | 顯示「你已有進行中任務」+ details.job |
| 413 | payload_too_large |
converter | 提示檔案大小限制 |
| 502 | converter_unavailable |
visionA | 提示「轉檔服務暫時無法使用」+ 重試按鈕 |
| 502 | converter_auth_failed |
visionA(Phase 0.8b v1.0 新增;v2.0 維持) | 同上文字 — frontend 看不出差別;SRE 從 log 排查 converter API key 同步 |
| 503 | service_busy |
converter | 提示稍後重試 |
2. GET /api/conversion/{job_id}
查 job 狀態。Frontend 用 polling,建議間隔 2 秒。
Response 200
{
"success": true,
"data": {
"job_id": "550e8400-...",
"status": "running",
"stage": "bie",
"progress": 45,
"stage_progress": 60,
"created_at": "2026-04-30T12:00:00Z",
"updated_at": "2026-04-30T12:05:30Z",
"expires_at": "2026-05-07T12:00:00Z",
"source_filename": "yolov5s.onnx",
"target_chip": "720",
"error_code": null,
"error_message": null
}
}
status enum:created / running / completed / failed
stage enum:onnx / bie / nef
| 欄位 | 用途 |
|---|---|
expires_at |
created_at + 7d,frontend 顯示倒數 |
source_filename |
原始檔名(顯示用,例 wireframe success card 「yolov5s.onnx → yolov5s_kl720.nef」) |
target_chip |
從 init 時的 platform 欄回傳(520 / 720 / 630 / 730) |
錯誤
| HTTP | code | 處理 |
|---|---|---|
| 403 | forbidden |
job 不屬於當前 user |
| 404 | not_found |
job_id 不存在 / 已過期 |
| 502 | converter_unavailable |
持續失敗 → 提示重試 |
Polling 建議
- Frontend 收到
status=running→ 2s 後再 poll status=completed/failed→ 停止 polling- 連續 5 次 5xx → 停止 polling 並顯示錯誤
3. POST /api/conversion/{job_id}/promote-to-models
「加到模型庫」 — 完整流程:promote → FAA pull → 寫進 visionA model store。
Request
POST /api/conversion/{job_id}/promote-to-models
Content-Type: application/json
{
"name": "yolov5s_kl720"
}
| Field | 必填 | 說明 |
|---|---|---|
name |
✓ | 在 model 庫顯示的名字。Design Phase 0.8 wireframe §7.1 要求此欄位,預設 {job.source_filename_stem}_{target_chip.lower()} |
description |
— | (Phase 0.8 不送,留 Phase 1)— backend 接受但忽略;Phase 1 開放 |
與 Design 對齊(議題 #4):Phase 0.8 wireframe §7.1 的 import Dialog 只有名稱欄位(不含描述);backend Phase 0.8 也只用
name,description雖在 schema 內但不顯示給使用者填寫。Phase 1 Design 開放描述欄位時 backend 已 ready,無需改 API。
Response 201
{
"success": true,
"data": {
"model_id": "abc-123",
"source": "converted",
"source_job_id": "550e8400-...",
"name": "YOLOv5 Face KL520",
"target_chip": "kl520",
"file_size": 12345678,
"status": "ready",
"created_at": "2026-04-30T12:30:00Z"
}
}
格式註記:這個 response 是既有
internal/model.Modelschema(沿用),其target_chip用"kl520"小寫格式。 跟 §2 / §5 conversion job 的target_chip用"720"(converterplatformenum)不同欄位、不同來源:
- conversion job:來自 converter scheduler 的
platform欄位("520"/"630"/"720"/"730")- model.target_chip:visionA 既有 model schema(
"kl520"/"kl720"/ etc)visionA-frontend 統一 normalize 成 UI 內部形式
KL520/KL720顯示(見lib/api/conversion.tsnormalizeTargetChip)。 Phase 1 評估是否值得在 backend 把兩邊統一(可能影響既有 model store 多處 caller,動範圍大)。
錯誤
| HTTP | code | 處理 |
|---|---|---|
| 403 | forbidden |
不是該 user 的 job |
| 404 | not_found |
job_id 不存在(visionA ownership 找不到、或 converter 端 GET result 回 404 result_not_found) |
| 409 | job_not_completed |
job 還沒 completed,不能 promote |
| 410 | result_expired |
v0.6 新增:job completed 但 converter MinIO 內 NEF 已過 7 天 expires_at 被 GC,無法 promote |
| 502 | converter_unavailable |
promote 失敗 / converter GET result 5xx,可重試 |
| 502 | converter_auth_failed |
converter API key 不同步(運維事件) |
冪等性:對同一 job_id 重複呼叫;若已建過 model record,回 200 + 既有 model 詳情(不重新建)。
v0.6 註:撤回 v0.5 的
mc_token_unavailable/download_token_failed/faa_unavailable三個 code(v0.5 規劃要回收給 FAA 線用)。visionA 端 v0.6 不再有 visionA → MC / visionA → FAA 直接呼叫;promote 內部 NEF pull 改走converter.GetResult,失敗模式收斂為converter_unavailable/converter_auth_failed/result_not_found/result_expired。converter → FAA(promote 時 converter 內部 PUT)是 converter 自己的事、與 visionA 無關(converter 端失敗會在 promote response 中體現為 5xx)。
4. GET /api/conversion/{job_id}/download
「下載」 — Phase 0.8b v0.6:visionA-backend server-side stream proxy from converter GET /api/v1/jobs/{id}/result。流程演進:
- Phase 0.8 (ADR-014):「302 redirect to FAA delegated URL」(browser 直連 FAA)
- Phase 0.8b v0.4 (ADR-015 v1.x):server-side stream proxy(從 FAA pull NEF),token 用 visionA API key
- Phase 0.8b v0.5 (ADR-015 v2.0):保留 server-side stream proxy;token 來源改回 MC delegated download token(對 MC source 驗證後確認設計 fictional、未實際 e2e 跑通)
- Phase 0.8b v0.6 (ADR-016):保留 server-side stream proxy;stream 來源從 FAA 改 converter
GET /api/v1/jobs/{id}/result;visionA 端不再經 MC / FAA
詳見 ADR-016、conversion.md §3 / §4.1。
對 frontend 而言呼叫方式完全一致(<a href> / window.location.href),response 仍是「200 + NEF binary stream + Content-Disposition: attachment」(與 v0.4 / v0.5 一致;只是 visionA backend 內部抓 stream 的下游 endpoint 改變,frontend 無感)。
Request
GET /api/conversion/{job_id}/download HTTP/1.1
Cookie: visiona_session=...
無 query string、無 body。
Response 200(成功 — Phase 0.8b 變更)
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 12345678
Content-Disposition: attachment; filename="yolov5s_kl720.nef"
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
<NEF binary stream...>
browser 收到 Content-Disposition: attachment 自動觸發下載對話框 / 直接存到 Downloads。
Frontend 使用方式(與 Phase 0.8 完全一致)
<!-- 推薦:anchor tag,browser 自動處理 attachment download -->
<a href={`/api/conversion/${jobId}/download`} download>下載</a>
或:
// 程式化觸發
window.location.href = `/api/conversion/${jobId}/download`;
Frontend 不需處理 token、不需處理 redirect;Content-Disposition: attachment 觸發 browser 原生 download 行為。
Phase 0.8b 不需要 FAA CORS:visionA backend → FAA 是 server-side 同進程 outbound HTTP call,完全不適用 CORS(CORS 只管 browser JS fetch / XHR)。同源 endpoint + server-side stream + attachment header = 無 CORS 議題。
錯誤(依 Accept header 回 JSON 或 HTML 錯誤頁)
| HTTP | code | 處理 |
|---|---|---|
| 401 | unauthorized |
沒登入;redirect /login(前端攔截) |
| 403 | forbidden |
不是該 user 的 job |
| 404 | not_found |
job_id 不存在 / 已過期(visionA ownership 找不到,或 converter GET result 回 404 result_not_found) |
| 409 | job_not_completed |
job 還沒 completed,不能下載 |
| 410 | result_expired |
v0.6 新增:job completed 但 converter MinIO 內 NEF 已過 7 天 expires_at 被 GC;frontend 顯示「轉檔結果已過期,請重新轉檔」並提供重新轉檔 CTA |
| 502 | converter_unavailable |
promote 失敗(首次下載且尚未 promote 過時可能發生)/ converter GET result 5xx |
| 502 | converter_auth_failed |
converter API key 不同步(運維事件,frontend 不需區分) |
錯誤回應格式:依 Accept header:
Accept: application/json→{success:false, error:{code, message}}Accept: text/html(一般 anchor 觸發) → HTML 錯誤頁;browser 直接顯示
注意:
- 每次「下載」按鈕都直接打
/downloadendpoint,不要前端 cache 任何中間狀態 - Phase 0.8b(v0.4 / v0.5 / v0.6)server-side proxy 模式下,visionA backend 變 streaming bottleneck — Phase 1+ 量大評估升級(converter Phase 2 download-tokens 讓 browser 直連 converter;或 FAA HMAC token)
- 不會與
promote-to-models衝突;兩者內部都會 ensurePromoted(冪等),兩條路徑都從 converterGET result拉同一份 NEF stream - v0.6 後
promote-to-models也走converter.GetResult(與 download 共用同一條 path;不再有 delegated token / FAA pull 任何概念)
v0.6 註:撤回 v0.5 加的
mc_token_unavailable/download_token_failed/faa_unavailable三個 code。對 frontend 文字仍維持一致(轉檔服務暫時無法使用),用converter_unavailable統一表達;result_expired(410)是新增的明確 case,給 frontend 「過期」精確提示。
5. GET /api/conversion/active
查當前 user 是否有 active job — 給 frontend 在跳出「上傳」UI 前 pre-check。
Response 200(有 active)
{
"success": true,
"data": {
"has_active": true,
"job": {
"job_id": "550e8400-...",
"status": "running",
"stage": "bie",
"progress": 45,
"created_at": "2026-04-30T12:00:00Z",
"expires_at": "2026-05-07T12:00:00Z",
"source_filename": "yolov5s.onnx",
"target_chip": "720"
}
}
}
此 endpoint 與
GET /api/conversion/{job_id}回傳同一個Jobshape;wireframe §3.3、flow-conversion.md §5.1 依賴此 shape 做「進入頁面就直接落 processing 畫面」的恢復邏輯。
重啟恢復行為(Phase 0.8 強化):當 visionA-backend 重啟導致 in-memory ownership 丟失時,此 endpoint 會 fallback 對 converter 查 GET /api/v1/jobs?user_id=<sub>&status=in_progress 並重建 ownership(lazy rebuild)。對 frontend 完全透明(同樣 endpoint、同樣 response shape)。詳見 conversion.md §2.6.1。
Response 200(無 active)
{
"success": true,
"data": {
"has_active": false,
"job": null
}
}
用法
Frontend 在「轉檔」入口的 /conversion 頁載入時打這個 endpoint:
has_active=true→ 顯示「你目前有進行中的任務」+ 跳轉到該 job 的進度頁has_active=false→ 顯示上傳表單
錯誤碼總覽
對齊 conversion.md §6。前端 i18n key 統一 conversion.error.<short-name>。
Phase 0.8b v0.6 變更:撤回 v0.5「回收 MC 相關 code」決定。visionA 端不再有任何 visionA → MC / visionA → FAA 直接呼叫,
mc_token_unavailable/download_token_failed/faa_unavailable三個 code 全部移除。新增result_not_found(404)與result_expired(410)對應 converterGET resultendpoint 的新失敗模式。
| code | HTTP | i18n key | 預設訊息(zh-TW) |
|---|---|---|---|
validation_failed |
400 | conversion.error.validation |
上傳的內容不符合要求 |
unauthorized |
401 | common.error.unauthorized |
請先登入 |
forbidden |
403 | conversion.error.forbidden |
你無權存取此任務 |
not_found |
404 | conversion.error.not_found |
任務不存在 |
result_not_found |
404 | conversion.error.not_found |
任務不存在(v0.6 新增:converter GET result 回 404;i18n 與 not_found 共用文字、SRE 從 log 區分) |
active_job_exists |
409 | conversion.error.active_job |
你目前已有進行中的轉檔任務 |
job_not_completed |
409 | conversion.error.not_completed |
任務尚未完成(promote-to-models 與 download 共用) |
result_expired |
410 | conversion.error.result_expired |
轉檔結果已過期,請重新轉檔(v0.6 新增:converter GET result 回 410;frontend 顯示重新轉檔 CTA) |
payload_too_large |
413 | conversion.error.too_large |
檔案超過大小限制 |
converter_unavailable |
502 | conversion.error.converter_down |
轉檔服務暫時無法使用 |
converter_auth_failed |
502 | conversion.error.converter_down |
轉檔服務暫時無法使用(v1.0 新增;v2.0 / v0.6 維持 — frontend 文字同 converter_unavailable) |
service_busy |
503 | conversion.error.busy |
系統繁忙,請稍後再試 |
Phase 0.8b v0.6 撤回(v0.5 加的、v0.6 移除):
| 已撤回 | 原 HTTP | 原語意(v0.5) | v0.6 替代 |
|---|---|---|---|
mc_token_unavailable |
502 | MC /oauth/token 4xx / 5xx |
不需要(visionA 端不再打 MC) |
download_token_failed |
502 | MC /file-access/download-tokens 4xx / 5xx |
不需要(visionA 端不再 issue delegated token) |
faa_unavailable |
502 | FAA pull 失敗 | 不需要(visionA 端不再直接打 FAA;converter GET result 失敗統一收斂進 converter_unavailable) |
Phase 0.8b v0.4 → v0.5 → v0.6 演進摘要:
| code | v0.4 狀態 | v0.5 狀態 | v0.6 狀態 |
|---|---|---|---|
converter_auth_failed |
新增 | 維持 | 維持(同一把 key 也用於 GetResult) |
converter_unavailable |
維持 | 維持 | 維持(含 GET result 5xx 收斂) |
result_not_found |
— | — | 新增(converter GET result 404) |
result_expired |
— | — | 新增(converter GET result 410) |
faa_auth_failed |
新增 | 撤回 | 維持撤回 |
faa_unavailable |
使用中 | 使用中 | 撤回 |
mc_token_unavailable |
移除 | 回收 | 撤回 |
download_token_failed |
移除 | 回收 | 撤回 |
idp_misconfigured |
移除 | 維持移除 | 維持移除 |
idp_unavailable |
移除 | 維持移除 | 維持移除 |
frontend i18n 字典:
- v0.6 後
conversion.error.faa_downi18n key 可保留但不再被任何 code 引用(向下相容;舊版 backend 不會發faa_*/mc_*三個 code) - 新增 i18n key:
conversion.error.result_expired—「轉檔結果已過期,請重新轉檔」(給 410 用、與其他 502 文字明確區分) result_not_found與not_found共用conversion.error.not_foundi18n key(user 角度兩者等價:任務不存在)
版本記錄
| 日期 | 版本 | 變更 |
|---|---|---|
| 2026-04-30 | 0.1 | 初稿(Phase 0.8 MVP 範圍) |
| 2026-04-30 | 0.2 | §4 download endpoint 從 POST /{job}/download-token(回 JSON {download_url, expires_at})改為 GET /{job}/download(HTTP 302 redirect),仿 FAA TestSite DownloadFileDirect pattern;token 不過 frontend JS、不需 FAA CORS;job_not_completed HTTP code 從 400 改為 409 + 補 mc_token_unavailable |
| 2026-04-30 | 0.3 | Phase 0.8 三方交叉審閱回饋整合:Job response shape 補 expires_at / source_filename / target_chip(議題 #7);/api/conversion/active 行為文件化 lazy rebuild 機制(議題 #2 重啟恢復);promote-to-models request body 對齊 Design 單欄位(議題 #4,description 留 Phase 1) |
| 2026-05-11 | 0.4 | Phase 0.8b 對應 ADR-015:(1) Header / 文件 metadata 標示服務間認證改 pre-shared API key(對 frontend 透明);(2) §4 download endpoint response 從「302 Location」改為「200 + NEF binary + Content-Disposition: attachment」— frontend 呼叫方式(<a href> / window.location.href)完全一致;(3) 錯誤碼總覽:移除 4 個 MC 相關 code(idp_misconfigured / idp_unavailable / download_token_failed / mc_token_unavailable),新增 2 個 *_auth_failed(converter_auth_failed / faa_auth_failed)對應 API key 不同步的運維事件 |
| 2026-05-16 | 0.5 | 對應 ADR-015 v2.0 範圍縮限:撤回 v0.4「visionA → FAA 改 API key」;FAA 線回到 ADR-014 §2 原設計(MC service token + delegated download token);visionA → converter API key 路線(v0.4)維持。主要變更:(1) Header metadata 區分 converter 線 / FAA 線兩條 server-to-server 認證;(2) §3 promote-to-models 與 §4 download endpoint 錯誤碼回收 mc_token_unavailable / download_token_failed,撤回 v0.4 加的 faa_auth_failed;(3) 錯誤碼總覽表更新對應;(4) §4 download endpoint 描述更新「v0.4 server-side proxy + v0.5 token 來源改回 delegated download token」演進。Frontend 行為對 user 完全透明:response shape / call pattern / 錯誤文字一律不變;只是 visionA backend 內部 token 來源演進。 |
| 2026-05-16 | 0.6 | 對應 ADR-016:撤回 v0.5「visionA → FAA 改回 MC service token + delegated download token」全部規劃。原因:對 MC source 全 grep 驗證後確認 MC 沒有 POST /file-access/download-tokens endpoint、也沒有 FAA IDelegatedDownloadTokenValidator assume 的 introspection endpoint—— v0.5 設計是 fictional。v0.6 新設計:visionA download 改走 converter 新增的 GET /api/v1/jobs/{id}/result + visionA stream 中轉;visionA 端不再有任何 visionA → FAA / visionA → MC 路徑、server-to-server 認證收斂為單條 visionA → converter(API key)。主要變更:(1) Header metadata 區段改寫「服務間認證(visionA → FAA / MC)」段落,整段撤回;(2) §3 promote-to-models 錯誤表移除 mc_token_unavailable / download_token_failed / faa_unavailable,新增 result_expired(410);(3) §4 download endpoint 描述更新 v0.6 演進、錯誤表同樣移除 v0.5 加的 MC / FAA 三個 code、新增 result_expired;(4) 錯誤碼總覽表更新 — 新增 result_not_found(404) + result_expired(410)兩個 code,撤回 faa_unavailable / mc_token_unavailable / download_token_failed;(5) v0.4 → v0.5 → v0.6 演進摘要表加 v0.6 column;(6) i18n 字典補 conversion.error.result_expired 新 key。Frontend 行為對 user 仍完全透明(response shape / call pattern 不變;只是 visionA backend 內部 stream 來源從 FAA 改為 converter)。新增 frontend 需處理:410 result_expired 顯示「請重新轉檔」CTA(i18n key conversion.error.result_expired)。 |