fix(rectification): place completion receipt above reply
Independent Staging Quality Gate / validate (push) Successful in 14m18s
Independent Staging Quality Gate / publish (push) Successful in 8m7s

This commit is contained in:
Jesse_Chen
2026-08-13 16:58:22 +08:00
parent ef591f673e
commit e8952dcc75
3 changed files with 20 additions and 5 deletions
+15
View File
@@ -3123,3 +3123,18 @@
- 防复发:首页编辑型主标题与产品入口标题必须继续使用设计系统的展示字体 token;通用去风格化或排版重构不得把展示标题批量替换成正文无衬线字体。
- 相关记录:BUG-026
- 修复版本:本次 staging 发布提交(精确 SHA 以提交与部署结果为准)
## BUG-184 | 生时校正完成凭证显示在 Agent 正文下方
- 状态:resolved(本地候选,待 staging 部署与登录态视觉验收)
- 首次发现:2026-08-13
- 最近更新:2026-08-13
- 影响面:V9 生时校正每轮完成后的“本轮完成 · 查看详情”执行凭证与 Agent 正文阅读顺序。
- 用户现象:执行凭证显示在整段 Agent 回复之后,与运行中 Activity 位于 Agent 消息上方的空间关系不一致;用户需要读完正文后才看到本轮执行状态。
- 触发条件:生时校正消息完成且持久化了公开 `completedReceipt`
- 根因:消息容器的 JSX 顺序固定为 `ChatMessageRow` 后渲染 `CompletedActivityReceipt`,并由静态合同测试锁定了错误的下方顺序。
- 修复:在同一消息容器内先渲染 settled 状态的 `CompletedActivityReceipt`,再渲染 `ChatMessageRow`;运行中的真实 Activity 仍由消息行自身在正文上方展示,不改变公开事件协议、折叠行为或凭证内容。
- 验证:更新 Agentic rectification DOM 合同测试,明确要求 `CompletedActivityReceipt` 位于 `ChatMessageRow` 之前;聚焦测试、目标 ESLint、生产构建、`git diff --check` 与 staging 精确 SHA 验证按发布结果记录。
- 防复发:生时校正的运行中状态与完成凭证都必须位于对应 Agent 正文上方;不得仅通过 CSS `order` 视觉重排而保留错误的 DOM/读屏顺序。
- 相关记录:BUG-172、BUG-181
- 修复版本:本次 staging 发布提交(精确 SHA 以提交与部署结果为准)
@@ -423,6 +423,9 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
)}
{messages.map((message) => (
<div key={message.renderKey} className="rectification-message-wrap">
{message.state === "settled" && message.completedReceipt && (
<CompletedActivityReceipt receipt={message.completedReceipt} />
)}
<ChatMessageRow
message={{
...message,
@@ -432,9 +435,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
}}
showActivity={message.state === "thinking" || Boolean(message.activeActivity)}
/>
{message.state === "settled" && message.completedReceipt && (
<CompletedActivityReceipt receipt={message.completedReceipt} />
)}
</div>
))}
{candidateResult?.selectionAllowed && candidateResult.candidates.length > 0 && (
@@ -181,7 +181,7 @@ test("Agentic rectification scrolls the conversation container as streamed messa
assert.doesNotMatch(chat, /conversationEnd|scrollIntoView/);
});
test("rectification activity separates live work from the receipt below the Agent message", () => {
test("rectification activity separates live work from the receipt above the Agent message", () => {
const activityHelper = chat.slice(
chat.indexOf("function completedReceiptFromPersisted"),
chat.indexOf("export function RectificationAgenticChat"),
@@ -204,7 +204,7 @@ test("rectification activity separates live work from the receipt below the Agen
chat.indexOf('{messages.map((message) => ('),
chat.indexOf('{candidateResult?.selectionAllowed'),
);
assert.ok(messageRender.indexOf("<ChatMessageRow") < messageRender.indexOf("<CompletedActivityReceipt"));
assert.ok(messageRender.indexOf("<CompletedActivityReceipt") < messageRender.indexOf("<ChatMessageRow"));
assert.match(completedActivityReceipt, /<details className=/);
assert.doesNotMatch(completedActivityReceipt, /<details[^>]*\sopen/);
assert.match(completedActivityReceipt, /本轮完成 · \$\{stepLabels\.length\} 个步骤 · \$\{receipt\.methods\.length\} 项计算依据/);