fix(rectification): isolate receipt activity after staging sync
Staging Backend Quality Gate / validate (push) Failing after 11m36s
Staging Backend Quality Gate / publish (push) Has been skipped

This commit is contained in:
Jesse
2026-08-11 18:22:30 +08:00
parent b04dd1a9e0
commit 5dd0ab66a9
2 changed files with 24 additions and 7 deletions
+16
View File
@@ -2832,3 +2832,19 @@
- 防复发:引擎适配器必须有真实响应形状的契约测试;新增任何映射层必须对照 `scripts/rectification/contracts.py:SCOREABLE_EVENT_KINDS`;RPC 重放/幂等语义以“先重放后校验、重放校验已落库状态”为唯一实现顺序;迁移修改必须在真实 PostgreSQL 上从空库全量应用并重跑。
- 相关记录:BUG-163、BUG-112
- 修复版本:本地 staging 候选(未 push / deploy
## BUG-165 | 合并咨询 Agentic runtime 后生时校正执行步骤复用共享 activity 字段导致构建失败
- 状态:resolvedlocal candidate,待 staging gate
- 首次发现:2026-08-11
- 最近更新:2026-08-11
- 影响面:`RectificationAgenticChat`、共享 `ChatMessageView`、Next.js production type-check
- 用户现象:生时校正与最新 staging 的咨询 Agentic runtime 单独测试均通过,但合并后 `next build``rectification-agentic-chat.tsx``string[]` 不能赋给结构化 `AgentActivityView`
- 触发条件:将 V9 生时校正分支合入包含咨询 Agentic runtime 的最新 `origin/staging`,再运行 production build。
- 根因:并行开发期间,共享 `ChatMessageView.activity` 从通用状态扩展为咨询 runtime 使用的单个 `{ phase, label }`;生时校正把执行回执的多步骤 `string[]` 也命名为 `activity`。聚焦源码/运行测试没有跨两个 runtime 做 TypeScript 组合检查,只有 production build 暴露交叉类型冲突。
- 修复:生时校正的持久化/流式回执步骤改为独立 `receiptActivity: readonly string[]`,只用于折叠的“本轮做了什么”;共享 `activity` 继续由咨询 runtime 独占并保持 `{ phase, label }` 合同,不改其 UI 与状态映射。
- 验证:合并最新 staging 后,`rectification-v9-stream``rectification-agentic-entry``chat-stream-layout` 共 36/36 通过;目标 ESLint 通过;Next.js 16.2.10 production build 完成 type-check、60 个静态页面生成与 route 列表收口;`git diff --check` 通过。
- 防复发:生时校正执行回执与咨询即时 activity 必须使用不同字段;任何同时修改 `ChatMessageView` 与专用聊天 surface 的分支,合并最新 staging 后必须运行共享 layout 测试和 production build,不能只依赖各自聚焦测试。
- 相关记录:BUG-162、BUG-163、BUG-164
- 复发自:无(并行 runtime 合并冲突)
- 修复版本:本次 staging 候选
@@ -57,7 +57,8 @@ type RectificationAgenticChatProps = Readonly<{
type RenderMessage = ChatMessageView & {
renderKey: string;
activity?: readonly string[];
/** V9 execution-receipt steps; separate from shared consultation activity. */
receiptActivity?: readonly string[];
receiptStatus?: string;
};
@@ -119,7 +120,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
text: turn.text ?? "",
renderKey: key,
state: turn.status === "completed" ? "settled" : "thinking",
activity: activityFromReceipt(turn.receipt),
receiptActivity: activityFromReceipt(turn.receipt),
receiptStatus: turn.receipt?.status,
}];
}
@@ -212,7 +213,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
...(action === "message"
? [{ role: "user", text: trimmed, renderKey: userRenderKey, state: "settled" } satisfies RenderMessage]
: []),
{ role: "assistant", text: "", renderKey: assistantRenderKey, state: "thinking", activity: [] },
{ role: "assistant", text: "", renderKey: assistantRenderKey, state: "thinking", receiptActivity: [] },
]);
setDraft("");
@@ -293,7 +294,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
activitySet.add(event.type);
liveActivity = [...liveActivity, labelForPhase(event.type)];
setMessages((current) => current.map((message) => message.renderKey === assistantRenderKey
? { ...message, activity: liveActivity }
? { ...message, receiptActivity: liveActivity }
: message));
}
}
@@ -304,7 +305,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
const succeeded = completed && !streamFailed && Boolean(parsed.text);
setMessages((current) => succeeded
? current.map((message) => message.renderKey === assistantRenderKey
? { ...message, text: parsed.text, suggestions: parsed.suggestions, state: "settled", activity: liveActivity }
? { ...message, text: parsed.text, suggestions: parsed.suggestions, state: "settled", receiptActivity: liveActivity }
: message)
: current.filter((message) => message.renderKey !== assistantRenderKey));
setSuggestions(succeeded ? parsed.suggestions : []);
@@ -386,11 +387,11 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
{messages.map((message) => (
<div key={message.renderKey} className="rectification-message-wrap">
<ChatMessageRow message={message} />
{message.activity && message.activity.length > 0 && (
{message.receiptActivity && message.receiptActivity.length > 0 && (
<details className="rectification-activity">
<summary></summary>
<ol>
{message.activity.map((step) => <li key={step}>{step}</li>)}
{message.receiptActivity.map((step) => <li key={step}>{step}</li>)}
</ol>
</details>
)}