fix(rectification): stop re-parsing settled messages on every stream frame
This commit is contained in:
@@ -12,6 +12,10 @@ const chat = readFileSync(
|
||||
new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const messageEntry = readFileSync(
|
||||
new URL("../src/components/rectification-message-entry.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const activityStatus = readFileSync(
|
||||
new URL("../src/components/agent-activity-status.tsx", import.meta.url),
|
||||
"utf8",
|
||||
@@ -352,10 +356,13 @@ test("rectification keeps receipts for the varga sentence and shows live tool pr
|
||||
assert.match(activityHelper, /filter\(isPublicRectificationTool\)/);
|
||||
assert.match(activityHelper, /receipt\.methods/);
|
||||
assert.match(activityHelper, /filter\(isPublicRectificationMethod\)/);
|
||||
assert.match(chat, /completedReceipt\?: CompletedActivityReceiptView/);
|
||||
assert.match(chat, /showActivity=\{displayedMessage\.state !== "settled"\}/);
|
||||
assert.match(chat, /vargaSentenceFromMethods/);
|
||||
assert.match(chat, /vargaSentence=\{vargaSentence\}/);
|
||||
// 原值: 上述 token 都在 rectification-agentic-chat.tsx 的 messages.map 内。
|
||||
// 新值: RenderMessage / 时间轴 / varga 句随行组件抽出。
|
||||
// 原因: BUG-725 已结算行记忆化,派生不得留在容器每帧计算。
|
||||
assert.match(messageEntry, /completedReceipt\?: CompletedActivityReceiptView/);
|
||||
assert.match(messageEntry, /showActivity=\{displayedMessage\.state !== "settled"\}/);
|
||||
assert.match(messageEntry, /vargaSentenceFromMethods/);
|
||||
assert.match(messageEntry, /vargaSentence=\{vargaSentence\}/);
|
||||
assert.match(chat, /activityTraceFromReceipt/);
|
||||
assert.match(activityStatus, /vargaSentence/);
|
||||
assert.match(activityStatus, /VargaTraceStep/);
|
||||
@@ -375,9 +382,14 @@ test("rectification keeps receipts for the varga sentence and shows live tool pr
|
||||
chat.indexOf("{messages.map((message) => {"),
|
||||
chat.indexOf("{savedTime &&"),
|
||||
);
|
||||
const replyIndex = messageRender.indexOf("<ChatMessageRow");
|
||||
// 原值: `<ChatMessageRow` 写在容器 messages.map 里。
|
||||
// 新值: 行渲染在 `rectification-message-entry.tsx`,容器 map 只挂行组件。
|
||||
// 原因: BUG-725 抽出 memo 行。
|
||||
const replyIndex = messageEntry.indexOf("<ChatMessageRow");
|
||||
assert.ok(replyIndex >= 0);
|
||||
assert.match(messageRender, /<RectificationMessageEntry/);
|
||||
assert.doesNotMatch(messageRender, /<RectificationHouseTableView/);
|
||||
assert.doesNotMatch(messageEntry, /<RectificationHouseTableView/);
|
||||
// Former locks on `completed-activity-receipt.tsx` (`<details>`, never open, the "本轮完成 · N 个
|
||||
// 步骤" summary): that was a second collapsed block under every settled rectification reply.
|
||||
// Its methods now sit as source chips on the shared timeline's calculate row (BUG-476).
|
||||
@@ -397,7 +409,10 @@ test("rectification keeps receipts for the varga sentence and shows live tool pr
|
||||
assert.match(chat, /completeActivityTraceStep/);
|
||||
// Former lock: `activityStatus` contained `activityTrace` — the rectification-only trace panel.
|
||||
// The trace is now projected onto the shared timeline rows (BUG-476); the panel is a fallback.
|
||||
assert.match(chat, /rectificationTimelineRows/);
|
||||
// 原值: chat 源码直接调用 rectificationTimelineRows。
|
||||
// 新值: 调用点在行组件内。
|
||||
// 原因: BUG-725 容器不再每帧派生时间轴行。
|
||||
assert.match(messageEntry, /rectificationTimelineRows/);
|
||||
assert.doesNotMatch(activityStatus, /activityTrace/);
|
||||
assert.match(activityStatus, /className="message-thinking"/);
|
||||
assert.match(activityStatus, />思考</);
|
||||
@@ -409,7 +424,10 @@ test("completed Agent replies restore feedback, copy and safe in-place regenerat
|
||||
for (const label of ["赞", "踩", "复制回答", "重新生成回答"]) {
|
||||
assert.match(messageActions, new RegExp(`aria-label="${label}"`));
|
||||
}
|
||||
assert.match(chat, /<ChatMessageActions/);
|
||||
// 原值: `<ChatMessageActions` 写在容器 messages.map 里。
|
||||
// 新值: 动作条随行组件抽出;容器仍切换 feedback。
|
||||
// 原因: BUG-725 动作回调走 actionsRef,组件本身仍要渲染动作条。
|
||||
assert.match(messageEntry, /<ChatMessageActions/);
|
||||
assert.match(chat, /toggleRectificationFeedback/);
|
||||
assert.match(chat, /navigator\.clipboard\.writeText\(copyTextForMessage\(message\.text, message\.question\)\)/);
|
||||
assert.match(chat, /\/turns\/\$\{encodeURIComponent\(message\.turnId\)\}\/regenerate/);
|
||||
@@ -419,11 +437,12 @@ test("completed Agent replies restore feedback, copy and safe in-place regenerat
|
||||
chat.indexOf("{messages.map((message) => {"),
|
||||
chat.indexOf("{savedTime &&"),
|
||||
);
|
||||
const replyIndex = messageRender.indexOf("<ChatMessageRow");
|
||||
const actionsIndex = messageRender.indexOf("<ChatMessageActions");
|
||||
const replyIndex = messageEntry.indexOf("<ChatMessageRow");
|
||||
const actionsIndex = messageEntry.indexOf("<ChatMessageActions");
|
||||
assert.ok(replyIndex >= 0 && actionsIndex >= 0);
|
||||
assert.ok(replyIndex < actionsIndex);
|
||||
assert.doesNotMatch(messageRender, /<CompletedActivityReceipt/);
|
||||
assert.doesNotMatch(messageEntry, /<CompletedActivityReceipt/);
|
||||
|
||||
assert.doesNotMatch(chat, /send\("message",\s*(?:oldText|previousText)/);
|
||||
assert.doesNotMatch(regenerateRoute, /consultation-billing|reserveConsultation|billing\./);
|
||||
@@ -486,7 +505,10 @@ test("the natal house table is a live board beside the chat, not a message", ()
|
||||
board.indexOf("className=\"rectification-snapshot\""),
|
||||
board.indexOf("{scoringMinutes.length > 0 || displayMinutes.length > 0"),
|
||||
);
|
||||
const rowIndex = messageLoop.indexOf("<ChatMessageRow");
|
||||
// 原值: `<ChatMessageRow` 在容器 map 循环里。
|
||||
// 新值: 循环渲染 `RectificationMessageEntry`,ChatMessageRow 在行组件内。
|
||||
// 原因: BUG-725。
|
||||
const rowIndex = messageLoop.indexOf("<RectificationMessageEntry");
|
||||
const snapshotIndex = snapshot.indexOf("className=\"rectification-snapshot\"");
|
||||
const houseIndex = snapshot.indexOf("<RectificationHouseTableView");
|
||||
assert.ok(rowIndex >= 0);
|
||||
@@ -582,12 +604,18 @@ test("rectification composer can stop a live agent run", () => {
|
||||
// 新值:isAbortError(caught)
|
||||
// 原因:选择题与采用路径共用同一中止判定(BUG-552)
|
||||
assert.match(chat, /isAbortError\(caught\)/);
|
||||
assert.match(chat, /showActivity=\{displayedMessage\.state !== "settled"\}/);
|
||||
// 原值: 容器 map 写 `showActivity={displayedMessage.state !== "settled"}`。
|
||||
// 新值: 同一表达式在行组件内。
|
||||
// 原因: BUG-725。
|
||||
assert.match(messageEntry, /showActivity=\{displayedMessage\.state !== "settled"\}/);
|
||||
});
|
||||
|
||||
test("aborting a live run marks the bubble stopped without an alert", () => {
|
||||
assert.match(chat, /failed: false,\s*stopped: true/);
|
||||
assert.match(chat, /stoppedNotice=\{message\.stopped \? RECTIFICATION_STOPPED_NOTICE : undefined\}/);
|
||||
// 原值: 停止提示 prop 写在容器 map 的 ChatMessageRow 上。
|
||||
// 新值: 同一表达式在行组件内。
|
||||
// 原因: BUG-725。
|
||||
assert.match(messageEntry, /stoppedNotice=\{message\.stopped \? RECTIFICATION_STOPPED_NOTICE : undefined\}/);
|
||||
assert.doesNotMatch(chat, /role="alert"[\s\S]{0,80}RECTIFICATION_STOPPED_NOTICE/);
|
||||
const choiceFetch = chat.slice(chat.indexOf("const submitStructuredChoice"), chat.indexOf("const acceptCandidate"));
|
||||
assert.match(choiceFetch, /runAbort\.current = abortController/);
|
||||
@@ -609,9 +637,13 @@ test("time-selection cards use server adoption state and stay mutually exclusive
|
||||
chat.indexOf("{messages.map((message) => {"),
|
||||
chat.indexOf("{savedTime &&"),
|
||||
);
|
||||
const actionsIndex = messageLoop.indexOf("<ChatMessageActions");
|
||||
// 原值: ChatMessageActions 与 RangeDelivery 都在容器 map 里,且 actions 在卡之前。
|
||||
// 新值: 动作条在行组件内;交付卡仍在容器 map、排在行组件之后。
|
||||
// 原因: BUG-725 行组件抽出,交付卡不属于逐消息 Markdown 边界。
|
||||
const entryIndex = messageLoop.indexOf("<RectificationMessageEntry");
|
||||
const cardsIndex = messageLoop.indexOf("<RectificationRangeDelivery");
|
||||
assert.ok(actionsIndex >= 0 && cardsIndex > actionsIndex);
|
||||
assert.ok(entryIndex >= 0 && cardsIndex > entryIndex);
|
||||
assert.match(messageEntry, /<ChatMessageActions/);
|
||||
// 旧:showSelectionCards 只看 selectionAllowed && canAdopt && !showLiveChoiceCard && !busy,
|
||||
// 锚在 latestSettledAssistant。采集题没有 A–D 卡,所以 collect_evidence 会出采用卡;
|
||||
// 采用后 busy 把卡藏掉,新消息又把卡带走。
|
||||
@@ -644,11 +676,18 @@ test("time-selection cards use server adoption state and stay mutually exclusive
|
||||
// 新值: 还要有 candidateResult 才渲染行选卡
|
||||
// 原因: BUG-595 决策 1
|
||||
assert.match(messageLoop, /showSelectionCards && candidateResult && message\.renderKey === selectionCardMessageKey/);
|
||||
// 原值: 嵌入卡 token(RectificationChoiceCard / variant / choiceNonce / answer_option)在容器 map。
|
||||
// 新值: 嵌入卡在行组件;容器 map 仍有 persisted 题卡的 RectificationChoiceCard。
|
||||
// 原因: BUG-725。
|
||||
assert.match(messageEntry, /<RectificationChoiceCard/);
|
||||
assert.match(messageEntry, /variant="embedded"/);
|
||||
assert.match(messageEntry, /choiceNonce/);
|
||||
assert.match(messageEntry, /question\.answer_option/);
|
||||
assert.match(messageLoop, /<RectificationChoiceCard/);
|
||||
assert.match(messageLoop, /variant="embedded"/);
|
||||
assert.match(messageLoop, /choiceNonce/);
|
||||
assert.match(messageLoop, /question\.answer_option/);
|
||||
assert.doesNotMatch(messageLoop, /rectification-question-slot/);
|
||||
assert.doesNotMatch(messageEntry, /rectification-question-slot/);
|
||||
assert.doesNotMatch(choiceCardComponent, /setSelectedKey\(""\)/);
|
||||
assert.match(choiceCardComponent, /selectedKey/);
|
||||
assert.match(choiceCardComponent, /is-answered/);
|
||||
@@ -664,8 +703,12 @@ test("time-selection cards use server adoption state and stay mutually exclusive
|
||||
assert.doesNotMatch(chat, /send\("message", choiceCard\?\.stop_message/);
|
||||
assert.doesNotMatch(chat, /choiceCardUserMessage/);
|
||||
assert.match(caseProjection, /choice_card: choiceCardFromCaseDossier/);
|
||||
assert.match(chat, /choiceCard\s+&& choiceCard\.focus_id === question\.focus_id/);
|
||||
assert.match(chat, /disabled=\{!liveQuestion\}/);
|
||||
// 原值: 容器 map 用 `choiceCard && choiceCard.focus_id === question.focus_id` 和 `disabled={!liveQuestion}`。
|
||||
// 新值: 行组件用 liveChoiceCard / liveQuestion;容器的 live 卡判定仍比对 latestLiveQuestion。
|
||||
// 原因: BUG-725。
|
||||
assert.match(messageEntry, /liveChoiceCard\s+&& liveChoiceCard\.focus_id === question\.focus_id/);
|
||||
assert.match(messageEntry, /disabled=\{!liveQuestion\}/);
|
||||
assert.match(chat, /choiceCard\s+&& choiceCard\.focus_id === latestLiveQuestion\.focus_id/);
|
||||
assert.match(caseProjection, /current_question: projectCurrentQuestion/);
|
||||
assert.match(caseProjection, /overlayPublicDecision/);
|
||||
assert.match(caseProjection, /const fields = publicDecisionFields\(decision\)/);
|
||||
|
||||
Reference in New Issue
Block a user