fix(rectification): restore message actions and retry receipts
Independent Staging Quality Gate / validate (push) Failing after 30m22s
Independent Staging Quality Gate / publish (push) Has been skipped

This commit is contained in:
Jesse_Chen
2026-08-14 00:10:22 +08:00
parent e8952dcc75
commit 2e17ccad64
17 changed files with 1225 additions and 60 deletions
@@ -24,6 +24,13 @@ const completedActivityReceipt = readFileSync(
new URL("../src/components/completed-activity-receipt.tsx", import.meta.url),
"utf8",
);
const regenerateRoute = readFileSync(
new URL(
"../src/app/api/rectification/cases/[caseId]/turns/[turnId]/regenerate/route.ts",
import.meta.url,
),
"utf8",
);
test("birth-time rectification entry mounts the V9 case-ref chat", () => {
assert.match(component, /return <RectificationAgenticChat \{\.\.\.props\} \/>/);
@@ -193,18 +200,21 @@ test("rectification activity separates live work from the receipt above the Agen
assert.match(activityHelper, /filter\(isPublicRectificationMethod\)/);
assert.match(chat, /activeActivity\?: PublicActivity/);
assert.match(chat, /completedReceipt\?: CompletedActivityReceiptView/);
assert.match(chat, /showActivity=\{message\.state === "thinking" \|\| Boolean\(message\.activeActivity\)\}/);
assert.match(chat, /showActivity=\{displayedMessage\.state === "thinking" \|\| Boolean\(displayedMessage\.activeActivity\)\}/);
assert.match(chat, /"rectification-read-case": "正在读取校正记录…"/);
assert.match(chat, /event\.type === "tool\.activity"/);
assert.match(chat, /event\.status === "started"/);
assert.match(chat, /event\.status === "completed"/);
assert.match(chat, /event\.status === "failed"/);
assert.match(chat, /event\.status !== "completed" && event\.status !== "failed"/);
const messageRender = chat.slice(
chat.indexOf('{messages.map((message) => ('),
chat.indexOf('{candidateResult?.selectionAllowed'),
chat.indexOf("{messages.map((message) => {"),
chat.indexOf("{candidateResult?.selectionAllowed"),
);
assert.ok(messageRender.indexOf("<CompletedActivityReceipt") < messageRender.indexOf("<ChatMessageRow"));
const receiptIndex = messageRender.indexOf("<CompletedActivityReceipt");
const replyIndex = messageRender.indexOf("<ChatMessageRow");
assert.ok(receiptIndex >= 0 && replyIndex >= 0);
assert.ok(receiptIndex < replyIndex);
assert.match(completedActivityReceipt, /<details className=/);
assert.doesNotMatch(completedActivityReceipt, /<details[^>]*\sopen/);
assert.match(completedActivityReceipt, /本轮完成 · \$\{stepLabels\.length\} 个步骤 · \$\{receipt\.methods\.length\} 项计算依据/);
@@ -223,6 +233,30 @@ test("rectification activity separates live work from the receipt above the Agen
assert.doesNotMatch(receiptRule, /border:/);
});
test("completed Agent replies restore feedback, copy and safe in-place regeneration actions", () => {
for (const label of ["赞", "踩", "复制回答", "重新生成回答"]) {
assert.match(chat, new RegExp(`aria-label="${label}"`));
}
assert.match(chat, /toggleRectificationFeedback/);
assert.match(chat, /navigator\.clipboard\.writeText\(message\.text\)/);
assert.match(chat, /\/turns\/\$\{encodeURIComponent\(message\.turnId\)\}\/regenerate/);
assert.match(chat, /requestId: globalThis\.crypto\.randomUUID\(\)/);
const messageRender = chat.slice(
chat.indexOf("{messages.map((message) => {"),
chat.indexOf("{candidateResult?.selectionAllowed"),
);
const receiptIndex = messageRender.indexOf("<CompletedActivityReceipt");
const replyIndex = messageRender.indexOf("<ChatMessageRow");
const actionsIndex = messageRender.indexOf('className="rectification-message-actions"');
assert.ok(receiptIndex >= 0 && replyIndex >= 0 && actionsIndex >= 0);
assert.ok(receiptIndex < replyIndex && replyIndex < actionsIndex);
assert.doesNotMatch(chat, /send\("message",\s*(?:oldText|previousText)/);
assert.doesNotMatch(regenerateRoute, /consultation-billing|reserveConsultation|billing\./);
assert.match(regenerateRoute, /regenerateV9AssistantTurn/);
});
test("candidate state renders from the snapshot API and never from sentinels", () => {
assert.match(chat, /当前可能的出生时间/);
assert.match(chat, /可以先采用一个作为当前排盘时间,也可以继续补充事件/);