diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 7ab3cb98..32c103fc 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -4598,8 +4598,8 @@ - 根因:BUG-049 / BUG-185 只把操作栏接到生时校正消息列表。普通咨询 `page.tsx` 只渲染 `ChatMessageRow`,没有复用同一组操作。 - 修复:抽出共用 `ChatMessageActions`。普通咨询与生时校正共用赞踩互斥、复制和仅最新回答可重跑。普通咨询重跑去掉最后一条助手消息后走现有 `/api/consult`(会扣点),失败则恢复原文;生时校正仍走免费原位 regenerate。 - 验证:`frontend/tests/chat-message-actions.test.ts`、`chat-stream-layout.test.ts`、`rectification-agentic-entry.test.ts`。 -- 防复发:普通咨询与生时校正的可见操作必须共用同一组件;不得再复制一套图标按钮。 -- 相关记录:BUG-049、BUG-094、BUG-185 +- 防复发:普通咨询与生时校正的可见操作必须共用同一组件;不得再复制一套图标按钮。源码合同若切片 `send()` 失败回填,必须带着 `restoreOnFailure` 守卫,见 BUG-308。 +- 相关记录:BUG-049、BUG-094、BUG-185、BUG-308 - 复发自:无 - 修复版本:待提交 @@ -4618,3 +4618,19 @@ - 相关记录:BUG-179、BUG-181、BUG-304 - 复发自:BUG-181(完成凭证成为用户主结果后,分盘和宫位没有独立正文位置) - 修复版本:待提交 + +## BUG-308 | 咨询重跑改了草稿回填条件后,源码合同仍切旧 if,staging 质量门 1807/1808 + +- 状态:resolved +- 首次发现:2026-08-19 +- 最近更新:2026-08-19 +- 影响面:Gitea `backend-quality-gate.yml` 的 `npm test --prefix frontend`、`frontend/tests/composer-isolation-contract.test.ts` +- 用户现象:`c8af18e90a` 的 validate 在 1808 项前端测试里红 1 项。日志末尾是已通过的 timing-output-guard / truth-source 合同,真正失败是 `not ok 578 - every external draft writer keeps working through the page-owned setters`。 +- 触发条件:向 `staging` 推送含 BUG-306 重跑路径的 `page.tsx`。 +- 根因:草稿隔离合同用 `sourceBetween` 按字面切 `if (activeSessionIdRef.current === sessionId) {`。重跑把持久化失败回填改成 `if (!options.restoreOnFailure && activeSessionIdRef.current === sessionId)`:普通发送失败仍把问题放回输入框,重跑失败则还原被删的助手消息、不改草稿。旧标记在文件里已不存在,`indexOf` 得到 `-1`。产品回填还在,测试锚点过期。 +- 修复:切片起点改为当前守卫;继续断言该段含 `setDraft(originalQuestion)`。 +- 验证:`frontend/tests/composer-isolation-contract.test.ts` 修复前 4/5、修复后 5/5。 +- 防复发:切 `send()` 失败回填不得再用无 `restoreOnFailure` 的旧 if。源码合同的起止标记必须随被切代码一起改,否则质量门会把无关提交打红。 +- 相关记录:BUG-249、BUG-306 +- 复发自:BUG-306(加了 `restoreOnFailure` 而未更新草稿隔离切片) +- 修复版本:待提交 diff --git a/frontend/tests/composer-isolation-contract.test.ts b/frontend/tests/composer-isolation-contract.test.ts index 670f1450..c067f45c 100644 --- a/frontend/tests/composer-isolation-contract.test.ts +++ b/frontend/tests/composer-isolation-contract.test.ts @@ -87,7 +87,11 @@ test("every external draft writer keeps working through the page-owned setters", const saveOnboardingName = sourceBetween(pageSource, "async function saveOnboardingName()", "async function saveOnboardingBirth"); const stopRestore = sourceBetween(pageSource, "updateSession(pending.sessionId, () => pending.previousSession);", "function completeConsultationInterface"); const sendClear = sourceBetween(pageSource, " updateSession(sessionId, () => userSession);", "if (!resuming && process.env.NODE_ENV === \"development\" && uiPreview.current)"); - const sendRestore = sourceBetween(pageSource, "if (activeSessionIdRef.current === sessionId) {", "setRequestError({"); + const sendRestore = sourceBetween( + pageSource, + "if (!options.restoreOnFailure && activeSessionIdRef.current === sessionId) {", + "setRequestError({", + ); // Then: suggestions fill, session switches clear, stop restores and send clears. assert.match(chooseSuggested, /setDraft\(question\);\n\s*setDraftTheme\(theme \?\? null\);\n\s*setDraftEntrypoint\(entrypoint\);/);