# Design 對 PRD + TDD 的交叉審閱 | 項目 | 內容 | |------|------| | 審閱者 | Design Agent | | 審閱日期 | 2026-04-21 | | 審閱對象 | PRD(v0.1)+ Design Doc / TDD(v0.1)+ api-spec.md + tunnel.md | | 基準 | 設計規格 `03-design/`(含 Pairing 與 Offline 兩大流程)| --- ## 1. 總評 **體驗完整度評分:3.5 / 5 ⚠️ 有改善空間** 骨架與關鍵流程(REST 轉發、tunnel 斷連語意、Pairing API)在三方文件間**對得上**,但在 **Pairing token 格式、離線訊號來源、錯誤語意、雛形 stub 的 UX 落點**有幾處**跨文件不一致**會直接讓 Phase 0 雛形體驗破功。建議在進入雛形骨架前修正 Critical 項。 --- ## 2. 流程完整性檢查 | 流程 | PRD | TDD / API | Design 規格 | 判定 | |------|-----|----------|------------|------| | Pairing 正向 | ✅ | ✅(API `/api/pairing/token` + `/status`)| ✅(3 步 stepper)| 🔴 **Token 格式三方不一致**(見 C1)| | Pairing Token 過期 | ✅ 15min | ✅ TTL 可調 | ⚠️ 設計規格寫 **72 小時** | 🔴 **C2** | | Pairing Token 已使用 | ✅ `409 Conflict` | ⚠️ POC 行為「後來覆蓋前」| ⚠️ 設計規格顯示「此 token 已被其他裝置使用」 | 🟡 **M1**(語意衝突)| | 裝置掉線(tunnel 斷)| ⚠️ 僅提「自動重連」| ✅ `502 TUNNEL_DISCONNECTED` | ✅ 完整(NetworkErrorBanner + 全頁遮罩 + 60s 倒數)| 🟡 **M2**(心跳 / lastSeen 未定義)| | Workspace 推論中斷線 | ⚠️ 「立即提示 + 自動重連」| ✅ 不 transparent retry | ✅ 覆蓋遮罩 + 不自動重啟推論 | ✅ 對齊 | | 登入 / 註冊(stub)| ✅ 可 stub | ✅ 回 501 | ⚠️ flow-auth 寫「送出直接跳 Dashboard」| 🔴 **C3**(501 vs 直接跳轉矛盾)| | Model 上傳大檔 | ⚠️ 未提進度 | ✅ presigned PUT(不走 tunnel)| ❌ **設計規格缺上傳進度 / 失敗重試 UI** | 🔴 **C4** | | 雛形多 user 隔離 | ✅ 驗收條件要求 | ⚠️ Q4 裁決:**StaticAuthService 永遠 demo-user(單一使用者)**| ✅(預設隔離)| 🟡 **M3**(驗收條件無法通過)| --- ## 3. UX 風險(技術選型 → 體驗) **R1 · Tunnel 延遲對即時感的衝擊 🔴** TDD 延遲預算 P95 < 500ms、heartbeat 30s(yamux);但 `flow-offline-handling.md` 寫「45 秒判定掉線」、「15 秒心跳」。**兩套數字不一致**。使用者在 Workspace 按停止推論卻要等 30s 才看到狀態變化,會被誤以為 App 當了。建議 heartbeat 10-15s、判定掉線 30s,讓 UI 「離線最大感知延遲 ≤ 30 秒」明確。 **R2 · MJPEG 單向流無回壓 🟡** api-spec `GET /api/camera/stream` 透過 yamux stream 走 MJPEG。若瀏覽器 tab 切到背景、或頻寬不足,MJPEG 會造成 yamux window 塞爆拖慢控制通道(切換 slider、停止推論按鈕)。TDD 未定義**背景 tab 時是否暫停 stream**。設計規格也沒寫「頁面不可見時」行為。Chrome 預設 throttle setInterval,但 MJPEG 是伺服器 push — 需要前端 visibilitychange 主動關 stream。 **R3 · 501 回應如何呈現 🔴** TDD 雛形大量端點回 `501 NOT_IMPLEMENTED`(登入、pairing token 產生、revoke、converter、batch 推論)。設計規格**沒有 501 的統一 UX 呈現**。目前 flow-auth 寫「送出直接跳 Dashboard」是繞過 API 的前端 hack,但 PRD 驗收條件 US-01 要求「看到登入頁」,US-02 要求「完成註冊」— 三方對雛形 auth 的期望不一致。 **R4 · Pairing Token 顯示格式與 API 脫鉤 🟡** Q7 裁決:「API 回傳純 hex,前端加空格顯示」。TDD `api-spec.md` 範例用 `pk_AbCd1234...`(前綴 + base62?),PRD 用 `vAc_` + 32 hex,設計規格用 16 字元 hex + 8+8 分隔。**三方 token 格式各自為政**。雛形實作時必須選一個,否則前端格式化 `.replace` 會錯字元數。 **R5 · ConnectedDevicesList / 全域 Badge 缺資料源 🟡** 設計規格要求 `RemoteDeviceBadge` 顯示「最後心跳 X 秒前」,TDD `Device` 結構與 `InMemoryDeviceRepository` 未定義 `lastSeenAt` 欄位,只有 `SessionStore.Summary` 有。需明確:**前端取 lastSeen 走哪支 API**?走 `/api/pairing/status`(單一 tunnel)還是每個 device 都要有一個欄位? **R6 · i18n 缺 key 🟢** 設計規格列出 ~40 個 `pairing.*` / `remote.*` / `network.*` i18n key,TDD / 前端改造章節(§10)未提及 i18n 擴充工作。需要在前端任務清單加一項「新增 i18n key」。 --- ## 4. 設計規格 vs PRD / TDD 的矛盾點 | # | 類別 | Design 規格寫的 | PRD / TDD 寫的 | 建議解法 | |---|------|----------------|--------------|---------| | **C1** | Token 格式 | 16 字元 hex,`a1b2c3d4 e5f6g7h8` 顯示 | PRD:`vAc_` + 32 hex;TDD:`pk_AbCd1234`(範例)| **三方統一**:建議採 PRD 格式 `vAc_` + 32 hex,設計規格改為 8+8+8+8 分四段顯示 | | **C2** | Token TTL | **72 小時** | PRD / TDD:**15 分鐘** | 設計規格改 15 分鐘(72 小時安全風險過高,PRD 對) | | **C3** | Auth 雛形行為 | 送出表單直接跳 Dashboard | TDD:回 501 | 設計規格增加「雛形 stub 模式」版本:前端偵測 501 → 前端強制過 → 跳 Dashboard + banner「雛形模式,尚未接入認證」 | | **C4** | Model 上傳 | 無進度 / 失敗 UI | presigned PUT 流程 | 補 `pages.md` 模型上傳頁的「上傳進度條 / 大小限制 / checksum 失敗 / presigned expire」四個狀態 | | **M1** | 同 token 多連線 | 「被其他裝置使用」錯誤提示 | POC 行為「後來覆蓋前」 | Q5 已裁決沿用 POC;設計規格改為顯示「此 token 已配對過 — 舊連線已被取代」,或隱藏該錯誤(因為覆蓋是成功的) | | **M2** | 心跳 / 掉線判定 | 15s 心跳 + 45s 判定 | tunnel.md:30s yamux keepalive | 統一:**10s 心跳 / 30s 判定**,TDD tunnel.md 補充 | | **M3** | 多 user 隔離驗收 | 設計要求裝置列表隔離 | Q4 裁決:雛形單一使用者 | PRD US-10 / feature-inference 驗收條件「不同使用者互相隔離」改為 **Phase 1 必須** | | **M4** | `remoteStatus` 欄位 | Device 新增 5 個欄位 | `InMemoryDeviceRepository` / `Device` struct 未涵蓋 | TDD database.md 補上 `remoteStatus` / `lastSeenAt` / `hostName` / `pairedAt` / `errorMessage` | --- ## 5. 發現的問題 ### Critical(阻擋雛形進入開發) - **[C1]** Pairing Token 格式 PRD / TDD / Design 三方不一致 → 先對齊格式 - **[C2]** Pairing Token TTL 設計寫 72h、PRD 寫 15min → 安全性必須是 15min - **[C3]** 雛形 Auth 的三方預期不一致(501 vs 前端強跳)→ 定義「雛形模式 banner」 - **[C4]** 設計規格缺大檔上傳的進度 / 失敗 / 重試 UI(feature-inference Phase 1 還有影片上傳) ### Major - **[M1]** 同 token 多連線語意:顯示錯誤 vs 靜默覆蓋 → 統一 - **[M2]** 心跳 / 掉線判定數字三方不一致 - **[M3]** 多 user 隔離驗收條件與 Q4 裁決衝突 - **[M4]** Device 結構缺 `remoteStatus` 等欄位,前端 UI 無資料可顯示 - **[M5]** 501 回應的統一 UX 呈現未定義 ### Minor / Suggestion - **[m1]** Background tab 時 MJPEG stream 行為未定 - **[m2]** flow-pairing.md 的 token 顯示範例(16 hex)與 PRD(32 hex)長度不同,導致 QR Code 尺寸需重估 - **[m3]** TDD §10 未提 i18n key 擴充任務 - **[m4]** WebSocket `/ws/pairing/status` 設計規格沒用,還在 polling(Step 3),建議改用 WS 即時 - **[m5]** Reduce Motion 在 flow-offline-handling 的「重連倒數」需特別測試(不要閃爍) --- ## 6. 設計規格需修改清單 | 檔案 | 修改 | |------|------| | `flow-pairing.md` §4.1 / §4.5 | Token 格式改 `vAc_` + 32 hex,TTL 15 分鐘(對齊 PRD)| | `flow-pairing.md` §6.4 | polling → 改用 `WS /ws/pairing/status`(TDD 已定義)| | `flow-auth.md` | 新增「雛形模式 banner」,說明 401/501 時的 UX | | `flow-offline-handling.md` §12 | 心跳 10s / 判定掉線 30s | | `pages.md`(模型管理頁)| **新增**上傳進度 / 錯誤 / presigned expire 狀態 | | `components.md` `RemoteDeviceBadge` | 標注資料來源 = `/api/pairing/status` 的 `last_seen_at` | | `design-spec.md` §5.2 給 Architect 提醒 | 補「需要 Device.remoteStatus / lastSeenAt 欄位」| --- ## 7. 結論 PRD 與 TDD **骨架堅實**,但 **Pairing Token 格式、TTL、心跳參數、501 UX、大檔上傳、雛形 Auth 流程**六處跨文件不一致。若不先對齊,雛形實作時會出現「前端以 72h 產 UI,後端 15min 失效」等漏洞。建議: 1. **先開 30 分鐘同步**統一 C1-C4(由 Orchestrator 召集 PM / Architect / Design) 2. 三方同步修文件後,再次互審;設計規格依第 6 節修訂 3. 通過後再進入雛形骨架 完整度達標才進入開發,可避免雛形一跑起來就遇到「規格對不上」的尷尬。