visionA/docs/autoflow/03-design/flows/flow-conversion.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

26 KiB
Raw Permalink Blame History

轉檔流程 — visionA Cloud

Phase 0.8 雲端版新增流程。使用者把 ONNX / TFLite 模型轉成 .nef,全程在 visionA Cloud 內完成,不必跳到 converter 站台。

對應 wireframewireframes/wireframe-conversion.md 對應 Feature spec02-prd/features/feature-converter-integration.md


1. User Story

作為 一個 Kneron AI 應用開發者, 我想要 把手上的 ONNX / TFLite 模型直接在 visionA Cloud 轉成 .nef 這樣 我就能立刻把它部署到我配對的 KL 裝置,不用先去 converter 站台再回來,整個動線是一條直線。

成功條件:

  • 從上傳到拿到 .nef 的 P95 時間 < 10 分鐘(含上傳 + 轉檔 + promote
  • 完成後使用者顯式選擇結果如何處理(加到模型庫 / 下載 / 兩個都做)
  • 失敗時錯誤訊息可理解PRD §F5 對照表)

2. 設計決策摘要

對齊 Feature spec §6「整合決策」。設計面承接的關鍵決策

# 決策 UX 含意
D1 Upload 走 visionA backend streaming proxy非 presigned PUT 使用者只看到一個進度條browser → visionA。不暴露 converter 端點
D2 Download 走 server-side 換 token + 302 redirect → browser 直連 FAA 點下載 → 前端打 GET /api/conversion/{job_id}/download → backend 302 redirect 到 FAA沒有第二段「下載到瀏覽器」進度token 不暴露給前端 JS
D3 結果處理半自動user 顯式選擇) 完成後永遠顯示「加到模型庫」「下載」兩個按鈕,不自動執行
D4 Polling 510 秒一次 不做 SSE / WebSocket前端複雜度低不顯示「即時百分比」(converter 不給)
D5 converter API 不動 UI 直接綁 visionA backend 的 /api/conversion/*
D6 Sidebar 獨立 tab不混 /models 入口單一、心智清楚

3. 流程全景圖Mermaid

sequenceDiagram
    autonumber
    participant U as User<br/>(Browser)
    participant FE as visionA Frontend<br/>(/conversion)
    participant BE as visionA Backend<br/>(/api/conversion/*)
    participant CV as kneron_model_converter
    participant FAA as File Access Agent

    Note over U,FE: ── State Aidle ──
    U->>FE: 進 /conversion
    FE->>BE: GET /api/conversion/active
    alt 已有 active job
        BE-->>FE: { active: true, jobId, status }
        FE->>FE: 直接切 processing 畫面§5
    else 無 active job
        BE-->>FE: { active: false }
        FE->>U: 顯示空狀態 + 「開始轉檔」CTA
    end

    Note over U,FE: ── State B選檔 + 設定 ──
    U->>FE: 點「開始轉檔」開 Upload Dialog
    U->>FE: 選 .onnx / .tflite + 選 chip + (可選) ref images
    FE->>FE: 前端驗證(副檔名、大小、必填)
    U->>FE: 按「開始上傳」

    Note over U,FE: ── State CuploadingXHR streaming proxy──
    FE->>BE: POST /api/conversion/init<br/>(multipart, XHR upload.onprogress)
    BE->>CV: forward stream 到 POST /api/v1/jobs
    CV-->>BE: 201 Created { job_id }
    BE-->>FE: 200 { job_id, status: queued }
    FE->>FE: Dialog 自動關閉,主畫面切 processing

    Note over U,FE: ── State Dprocessingpolling──
    loop 每 510 秒(分頁可見時)
        FE->>BE: GET /api/conversion/{job_id}
        BE->>CV: GET /api/v1/jobs/{job_id}
        CV-->>BE: { status: queued/running/succeeded/failed, ... }
        BE-->>FE: { status, error_code?, ... }
    end

    alt status = succeeded
        Note over CV,FAA: converter 內部 promote 已上 FAA
        FE->>U: 顯示 success 畫面§7
    else status = failed
        FE->>U: 顯示 failed 畫面§8
    end

    Note over U,FE: ── State Ecompleted.success ──
    alt 使用者點「加到模型庫」
        U->>FE: 點 + 確認 Dialog輸入名稱
        FE->>BE: POST /api/conversion/{job_id}/promote-to-models
        BE->>FAA: server-to-server pull NEF
        FAA-->>BE: NEF binary
        BE->>BE: 走既有 /api/models/init + /finalize
        BE-->>FE: 200 { model_id }
        FE->>U: toast 「已加入模型庫」+ 連結
    end

    alt 使用者點「下載」
        U->>FE: 點按鈕
        FE->>U: window.location.href = '/api/conversion/{job_id}/download'
        U->>BE: GET /api/conversion/{job_id}/download
        BE->>BE: 跟 MC 換 delegated tokenserver-side
        BE-->>U: HTTP 302 Redirect<br/>Location: <FAA-URL>/files/{key}?access_token=...
        U->>FAA: GET /files/{key}?access_token=...(跟著 redirect
        FAA-->>U: NEF binary瀏覽器下載
    end

4. State Machine前端

/conversion 路由內以單一 store 維護狀態(建議:useConversionStore Zustand

       idle
        │
        │ click「開始轉檔」
        ▼
    upload-form-open  (Upload Dialog 顯示中)
        │
        │ submit
        ▼
    uploading         (Dialog 內、XHR onprogress 0100%)
        │
        ├── XHR 4xx/5xx → idle  (toast error)
        ├── 取消上傳 → idle  (toast canceled)
        └── XHR 200 + got job_id
            ▼
       processing     (主畫面、polling)
        │
        ├── poll status=succeeded → completed.success
        ├── poll status=failed    → completed.failed
        ├── poll 5 次 fail        → 顯示「重試」按鈕,不離開 processing
        └── 使用者離開頁面 / 重新整理 → 下次進入 §3.idle 自動恢復

狀態保存策略D6 補充):

狀態 存 localStorage 為何
idle 預設狀態
upload-form-open 內的選檔 檔案物件無法序列化,使用者重新整理就清空
uploading 進度 XHR 中斷無法續傳
processing job_id 由 backend GET /jobs/active 提供 source of truth不靠前端記
completed 結果 重新整理後從 backend 重新取(仍需要顯示給使用者)

核心原則:前端不持久化 jobId全部由 backend GET /jobs/active 提供。 這樣「換瀏覽器 / 多分頁 / 私密模式」都能正確還原。


5. State 細節

5.1 idle — 進入頁面 / 完成後重置

進入點:

  • 直接進 /conversion(從 sidebar
  • 完成後點「開始新轉檔」
  • 失敗後點「重新開始」

載入流程:

  1. Mount 時打 GET /api/conversion/active
  2. active=true → 跳 processing 或對應 completed 狀態
  3. active=false → 顯示空狀態wireframe §3

邊界:

  • API 失敗 → 顯示「無法載入轉檔狀態,請重試」+ retry button不假設沒 active job 就讓使用者開新的,避免重複 submit

5.2 upload-form-open — Dialog 內選檔

操作:

  • 拖拽檔案到 dropzone或點「選擇檔案」開 file picker
  • 輸入任務名稱(自動帶檔名 stem可改
  • 選 chip4 個 RadioGroup 必選)
  • 加 ref images多選選填

前端驗證 — submit 前:

規則 違反訊息 阻擋送出
必須選檔 conversion.upload.error.noFile
副檔名 .onnx / .tflite conversion.upload.error.unsupported
模型 ≤ 500 MB conversion.upload.error.modelTooLarge
必選 chip conversion.upload.error.noChip
每張 ref ≤ 10 MB conversion.upload.error.refTooLarge 該檔
ref images ≤ 100 張 conversion.upload.error.refTooMany

驗證失敗顯示在對應欄位下方紅字(text-sm text-destructive),同時「開始上傳」按鈕變 disabled。

取消:

  • [✕][取消] → 關 Dialog、回 idle、選檔狀態清空

5.3 uploading — Dialog 內顯示進度

送出:

  1. 構造 FormDatamodel file + ref images + 任務名稱 + chip
  2. XMLHttpRequest POST 到 /api/conversion/init
  3. xhr.upload.onprogress 更新 progressloaded / total
  4. 計算預估剩餘:取最近 3 秒移動平均速度

進度條顯示文案:

狀態 顯示
progress < 100% 已上傳 11.9 / 28.4 MB · 預估剩餘 0:24
progress = 100% 但 server 還沒回 即將完成…XHR 已送完但 backend 還在 forward 到 converter
等待時間超過 5 秒 伺服器處理中…

離開警告:

  • window.addEventListener('beforeunload', e => { e.preventDefault(); e.returnValue = ''; })
  • 取消 / 完成 / 失敗時 cleanup

Tab 標題更新:

visionA Cloud · 上傳中 (42%)  ← 動態插入百分比

完成後還原為 visionA Cloud

取消:

  • 點「取消上傳」→ AlertDialog 確認 → xhr.abort() → toast「已取消上傳」→ 回 idle
  • 重要visionA backend 收到取消信號後也要對 converter 發 cancel(避免孤立 job

失敗:

HTTP 行為
4xx 一般(不含 409 Dialog 內紅字顯示錯誤;保留 「重試」按鈕(重新打 submit
409 user_has_active_job Dialog 關閉 → 切 processing 畫面 + banner「您已有一個轉檔正在進行中已切換至該任務」
5xx / 網路 Dialog 內顯示「上傳失敗:{訊息}」+ 「重試」

5.4 processing — 主畫面、polling

Polling 策略:

const POLL_FAST_INTERVAL = 5_000;   // 060 秒
const POLL_SLOW_INTERVAL = 10_000;  // 60+ 秒
const POLL_MAX_RETRIES = 5;

// 暫停 / 恢復
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    pollNow();
    resumePolling();
  } else {
    pausePolling();
  }
});

狀態變化處理:

從 → 到 觸發 UI 更新
queuedqueued 持續 polling 無變化(保持 stage 1 完成、stage 2 當前)
queuedrunning 第一次拿到 running stage 2 標完成、stage 3 當前
runningrunning 持續 polling indeterminate progress 持續動
*succeeded 拿到 succeeded 切 §5.5 success 畫面、停 polling、tab title 更新
*failed 拿到 failed 切 §5.6 failed 畫面、停 polling
連 5 次 polling 失敗 exponential backoff 用完 顯示「無法取得轉檔狀態」+ retry 按鈕(不切走 processing

Tab 標題更新:

狀態 Tab title
processing visionA Cloud · 轉檔中…
完成5 秒內持續顯示) ✓ 轉檔完成 · visionA Cloud
失敗 ⚠ 轉檔失敗 · visionA Cloud

完成 / 失敗時用 emoji 是為了在分頁列上一眼可見reduced-motion / SR 友善(純文字)。

長時間排隊提示:

  • queued 持續 > 5 分鐘 → banner「目前排隊較久你可以離開此頁稍後再回」
  • running 持續 > 15 分鐘 → banner「轉檔耗時較長仍在進行中」

5.5 completed.success — 顯示結果 + 半自動分支

進入時:

  • 停止 polling
  • 顯示成功 Cardwireframe §7
  • toast「轉檔完成」+ action「下載 .nef」使用者直接 toast 點下載也 OK

「加到模型庫」分支:

1. 點按鈕 → 開 AlertDialog含名稱輸入欄
   - 預設 name = job.name 或 source filename stem + "_" + chiplowercased
   - 例yolov5s.onnx + KL720 → "yolov5s_kl720"
2. 使用者確認名稱 → 按「加到模型庫」
3. 按鈕變 spinner + disabled避免重複點
4. POST /api/conversion/{job_id}/promote-to-models
   body: { name }
5. 成功200 + model_id
   - toast「已加入模型庫」+ action 連結 /models/{model_id}
   - 結果 Card 內按鈕變綠勾「✓ 已加入(前往查看 →)」
   - 按鈕仍可重複點(再點會 409 重複)
6. 409 already imported
   - toast「此任務已加入過模型庫」+ action「查看現有模型 →」
7. 其他錯誤toast 顯示 + 按鈕回原狀態

「下載」分支:

1. 點按鈕 → 按鈕短暫變 spinner + 「準備下載…」
2. window.location.href = '/api/conversion/{job_id}/download'
   (或用 anchor tag <a href="..." download>,效果等價)
3. backend 收到後 server-side 跟 MC 換 delegated token
   直接回 HTTP 302 Redirect → Location 指向 FAA URL
4. 瀏覽器跟著 302 跳轉,內建下載管理器接管下載
   - 按鈕回原狀態(可重複點 → 重新換新 token
   - toast「下載已開始」+ 「若沒看到下載提示,請檢查瀏覽器設定」
5. 4xx / 5xxbackend 還沒到 redirect 階段就 fail
   - 因為已經 navigation瀏覽器會顯示 backend 的錯誤頁
   - 為避免此情境,可選用 fetch + manual handling 偵測 status 後再 navigate
   - 簡化版:直接 navigate靠 backend 顯示錯誤頁;遇到 4xx/5xx 使用者按 back 即可

注意token 不暴露給 frontend JS整個換 token 流程在 backend 內完成。

為何兩個按鈕互不互斥? PRD §F4 D3 明文:使用者可以兩個都做(先試試下載驗證、再加進模型庫;或反過來)。按鈕不會因為按過就消失,只會在 import 成功後加綠勾標記。

過期提醒:

  • 計算 expires_at - now() → 顯示「6 天 21 小時後自動清除」
  • 每分鐘更新一次(不需要每秒)
  • 過期當下頁面切「已過期」狀態wireframe §8.2),按鈕全部 disabled

「開始新轉檔」:

  • 點擊 → 重置 store → 回 idle 狀態
  • 不需要清除 backend jobconverter 自己 7 天 GC

5.6 completed.failed — 顯示錯誤 + 重試引導

進入時:

  • 停止 polling
  • 顯示 failed Cardwireframe §8
  • toast「轉檔失敗{user-friendly message}」

錯誤翻譯: 對照 PRD §F5 表 + wireframe §8.1。前端從 error_code 查 i18n key

const message = t(`conversion.error.${errorCode}.message`)
              ?? t('conversion.error.unknown.message');
const suggestions = t(`conversion.error.${errorCode}.suggestions`)
              ?? t('conversion.error.unknown.suggestions');

未知 code 一律 fallback 到 unknown

「重新開始」:

  • 重置 store → 回 idle
  • 上一個失敗的 job 不會自動重送converter 根本沒收到,或已 failed使用者需要重新選檔

「回模型庫」:

  • router.push('/models')

「複製任務 ID」

  • navigator.clipboard.writeText(fullJobId) → toast「已複製任務 ID」

6. 邊界情境

6.1 同 user 已有 active job409

PRD §F3 D1converter 端 enforce「同 user 同時 1 個 active job」。

情境 UI 行為
進入 /conversion、有 active 直接落 processing§5.1
點「開始轉檔」、submit 時拿到 409 Dialog 自動關閉、切 processing、banner「您已有一個轉檔正在進行中已切換至該任務」
點「開始新轉檔」(在 success 畫面)、實際上有別的 active 同上(理論上 success 表示自己的 job 已結束,不會撞)

→ 設計上不要在 idle 顯示「您有 active job」就把 CTA 變 disabled因為 §5.1 已經會直接跳走。

6.2 上傳到一半失敗

失敗點 已產生 converter job UI 行為
網路斷在前段XHR 還在 forward toast「上傳失敗請重試」+ Dialog 內可 retry
網路斷在 backend → converter 之間 可能(看 backend 實作) backend 應 cancel converter job前端 retry 不會撞 409
取消上傳 視 backend 實作 backend 應 cancel converter job

給 Architect 的補充visionA backend 在 forward stream 失敗 / 收到 cancel 時,必須對 converter 發 POST /api/v1/jobs/{id}/cancel,否則使用者下一次 submit 就撞 409 直到 converter idle 為止。

6.3 Job 7 天後過期

converter Phase 1 已實作 7 天 GC。前端體驗

進入點 UI
使用者重新整理 success 畫面(過期後) GET /jobs/active 回 404 → 進 idle如果有 backend cache 顯示「已過期」hint card 更友善
使用者點「下載」/「加到模型庫」(過期後) 4xx 失敗 → toast 顯示 + 自動切「已過期」狀態

理想做法success 畫面每分鐘 check 一次 expires_at,到期當下自動切「已過期」(不靠 polling 回 404

6.4 多分頁同時開 /conversion

情境 行為
兩個分頁都在 idle 各自獨立、互不影響
分頁 A submit 開始 upload分頁 B 進 idle 分頁 B 會在頁面 mount 時打 /jobs/active,發現 A 已開始 → 直接落 processing 畫面
兩個分頁同時點「開始轉檔」並各自 submit 第二個 submit 會收 409 → 切 processing 顯示已存在的 job
一個分頁完成、按「加到模型庫」、另一個分頁仍在 processing A 已 model importedB 的 polling 拿到 succeeded 也切到 success 畫面,不會撞(兩個分頁狀態一致)

不需要跨分頁通訊BroadcastChannel,靠 backend 是 source of truth 就足夠。

6.5 使用者在 uploading 中重新整理 / 關掉

  • XHR 中斷
  • backend 偵測到 stream 結束(沒收滿)→ cancel converter job
  • 使用者重進頁面 → /jobs/active 回 false → 落 idle

6.6 使用者在 processing 中重新整理 / 關掉 / 切走

  • 沒影響backend / converter 繼續跑
  • 重進 → /jobs/active 回 true → 落 processing → 繼續 polling

→ 這是 visionA Cloud 相對 local-tool 的核心優勢:「跑一個轉檔可以離開電腦」。在 idle 空狀態與 processing hint 都會說明這點。

6.7 上傳大檔500 MB的 UX

  • 進度條 + ETA基於 loaded / total 移動平均)
  • 不擋 UI使用者可以離開 /conversion 切到別頁XHR 仍在背景跑、Dialog 關掉但 XHR 不取消)
    • ⚠️ 雛形範圍不做這個(會把上傳邏輯從 component 拆到 store / context。Phase 0.8 規格:Dialog 關掉 = 上傳取消
    • 文案上 §11 的 conversion.uploading.warning 已聲明「請勿關閉此分頁」
  • 分頁標題持續更新百分比(讓使用者切到別的分頁也能看進度)
  • 慢網(< 1 MB/sETA 顯示「估計 8 分鐘」這種長時間,使用者要意識到要等

6.8 下載失敗 / 取消

  • window.location.href = url 是瀏覽器 navigation不會回到 visionA 顯示錯誤
  • 如果 token 已過期5 分鐘 TTL瀏覽器會顯示 FAA 的 403 頁面
  • 緩解:使用者回 /conversion 再點一次「下載」即可重拿 token

7. UX Writing 要點

對齊 design-spec.md §1.1「誠實呈現狀態」+ components.md §12「對開發者語調」

場景 寫法 不要
空狀態 heading 「還沒有進行中的轉檔」 「您尚未建立任何轉換任務」(過度禮貌)
開始按鈕 「開始轉檔」 「立即開始」「執行轉換」(贅字)
processing hint 「你可以離開此頁面,回來時會自動更新進度」 「請耐心等候」(沒提供資訊)
失敗 「模型內含不支援的運算子,無法量化到目標晶片」 「轉檔失敗,請重試」(沒說原因)
過期 「此轉檔結果保留期為 7 天,目前已超過保留期限並自動清除」 「資源已不存在」technical 語)
取消上傳確認 「上傳尚未完成,確定取消?」 「您確定要中止此操作嗎?」
加入模型庫 toast 「已加入模型庫」 「您的模型已成功加入至模型庫中」

→ 全部走 i18n key§wireframe §11不在元件 hardcode


8. 給其他 Agent 的補充

8.1 給 PM

  1. 「加到模型庫」確認 Dialog 內欄位:建議只保留模型名稱一個欄位,預設 {job.name}_{chip.toLowerCase()}。描述 / tags 留 Phase 1。如果 PM 認為要做最簡 UX「點下去就 import 不問」,請明確 confirm我移除 Dialog直接走 /models/{id} 後使用者再去改名)。
  2. GET /jobs/active 端點UX 設計依賴「進頁面就知道有沒有 active job」。如果這 API 沒列在 PRD §F 段,請補上;否則建議用 query param ?resume=true + 前端 localStorage jobId 替代(但體驗較差,跨瀏覽器壞掉)。
  3. 「開始新轉檔」 vs 結果保留success 畫面下方有兩個動作result card 內的「加模型庫 / 下載」+ 卡片外面的「開始新轉檔」)。我有意把「開始新轉檔」放結果卡片而不是並列,避免「使用者剛轉完想下載結果,結果一不小心點到開新的」造成困擾。如果 PM 覺得這樣動線不夠順,可以改放結果卡片內,但加 confirm dialog。
  4. 錯誤訊息 i18n fallbackconversion.error.unknown.* 用於未知 code未來 converter 加新 code 時,前端有合理 fallback後端 i18n 表更新前不會看到 raw code。

8.2 給 Architect

  1. 新端點建議:GET /api/conversion/active
    • { active: bool, job?: { id, status, source_filename, target_chip, started_at, expires_at } }
    • 用於 idle / 重新整理時恢復狀態
    • 沒這個端點 = 前端要自己用 localStorage 記 jobId跨瀏覽器 / 私密模式 / 多裝置壞掉
  2. Polling 對 backend 的負擔510 秒/次/user。建議在 visionA backend 對同一個 jobId 做 23 秒 cache避免 hammer converter。預期 10+ concurrent 時必要。
  3. Upload XHR onprogress 的精確度streaming proxy 模式下,前端進度 = browser → backend 的進度,不是 backend → converter 的進度。如果 backend buffer 過深,前端 100% 完成但 backend 還在傳,使用者會等不耐煩。建議 backend 在 forward 完成後才回 200把這段 buffer 算進進度。
  4. Cancel 時的清理:使用者按「取消上傳」/ 重新整理 → backend 偵測到 stream 結束 → 務必對 converter 發 cancel否則該 user 的下一個 submit 會撞 409 直到 converter idle。
  5. Job expires_at 的來源success 畫面顯示「6 天 21 小時後清除」需要確切時間。如果 converter 不直接給backend 自行 created_at + 7d 推算並回。
  6. Phase 1 升級時的相容性:未來 converter 提供 sub-progress百分比/ webhook → 前端只要在 processing 畫面把 indeterminate 換 determinate progress、減少 polling 頻率即可UI 結構不變。

8.3 給 Frontend

實作備忘(不是要你照做,是 design 角度的提醒):

  1. useConversionStore 建議結構:
    {
      state: 'idle' | 'uploading' | 'processing' | 'success' | 'failed' | 'expired';
      job: ConversionJob | null;          // 來自 GET /jobs/active 或 polling
      uploadProgress: number;             // 0100
      uploadEta: number;                  // seconds, 移動平均算
      pollErrorCount: number;
      // actions
      hydrate(): Promise<void>;           // mount 時打 GET /jobs/active
      submitUpload(payload): Promise<void>;
      cancelUpload(): void;
      importToModels(name): Promise<void>;
      requestDownload(): Promise<void>;
      reset(): void;
    }
    
  2. usePageTitle(title) hook上傳中 / processing 動態改 document.titlecleanup 還原。
  3. Indeterminate Progressshadcn Progress 不帶 value 時是空條,需要加 CSS animation建議 bg-gradient-to-r from-primary via-primary/40 to-primary + animate-[shimmer_2s_linear_infinite],並對 prefers-reduced-motion fallback 為純色)。
  4. beforeunload 警告:只在 uploading 狀態 attachprocessing 不需要(離開不會中斷後端)。
  5. Test 重點state machine 的轉移是核心邏輯;建議寫 component test手動觸發 store action、assert UI加上 GET /jobs/active 三種回應的 visual snapshot。

9. 不在 Phase 0.8 範圍

對齊 PRD §5 + wireframe §13

項目 何時做 影響 UX 的部分
轉檔歷史清單 Phase 1 / 之後 目前使用者只能看「眼前這個 job」跑完換新的舊的看不到converter 7 天 GC 也會清)
取消正在跑的 job Phase 1 processing 畫面沒有取消按鈕converter 已支援UI 不暴露)
多 chip 同時轉 converter 不支援 RadioGroup 單選、不是 Checkbox
SSE / WebSocket Phase 1 量大時 純 polling、有 510 秒延遲(人眼可接受)
進階參數FP16 等) Phase 1 Upload Dialog 沒有「進階」摺疊區
模型版本 / A/B Phase 2 model.SourceJobID 已預埋,可追溯但 UI 不展示
Webhookconverter → visionA push Phase 0.8 純 polling backend 不訂閱
上傳離開頁面繼續跑 Phase 1 Dialog 關閉 = 上傳取消

10. KPI / 驗收與設計的對應

KPIPRD §8 設計面如何支撐
第一個內部使用者轉檔成功率 > 80% 失敗訊息精準、suggestions 引導;前端驗證提早攔下不支援格式
上傳到 NEF P95 < 10 分鐘 Polling 間隔合理、不擋使用者離開頁面、tab title 通知
「加到模型庫」點擊率 > 50% 兩個按鈕視覺權重相當(不偏左 / 不預設 highlight、Dialog 摩擦力低
失敗錯誤訊息可理解率 100% §F5 對照表 + i18n unknown fallback

11. 待 Reviewer 確認的設計選擇

整理本流程中我做了選擇但可逆的決定,給 Design / PM 後續 review

# 決策點 我的選擇 替代方案 影響
Q1 sidebar icon Wand2 FileCog / Replace 視覺風格
Q2 sidebar 位置 模型庫之後 設定之前 / 工作區之後 心智模型
Q3 「加到模型庫」是否需 Dialog 確認名稱 需要、單欄位 靜默 import / 多欄位(含描述) 摩擦力 vs 控制感
Q4 「開始新轉檔」位置 結果卡片下方 結果卡片內、與兩個 action 並列 誤點風險
Q5 uploading 階段 Dialog 內顯示進度 vs 全頁切換 Dialog 內 上傳完直接全頁切 processing 並顯示進度 視覺一致性 vs 多狀態切換次數
Q6 processing 不給取消按鈕 不給 給 + 確認 dialog UX 安全 vs 控制感
Q7 success 兩按鈕順序 「加到模型庫」左、「下載」右 反過來 主動作優先級

如果使用者 / PM / Architect 對任一項有不同意見,文件以這份為準,調整後回頭更新此表 + wireframe + i18n