--- phase: 06- reviewed: 2026-04-15T03:54:20Z depth: standard files_reviewed: 5 files_reviewed_list: - frontend/src/components/workspace/input-box.tsx - frontend/src/core/threads/submit-files.ts - frontend/src/core/threads/hooks.ts - frontend/src/core/threads/hooks.test.ts - frontend/tests/e2e/input-and-compose.spec.ts findings: critical: 0 warning: 5 info: 1 total: 6 status: issues advisory: true --- # Phase 06: 代码评审报告(聚焦 06-04 gap-closure) **Reviewed:** 2026-04-15T03:54:20Z **Depth:** standard **Files Reviewed:** 5 **Status:** issues(建议性、非阻塞) ## Summary 本次重点审查了 06-04 涉及的输入引用与提交流程。未发现高危安全漏洞,但存在若干会导致行为偏差或可观测性不足的问题:附件仅发送路径被阻断、文件 URL 拉取缺少响应状态校验、上传失败被静默吞掉、缓存更新回调对空数据不安全,以及一个永久 skip 的 E2E 用例导致回归覆盖不足。 ## Warnings ### WR-01: 仅附件消息会被前端拦截,无法提交 **File:** `frontend/src/components/workspace/input-box.tsx:297` **Issue:** `handleSubmit` 只判断 `message.text` 和 `references`,忽略 `message.files`。当用户仅上传附件而不输入文本时会直接 `return`,与常见聊天上传行为不一致。 **Fix:** ```tsx if (!message.text && (message.files?.length ?? 0) === 0 && references.length === 0) { return; } ``` ### WR-02: 文件 URL 转 File 时未校验 HTTP 状态,可能上传错误内容 **File:** `frontend/src/core/threads/hooks.ts:509`, `frontend/src/core/threads/hooks.ts:723` **Issue:** 两处 `fetch(fileUIPart.url)` 后直接 `response.blob()`,未检查 `response.ok`。当 URL 失效返回 404/500 时,错误页面内容也可能被当作文件上传。 **Fix:** ```ts const response = await fetch(fileUIPart.url); if (!response.ok) { throw new Error(`Failed to fetch file blob: ${response.status}`); } const blob = await response.blob(); ``` ### WR-03: `useSubmitThread` 上传失败后继续发送,存在“静默丢附件” **File:** `frontend/src/core/threads/hooks.ts:747-749` **Issue:** `useSubmitThread` 中上传失败仅 `console.error`,未 toast、未中断提交,用户会看到消息发送成功但附件未随消息进入上下文。 **Fix:** ```ts } catch (error) { console.error("Failed to upload files:", error); toast.error("附件上传失败,请重试。"); return; // 或 throw error,阻断本次 submit } ``` ### WR-04: React Query 缓存更新回调假设 `oldData` 非空,存在运行时异常风险 **File:** `frontend/src/core/threads/hooks.ts:218-219`, `frontend/src/core/threads/hooks.ts:940-941` **Issue:** 两处 `setQueriesData` 回调直接 `oldData.map(...)`;当缓存尚未建立时 `oldData` 可能为 `undefined`,会触发 `TypeError`。 **Fix:** ```ts (oldData: Array | undefined) => oldData?.map((t) => { ... }) ?? oldData ``` ### WR-05: E2E 用例 DF-INPUT-008 被永久 skip,回归覆盖缺口持续存在 **File:** `frontend/tests/e2e/input-and-compose.spec.ts:159` **Issue:** `testInfo.skip(true, ...)` 是硬编码永久跳过,导致“stale 引用不阻断发送”的端到端行为无法被自动回归验证。 **Fix:** 改为条件 skip(基于 fixture 能力探测),或通过 mock/测试路由注入 stale 引用,使该用例在可控环境可执行。 ## Info ### IN-01: 留有 TODO 占位,后续建议纳入工单 **File:** `frontend/src/components/workspace/input-box.tsx:662`, `frontend/src/components/workspace/input-box.tsx:1045` **Issue:** 仍有连接器/skill 取消能力相关 TODO,表明交互与后端契约尚未完全收敛。 **Fix:** 将 TODO 关联到明确 issue/phase,避免长期悬置。 --- _Reviewed: 2026-04-15T03:54:20Z_ _Reviewer: Claude (gsd-code-reviewer)_ _Depth: standard_