依 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)。
481 lines
26 KiB
Markdown
481 lines
26 KiB
Markdown
# Phase 0.8 三方交叉審閱 — Architect 視角
|
||
|
||
> **作者**:Architect Agent
|
||
> **日期**:2026-04-30
|
||
> **審閱對象**:PM PRD(`feature-converter-integration.md`)、Design wireframe(`wireframe-conversion.md`)、Design flow(`flow-conversion.md`)
|
||
> **對照基準**:Architect ADR-014 v1.1、`conversion.md` v0.3、`api/api-conversion.md` v0.3
|
||
|
||
---
|
||
|
||
## 摘要
|
||
|
||
| 對象 | 嚴重 | 中 | 小 / nit |
|
||
|------|-----|----|---------|
|
||
| PM PRD | 1 | 4 | 2 |
|
||
| Design wireframe | 1 | 2 | 2 |
|
||
| Design flow | 1 | 1 | 1 |
|
||
|
||
> **嚴重 = 直接導致實作有歧義或 frontend / backend 對不上**;中 = 文件不精確但可推測;小 = 命名 / 措辭。
|
||
|
||
**Architect 自己的 TDD 修訂**:4 個章節(議題 #2 / #5 / #6 / #7)已寫進 `conversion.md` v0.3 + `api/api-conversion.md` v0.3。詳見 §3。
|
||
|
||
**需要使用者裁決的事項**:1 件(API endpoint 路徑命名 — `/api/conversion/*` vs `/api/converter/jobs/*`,目前三方文件不一致)。
|
||
|
||
---
|
||
|
||
## 1. 對 PM PRD 的審閱(`02-prd/features/feature-converter-integration.md`)
|
||
|
||
### 1.1 ✅ 同意的部分
|
||
|
||
- **§1 概要 / §2 User Stories**:MVP 範圍清楚,半自動設計理由明確
|
||
- **§3 F1–F5 的功能定義**:與我 TDD 的 endpoint 行為對齊(status enum、5–10s polling、500MB cap、ref images 上限、translated error messages)
|
||
- **§4 非功能需求**:500MB / 10MB / 100 張上限、polling 間隔、active job 限制 — 全部已在 TDD §4 / §9 / §10 cover
|
||
- **§5 Non-Goals**:完整且符合 MVP 邊界(不做歷史 / 取消 UI / SSE / 多 chip)
|
||
- **§6 整合決策 D1–D6**:與 ADR-014 完全一致(streaming proxy / FAA 直連 / 半自動 / polling / converter API 不動 / sidebar 獨立 tab)
|
||
- **§7 Dependency 一覽**:清楚標出 converter / FAA / MC / visionA-backend / visionA-frontend 各自要做什麼
|
||
- **§9 驗收條件**:可測、與 TDD 行為對齊
|
||
- **§11 給 Architect 的注意事項**:streaming proxy 用 `io.Pipe` 不 buffer 全 RAM、user-scoped 授權、idempotent promote — 全部已落地
|
||
|
||
### 1.2 ⚠️ 嚴重 — API endpoint 路徑命名與 TDD 不一致
|
||
|
||
**位置**:PRD §F6 / §F7
|
||
|
||
```
|
||
PRD §F6:POST /api/converter/jobs/{id}/import-to-models
|
||
PRD §F7:POST /api/converter/jobs/{id}/download-token
|
||
|
||
Wireframe §3.3 / Flow §3:GET /api/converter/jobs/active
|
||
Wireframe §6.3:GET /api/converter/jobs/{id}(polling)
|
||
|
||
TDD(Architect):
|
||
POST /api/conversion/init
|
||
GET /api/conversion/{job_id}
|
||
POST /api/conversion/{job_id}/promote-to-models
|
||
GET /api/conversion/{job_id}/download ← 注意:是 GET、不是 POST `/download-token`
|
||
GET /api/conversion/active
|
||
```
|
||
|
||
**三個衝突點**:
|
||
|
||
1. **path prefix**:`/api/converter/jobs/*`(PM/Design)vs `/api/conversion/*`(Architect)
|
||
2. **action 命名**:`import-to-models`(PM)vs `promote-to-models`(Architect)
|
||
3. **download endpoint method + 形式**:PRD/Design 假設 `POST /download-token` 回 `{url, expires_at}` JSON;TDD 已改為 `GET /download` server-side 302 redirect(ADR-014 v1.1 議題 #9 已決議的安全升級)
|
||
|
||
**建議改法**:
|
||
|
||
採 Architect 的版本(`/api/conversion/*` + `promote-to-models` + `GET /download` 302)— 理由:
|
||
- 路徑前綴 `/conversion/` 對應 visionA 內部模組名(`internal/conversion/`),命名一致
|
||
- `/converter/jobs/*` 容易讓人誤以為是「轉發給 converter 的 raw API」(其實 visionA backend 有自己的 ownership / 半自動邏輯,不是純 proxy)
|
||
- `promote-to-models` 對齊 converter 的 `/promote` 動詞、語意精確
|
||
- `GET /download` + 302 已在 ADR-014 v1.1 完成決策(安全考量壓倒 UX 微差)
|
||
|
||
**請 PM 修**(新增到 §1.3 PM 修訂建議清單):
|
||
- §F6 endpoint 改 `POST /api/conversion/{job_id}/promote-to-models`
|
||
- §F7 endpoint 改 `GET /api/conversion/{job_id}/download`(server 302 redirect)+ 移除「visionA backend 跟 Member Center 換 delegated download token」的描述(這仍正確但藏在 server-side),把「回傳 `{url, expires_at}`」改為「browser 自動 follow 302 直連 FAA」
|
||
|
||
**請 Design 修**(同節 §2.2):
|
||
- wireframe §3.3、wireframe §6.3、flow §3 / §5.5 / §8.2 全部 `/api/converter/jobs/*` → `/api/conversion/*`
|
||
- flow §3 sequence diagram 的 download 那段 `POST /api/converter/jobs/{id}/download-token` + `200 { url, expires_at }` → `GET /api/conversion/{id}/download` + `302 Location: faa-url`
|
||
- wireframe §7.2「下載」流程描述:移除「拿到 `{ url, expires_at }`」,改為「按鈕觸發 anchor click 或 `window.location.href = '/api/conversion/{id}/download'`,browser 自動處理 302」
|
||
|
||
### 1.3 ⚠️ 中 — Active job 端點未列入 PRD
|
||
|
||
**位置**:PRD §F 段(功能需求)
|
||
|
||
PRD §F1–F7 沒有列出「Active job pre-check」這個功能,但 §3 F3 隱含提到「同 user 同時只能跑一個 active job」。Design wireframe §3.3 與 flow §5.1 明確使用 `GET /api/converter/jobs/active`(建議改名 `/api/conversion/active`)做 idle / 重新整理 / 多分頁的恢復邏輯。
|
||
|
||
**建議**:PRD §F 段補一條 F0:
|
||
|
||
```markdown
|
||
### F0:進入「轉檔」頁面時的 active job 偵測
|
||
|
||
- 進入 /conversion 時前端打 GET /api/conversion/active
|
||
- 後端回應 `{ has_active: bool, job? }`
|
||
- has_active=true → 直接顯示「進行中」畫面(同 F3)+ banner「您離開前的轉檔仍在進行中」
|
||
- has_active=false → 顯示空狀態 + CTA
|
||
```
|
||
|
||
理由:這個端點是 wireframe 的核心(§3.3、§6.4 多分頁同步、§6.6 重啟頁面恢復),在 PRD 沒列就成了「設計依賴的隱形 contract」,三方對齊時容易遺漏。
|
||
|
||
### 1.4 ⚠️ 中 — Streaming upload 進度語意未明示
|
||
|
||
**位置**:PRD §F2「上傳行為」、§F3「轉檔執行與進度」
|
||
|
||
PRD §F2 寫「使用者看到的是『上傳到 visionA』的單一進度條(XHR upload event)」— 這裡有歧義:
|
||
|
||
- **語意 A**:XHR `upload.onprogress` 顯示 browser → backend 的進度。當 browser send 完 100% 後,backend 還在 forward 給 converter 的階段,前端顯示什麼?
|
||
- **語意 B**:「上傳到 visionA」= browser → backend 的單一進度,使用者看到 100% 就視為完成
|
||
|
||
實作上若採 B,則 backend 需要 background goroutine + 額外 ownership 狀態(`upload_in_progress`)才能 early-return;採 A 則 backend 等 converter 201 才回 200,前端進度顯示 100% 後到實際切 processing 之間有 1-3 秒「即將完成…」/「伺服器處理中…」。
|
||
|
||
**TDD 採用語意 A**(見 `conversion.md` §4.3.1 v0.3 新增)— 理由:
|
||
- 100% 接近端到端真實狀態,使用者不會被欺騙
|
||
- 實作簡單,失敗處理路徑清楚
|
||
- 1-3 秒延遲對 500MB 上傳體感影響極小
|
||
|
||
**建議 PM 補在 §F2 / §F3**:
|
||
|
||
```markdown
|
||
> **進度條語意說明**:上傳進度條顯示 browser → visionA-backend 的進度。當顯示 100% 但
|
||
> backend 還在轉發給 converter 時,文案會切為「即將完成…」/「伺服器處理中…」(1–3 秒),
|
||
> 然後才切到「轉檔進行中」畫面。對使用者誠實 — 不謊報已完成。
|
||
```
|
||
|
||
### 1.5 ⚠️ 中 — Promote-to-models request body 對齊 Design
|
||
|
||
**位置**:PRD §F6「加到模型庫」流程
|
||
|
||
PRD §F6 沒有提到「需要使用者輸入名稱」,但 Design wireframe §7.1 設計了 import Dialog 含名稱輸入欄。我這邊 API spec(v0.3)已對齊 Design 的「單欄位 name only」決議。
|
||
|
||
**建議 PM 補在 §F6**:
|
||
|
||
```markdown
|
||
- 點「加到模型庫」開確認 Dialog(單欄位):
|
||
- 模型名稱(預設 `{source_filename_stem}_{target_chip.lower()}`,可改)
|
||
- 描述欄位 Phase 1 才開放
|
||
- 確認後呼叫 POST /api/conversion/{id}/promote-to-models
|
||
```
|
||
|
||
或在 §F6 末尾加註:「Dialog UI 與欄位定義詳見設計規格 wireframe §7.1。」
|
||
|
||
### 1.6 ⚠️ 中 — visionA-backend 重啟後的 UX
|
||
|
||
**位置**:PRD §11 給 Architect 的注意事項 / 應該補在 §6 整合決策或 §F0
|
||
|
||
PRD 沒有討論 visionA-backend 重啟(部署、crash recovery)後使用者 UX 該怎樣。我 TDD §2.6.1 v0.3 補了 lazy rebuild 機制(A4 方案 — `/active` 端點 fallback 對 converter 查 `?user_id=&status=in_progress`),讓使用者重啟後仍能看到自己的 active job。
|
||
|
||
**這需要 converter Phase 1 提供 `GET /api/v1/jobs?user_id=&status=in_progress` endpoint**(converter Phase 1 已實作,但 PRD §7 dependency 表未列入「visionA 會用此 endpoint」)。
|
||
|
||
**建議 PM 補在 §7 Dependency 表**:
|
||
|
||
```markdown
|
||
| kneron_model_converter | ❌ 完全不用動 | 沿用既有 endpoint:`POST /api/v1/jobs`、`GET /api/v1/jobs/{id}`、`POST /promote`、`GET /api/v1/jobs?user_id=&status=in_progress`(visionA-backend 重啟後 lazy rebuild ownership 用)、`POST /api/v1/jobs/{id}/cancel`(visionA-backend 偵測 client disconnect 時內部 cleanup 用) |
|
||
```
|
||
|
||
**也建議在 §6 整合決策表加一條 D7**:
|
||
|
||
```markdown
|
||
| D7 | visionA-backend in-memory ownership 重啟遺失 | Phase 0.8 採 lazy rebuild(`/active` fallback 對 converter 查);Phase 0.9+ 評估 DB persist | converter 7 天 expires_at 兜底;UX 上使用者重進頁面仍看得到 active job |
|
||
```
|
||
|
||
### 1.7 小 — KPI「上傳到拿 NEF P95 < 10 分鐘」量測點
|
||
|
||
**位置**:PRD §8 KPI
|
||
|
||
PRD §8 寫「visionA backend 在 import / download 觸發點 log timestamp」— 但 import/download 是「使用者拿到 NEF 後的下一步」,不是「拿到 NEF 的當下」。建議改:
|
||
|
||
- 量測點 = converter status 從 `running` → `succeeded` 的當下時間 - `init` 收到 request 的時間
|
||
- 這是 backend 視角的端到端時間,不依賴使用者按按鈕
|
||
|
||
非阻擋議題,建議調整。
|
||
|
||
### 1.8 小 — 4 個 chip 數量
|
||
|
||
PRD §F2 寫「目標 chip:KL520 / KL630 / KL720 / KL730」(4 個),但 TDD `api-conversion.md` §1 與 converter `platform` 欄位的 enum 寫的是 `520 / 720`(2 個)。需確認:
|
||
|
||
- converter Phase 1 實際支援哪幾個 platform?
|
||
- 如果只支援 520 / 720,PRD 應改為 2 個(避免使用者選了 630/730 然後被 converter 拒絕)
|
||
- 如果 converter Phase 1 已擴充到 4 個,TDD `platform` enum 應改為 `520 / 630 / 720 / 730`
|
||
|
||
**建議 PM**:請與 converter 團隊確認 Phase 1 實際支援的 chip list,PRD 與 TDD 同步更新。
|
||
|
||
---
|
||
|
||
## 2. 對 Design 的審閱
|
||
|
||
### 2.1 對 Wireframe(`03-design/wireframes/wireframe-conversion.md`)
|
||
|
||
#### 2.1.1 ✅ 同意的部分
|
||
|
||
- **§0 設計對齊備註**:複用既有元件、不新增 Design Tokens、走 i18n — 全部對的方向
|
||
- **§1 Sidebar 進入點**:放模型庫之後,心智模型清楚;Wand2 icon 選擇合理
|
||
- **§2 頁面狀態總覽**:state 機切 4 種畫面,無 URL query state — 簡潔正確
|
||
- **§3.3 邊界 — 已有 active job**:用 `GET /jobs/active` 做進入恢復是正確的設計(解決多分頁 / 重新整理 / 重啟)
|
||
- **§4 Upload Dialog**:階段切換(select / uploading)合理;§4.4 上傳失敗的 4 種 case 完整
|
||
- **§5 主畫面 vs Dialog 進度**:分開顯示避免使用者困惑、tab title 動態更新
|
||
- **§6 Processing 畫面**:3-stage indicator 設計、indeterminate progress、polling 間隔(5s 前 60 秒、之後 10s)— 都與 TDD 對齊
|
||
- **§6.4 邊界情境**:4 種情境(關分頁、多分頁、長排隊、長執行)有 cover
|
||
- **§7 Success 結果**:兩按鈕互不互斥 / 過期倒數 / 「開始新轉檔」放外面 — 與 PRD §F4 對齊
|
||
- **§8 Failed 狀態 + suggestions**:依 error code 切換建議很棒
|
||
- **§8.2 Job 已過期**:對 TDD `expires_at` 機制依賴正確
|
||
- **§9 響應式 / §11 i18n / §12 無障礙**:全部到位
|
||
|
||
#### 2.1.2 ⚠️ 嚴重 — endpoint 路徑與 TDD 不一致
|
||
|
||
同 §1.2。請改 `/api/converter/jobs/*` → `/api/conversion/*`、`POST /download-token` 形式 → `GET /download` + 302 redirect。
|
||
|
||
具體要改的位置:
|
||
- §3.3 邊界表「`GET /api/converter/jobs/active`」 → `GET /api/conversion/active`
|
||
- §6.3 polling 表「端點 `GET /api/converter/jobs/{id}`」 → `GET /api/conversion/{id}`
|
||
- §7.1 「呼叫 POST /api/converter/jobs/{id}/import-to-models」 → `POST /api/conversion/{id}/promote-to-models`
|
||
- §7.2 整段流程:去掉 `POST /download-token` + 拿 `{ url, expires_at }` → 改為 anchor tag 觸發 `GET /api/conversion/{id}/download`,browser 自動 follow 302
|
||
|
||
§7.2 流程修改範例:
|
||
|
||
```diff
|
||
- 1. 點擊 → 按鈕進 loading 狀態(spinner + 「準備下載…」)
|
||
- 2. 呼叫 POST /api/converter/jobs/{id}/download-token
|
||
- 3. 200 OK:
|
||
- - 拿到 { url, expires_at }
|
||
- - 立即 window.location.href = url(瀏覽器內建下載管理器接手)
|
||
- - 按鈕回到原狀態(不變灰,使用者可重複下載)
|
||
+ 1. 點擊 → 觸發 anchor 行為 / window.location.href = '/api/conversion/{id}/download'
|
||
+ 2. visionA-backend server-side 302 → browser 自動 follow → 直連 FAA
|
||
+ 3. 按鈕不需 loading 狀態(navigation 由 browser 接管)
|
||
+ 若需要錯誤處理(例 job_not_completed),可改用 fetch + 檢 302 status
|
||
```
|
||
|
||
> **附註**:這個改動實際上「簡化」了 Design 的 §7.2 描述(不用處理 token、不用 fetch then redirect)— 是 ADR-014 v1.1 帶來的好處。
|
||
|
||
#### 2.1.3 ⚠️ 中 — Stage indicator 對應實際狀態
|
||
|
||
**位置**:wireframe §6.1 「Stage 對應」表
|
||
|
||
Design 把 converter 的 `running` 狀態切成兩個視覺 stage(「解析模型」、「編譯 NEF」),純粹是 UI 上的分段,不對應 converter 內部真實階段。
|
||
|
||
**問題**:converter Phase 1 的 `GET /api/v1/jobs/{id}` response 其實有 `stage` 欄位(`onnx` / `bie` / `nef`,見 api-conversion.md §2 Job response),這比 wireframe 假設的「單一 running 狀態」更精細。
|
||
|
||
**建議**:
|
||
|
||
- **選項 A**:wireframe 的 3-stage 對應到 converter `stage` 欄位(`onnx` → 解析、`bie` → 量化、`nef` → 編譯),這樣 stage indicator 是真實狀態而不是視覺 fake
|
||
- **選項 B**:維持現狀(fake stage)— 簡單但不夠誠實
|
||
|
||
**Architect 建議選項 A**。理由:converter `stage` 已經提供 — 不利用反而浪費。具體 mapping:
|
||
|
||
| converter stage | UI stage 名稱 | 觸發條件 |
|
||
|----------------|------------|---------|
|
||
| `onnx` | 解析模型 | converter status=running, stage=onnx |
|
||
| `bie` | 量化模型 | converter status=running, stage=bie |
|
||
| `nef` | 編譯 NEF | converter status=running, stage=nef |
|
||
|
||
stage 名稱建議改為 3 個:「解析」/「量化」/「編譯」(更貼近 converter 實際做的事)。
|
||
|
||
**請 Design 對齊 + 改 §6.1**:把 stage 名稱與 converter `stage` 欄位 mapping,並更新 wireframe §11.6 i18n key。
|
||
|
||
#### 2.1.4 ⚠️ 中 — Upload XHR 進度 100% 後的文案
|
||
|
||
**位置**:wireframe §4.2 階段 B「上傳中」
|
||
|
||
wireframe §4.2 設計「進度條 + 預估剩餘」,但沒明確規定 XHR `loaded === total` 後但 backend 還沒回 200 的這 1-3 秒要顯示什麼。flow §5.3 有提到「即將完成…」/「伺服器處理中…」— 這需要在 wireframe 對齊:
|
||
|
||
**建議**:wireframe §4.2 加註:
|
||
|
||
```markdown
|
||
**進度 100% 後但伺服器還沒回**:
|
||
| XHR 狀態 | 文案 |
|
||
|---------|-----|
|
||
| progress < 100% | 已上傳 X / Y · 預估剩餘 N |
|
||
| progress = 100%, 等待 < 5 秒 | 即將完成… |
|
||
| progress = 100%, 等待 ≥ 5 秒 | 伺服器處理中… |
|
||
```
|
||
|
||
對應 i18n key(§11.5)已有 `conversion.uploading.almostDone`,建議再加 `conversion.uploading.serverProcessing`。
|
||
|
||
#### 2.1.5 小 — 「加到模型庫」按鈕 vs Dialog 確認
|
||
|
||
wireframe §7.1 設計了確認 Dialog(複用 import flow)— 在 §14 給 PM 的補充第 1 條已正確標記為「待 PM 決定」。
|
||
|
||
**Architect 立場**:保留 Dialog 是對的(model record 是永久資源、使用者控制名稱比較自然)。已在 TDD api-conversion.md v0.3 的 promote-to-models request body 對齊「單欄位 name only」。
|
||
|
||
#### 2.1.6 小 — Wand2 icon 替代
|
||
|
||
§10 給的替代方案合理(FileCog / Replace),不是 Architect 領地,由 Design / 使用者決定。
|
||
|
||
### 2.2 對 Flow(`03-design/flows/flow-conversion.md`)
|
||
|
||
#### 2.2.1 ✅ 同意的部分
|
||
|
||
- **§3 全景圖(Mermaid)**:完整且與 ADR-014 對齊,包含所有 5 個 stage(active check / select / uploading / processing / completed)
|
||
- **§4 State Machine + 5 狀態保存策略**:「不持久化 jobId 全部由 backend `/active` 提供」是核心原則,正確
|
||
- **§5.4 Polling 策略**:visibilitychange 暫停、退避、終止條件 — 全部與 TDD §9 對齊
|
||
- **§6 邊界情境**:6.1 active job、6.2 上傳失敗、6.3 過期、6.4 多分頁、6.5/6.6 重新整理 — 全部 cover
|
||
- **§7 UX Writing 要點**:誠實呈現狀態、避免技術語 — 對齊 design-spec
|
||
- **§8.2 給 Architect 的補充**:5 條建議,第 1/2/3/4/5 條我都已採納並補進 TDD(謝謝)
|
||
|
||
#### 2.2.2 ⚠️ 嚴重 — endpoint 路徑與 TDD 不一致
|
||
|
||
同 §1.2 / §2.1.2。具體位置:
|
||
|
||
- §3 全景圖 sequence diagram 中所有 `POST /api/converter/jobs` / `GET /api/converter/jobs/{id}` / `POST /api/converter/jobs/{id}/promote-to-models` / `POST /api/converter/jobs/{id}/download-token` → 全部改 `/api/conversion/*` 前綴
|
||
- §3 stage 3b sequence diagram「下載」那段 `POST /api/converter/jobs/{id}/download-token` + 「`{ url, expires_at }`」→ `GET /api/conversion/{id}/download` + `HTTP 302 Found, Location: faa-url`
|
||
- §5.5 「下載」分支步驟 1-4 改成 anchor 觸發、browser 自動 302 follow
|
||
- §8.2 給 Architect 第 1 條建議端點名稱
|
||
|
||
修法同 §2.1.2。
|
||
|
||
#### 2.2.3 ⚠️ 中 — Cancel 後 backend 對 converter 的 cleanup
|
||
|
||
**位置**:flow §5.3「取消」末尾、§8.2 給 Architect 第 4 條
|
||
|
||
flow §5.3 寫「visionA backend 收到取消信號後**也要對 converter 發 cancel**(避免孤立 job)」— 我在 TDD `conversion.md` §4.3.2 v0.3 補了完整的 cleanup 鏈分析(C1–C4 情境表 + best-effort cancel via `POST /api/v1/jobs/{id}/cancel`)。
|
||
|
||
Design 的這段描述沒問題,但**沒講清楚** converter 是否提供 cancel endpoint。事實上 converter Phase 1 已實作 `POST /jobs/{id}/cancel`(PRD §5 N2 提到),但 PM Phase 0.8 Non-Goals 把「使用者主動取消」列在 N2。
|
||
|
||
**澄清**:「使用者主動取消」(PM 不做)≠ 「backend 內部 best-effort cancel 做 cleanup」(TDD 做)。前者是 UI feature、後者是 reliability infra。兩者用同一個 converter endpoint 但語義不同。
|
||
|
||
**建議 Design**:在 flow §5.3「取消」末尾加註:
|
||
|
||
```markdown
|
||
> **澄清**:使用者按「取消上傳」是 frontend 端的 `xhr.abort()`,TCP RST 觸發 backend
|
||
> cleanup 鏈,backend 內部會 best-effort 對 converter 發 cancel。這個 cancel **是內部
|
||
> 韌性處理**,不暴露 UI;PRD Phase 0.8 Non-Goals N2 講的是「進行中 job 的取消 UI」,
|
||
> 兩者不衝突。
|
||
```
|
||
|
||
#### 2.2.4 小 — `expires_at` 來源 + frontend 顯示
|
||
|
||
flow §5.5 success 階段「過期提醒」描述:
|
||
|
||
> 計算 `expires_at - now()` → 顯示「6 天 21 小時後自動清除」
|
||
|
||
正確,且 TDD api-conversion.md v0.3 已確保 `expires_at` 在 Job response 必出現(無論 converter 給或 backend 推算)。✅ 對齊。
|
||
|
||
---
|
||
|
||
## 3. Architect 自己的 TDD 修訂(這次補上的)
|
||
|
||
### 3.1 議題 #2 — visionA backend 重啟後 ownership 全失
|
||
|
||
**修訂**:`conversion.md` §2.6.1(新增)
|
||
|
||
採方案 **A4:lazy rebuild**:`/active` endpoint 在 in-memory miss 時,fallback 對 converter 查 `GET /api/v1/jobs?user_id=<sub>&status=in_progress` 並重建 ownership。對 frontend 完全透明。
|
||
|
||
**為什麼選 A4 不選 A2(啟動時批次掃)**:A2 對 converter 是 hammer;A4 是 lazy(user 行為觸發),cost 對應實際需求。
|
||
|
||
**新增依賴**:converter Phase 1 的 `GET /api/v1/jobs?user_id=&status=in_progress` endpoint(converter Phase 1 已實作)。已要求 PM 補進 §7 dependency 表(§1.6)。
|
||
|
||
### 3.2 議題 #3 — `GET /api/conversion/active` 端點
|
||
|
||
**修訂**:原本 TDD 已有 `GET /api/conversion/active`,這次只是把它的 response shape 補上 `expires_at` / `source_filename` / `target_chip`,並文件化「lazy rebuild」行為(議題 #2)。
|
||
|
||
**修訂位置**:`api/api-conversion.md` §5
|
||
|
||
**已對齊 Design 需求**:wireframe §3.3 / flow §5.1 進入 `/conversion` 打 `/active` 直接落 processing 的設計,能拿到所有需要顯示的欄位(檔名、目標 chip、過期時間)。
|
||
|
||
### 3.3 議題 #5 — Cancel 清理鏈
|
||
|
||
**修訂**:`conversion.md` §4.3.2(新增完整章節)
|
||
|
||
新增內容:
|
||
|
||
1. 4 種 cancel 觸發情境(C1 使用者取消 / C2 重新整理 / C3 網路斷 / C4 converter 拒絕)
|
||
2. cleanup 鏈:`xhr.abort()` → TCP RST → `gin Context.Done()` → goroutine `pw.Close()` → io.Pipe EOF → converter multer abort
|
||
3. Best-effort `POST /api/v1/jobs/{id}/cancel`:當 backend 已拿到 job_id 但 streaming 失敗時,主動對 converter 發 cancel 避免孤立 job
|
||
4. §9 retry 表新增 `cancel` row(best-effort、不重試)
|
||
|
||
**重要區分**:「使用者主動取消 UI」(PM Non-Goals N2,Phase 1+ 才做)vs 「backend 內部 best-effort cancel cleanup」(TDD 此次補)— 兩者不衝突,後者是韌性 infra。
|
||
|
||
### 3.4 議題 #6 — Upload XHR 進度語意
|
||
|
||
**修訂**:`conversion.md` §4.3.1(新增完整章節)
|
||
|
||
設計選擇表(選項 A vs B),明確採選項 A:**backend 等 converter 201 才回 200,不 early-return**。理由:
|
||
|
||
- 進度 100% 接近端到端真實狀態
|
||
- 實作簡單(同步等)
|
||
- 失敗處理路徑清楚(同步錯誤直接回)
|
||
- 1-3 秒 io.Pipe drain 延遲對 500MB 上傳可接受
|
||
|
||
Frontend UX 補償:100% 後到 backend 回 200 之間,文案切「即將完成…」/「伺服器處理中…」(已在 flow-conversion.md §5.3 提到)。已要求 Design 在 wireframe §4.2 對齊文案表(§2.1.4)。
|
||
|
||
### 3.5 議題 #7 — `expires_at` 來源
|
||
|
||
**修訂**:`conversion.md` §2.6.2(新增)+ `api/api-conversion.md` §1 / §2 / §5(response shape 補欄位)
|
||
|
||
決策:
|
||
|
||
- 優先從 converter response 直接讀(converter Phase 1 是否提供 — 給 Backend Agent 確認)
|
||
- 若 converter 沒給,backend 自行 `created_at + 7d` 推算
|
||
- **Frontend 永遠拿到 `expires_at`**,無論來源
|
||
|
||
**Job response shape 統一補欄位**(v0.3):
|
||
- `expires_at`:必有
|
||
- `source_filename`:必有(給 success card 顯示「yolov5s.onnx → yolov5s_kl720.nef」)
|
||
- `target_chip`:必有(給 wireframe §6 / §7 顯示「→ KL720」)
|
||
|
||
### 3.6 同步影響的小修訂
|
||
|
||
- `conversion.md` §3 endpoint 表加註「不對外暴露但內部使用的 converter endpoint」(cancel + lazy rebuild)
|
||
- `conversion.md` §9 retry 表新增 cancel / lazy rebuild 兩 row
|
||
- `api/api-conversion.md` §3 promote-to-models request body 對齊 Design 單欄位(議題 #4)
|
||
|
||
---
|
||
|
||
## 4. 給 Orchestrator 的決策清單
|
||
|
||
> 三方修訂大部分各自進行,以下是需要 Orchestrator 協調或使用者裁決的事項。
|
||
|
||
### 4.1 [使用者裁決] API endpoint 路徑命名統一
|
||
|
||
**問題**:PM PRD 用 `/api/converter/jobs/*`、Design wireframe / flow 用 `/api/converter/jobs/*`、Architect TDD 用 `/api/conversion/*`,三方不一致。
|
||
|
||
**選項**:
|
||
|
||
- **選項 A**(Architect 建議):統一用 `/api/conversion/*`、`promote-to-models`、`GET /download` 302
|
||
- 優點:對齊 visionA 內部模組名(`internal/conversion/`);ADR-014 v1.1 download 安全升級已採;命名語意清楚
|
||
- 改動:PM PRD §F6 / F7、Design wireframe / flow(多處)
|
||
|
||
- **選項 B**:統一用 `/api/converter/jobs/*`
|
||
- 優點:與 converter 真實 path(`/api/v1/jobs/*`)名字相近、有「轉發」直覺
|
||
- 缺點:誤導使用者以為是 raw proxy(其實 backend 有 ownership / 半自動邏輯);download flow 已決議用 GET 302,不能維持 `POST /download-token`
|
||
|
||
**Architect 強烈建議選 A**。
|
||
|
||
**決策後**:
|
||
- 選 A → PM 改 PRD §F6/F7、Design 改 wireframe / flow(標記位置已在 §1.2 / §2.1.2 / §2.2.2 列出);TDD 不動
|
||
- 選 B → Architect 改 TDD 全部(`internal/conversion/` package 名也要改)— 不建議
|
||
|
||
### 4.2 [PM 待補] 修訂建議清單(嚴重度)
|
||
|
||
| # | 嚴重度 | 修訂位置 | 內容 |
|
||
|---|--------|---------|------|
|
||
| P1 | 嚴重 | §F6 / §F7 | 對齊 endpoint 命名(見 §4.1)|
|
||
| P2 | 中 | §F 段(新增 F0)| 補「Active job pre-check」功能 |
|
||
| P3 | 中 | §F2 / §F3 | 補上傳進度 100% 後的文案語意說明 |
|
||
| P4 | 中 | §F6 | 補「加到模型庫」Dialog 含名稱欄位(對齊 Design)|
|
||
| P5 | 中 | §6 + §7 | 補 D7(重啟 lazy rebuild)+ converter dependency 補 2 個內部用 endpoint |
|
||
| P6 | 小 | §8 KPI | 量測點改為 backend log job 完成時間 |
|
||
| P7 | 小 | §F2 | 確認 chip 數量(4 個 vs 2 個),與 converter 對齊 |
|
||
|
||
### 4.3 [Design 待補] 修訂建議清單(嚴重度)
|
||
|
||
| # | 嚴重度 | 修訂位置 | 內容 |
|
||
|---|--------|---------|------|
|
||
| D1 | 嚴重 | wireframe §3.3 / §6.3 / §7.1 / §7.2、flow §3 / §5.5 / §8.2 | 對齊 endpoint 命名(見 §4.1)|
|
||
| D2 | 中 | wireframe §6.1 | stage indicator 改用 converter `stage` 欄位(onnx/bie/nef → 解析/量化/編譯)|
|
||
| D3 | 中 | wireframe §4.2、flow §5.3 | 補 100% 後等待文案表 + i18n key |
|
||
| D4 | 中 | flow §5.3 / §8.2 | 補「使用者取消」vs「backend 內部 cancel cleanup」的區分 |
|
||
| D5 | 小 | wireframe §10 | Wand2 icon 採用(不需改)|
|
||
|
||
### 4.4 [需與其他團隊確認]
|
||
|
||
- **Backend Agent**:確認 converter Phase 1 的 `GET /api/v1/jobs/{id}` response 是否含 `expires_at`,決定 backend 是直接透傳還是自行推算
|
||
- **DevOps**:確認 visionA stage → 192.168.0.130 converter 網路可達性(ADR-014 合規清單仍未勾)+ MC service client 4 scope 已授權
|
||
- **Converter 團隊**:確認 Phase 1 `platform` 欄位實際支援哪幾個 chip enum(`520 / 720` 或 `520 / 630 / 720 / 730`)
|
||
|
||
---
|
||
|
||
## 5. 結論
|
||
|
||
**已落地(Architect 端)**:4 個議題(#2 / #5 / #6 / #7)已寫進 `conversion.md` v0.3 + `api/api-conversion.md` v0.3。
|
||
|
||
**待 Orchestrator 協調**:
|
||
1. 1 個使用者裁決:API endpoint 命名統一(§4.1)
|
||
2. 7 個 PM 修訂:1 嚴重 + 4 中 + 2 小
|
||
3. 5 個 Design 修訂:1 嚴重 + 3 中 + 1 小
|
||
4. 3 個跨團隊確認
|
||
|
||
**建議下一步**:
|
||
1. Orchestrator 把 §4.1 提給使用者裁決(這是阻擋三方對齊的單一最大議題)
|
||
2. 使用者決定後,PM / Design 同步修訂(兩邊都在改 endpoint,可平行進行)
|
||
3. PM / Design 第二輪產出後,Architect 再做最終 review(確認沒新衝突)
|
||
4. 完成後進 Phase 0.8 實作階段
|
||
|
||
---
|
||
|
||
## 版本記錄
|
||
|
||
| 日期 | 版本 | 變更 |
|
||
|------|------|------|
|
||
| 2026-04-30 | 1.0 | 初版 — Phase 0.8 三方交叉審閱(Architect 視角)|
|