/** * BUG-1056 (TASK-rectification-grounding-20260927 T2): the rectification * 「重试(重新生成)」 is gone. The rewrite was a blind `agent.generate` with no * question stripping, range sentence, number or word checks, persisted as is. * Ordinary consultation keeps its regenerate button. The database function * `regenerate_agentic_rectification_turn` is deliberately left in place * (AGENTS §7.6: code first, retire the function in a later round). */ import assert from "node:assert/strict"; import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import { createElement, createRef } from "react"; import { renderToString } from "react-dom/server"; import test from "node:test"; import { ChatMessageActions } from "../src/components/chat-message-actions.tsx"; import { RectificationMessageEntry, type RectificationMessageActions, type RectificationMessageEntryProps, } from "../src/components/rectification-message-entry.tsx"; const frontendRoot = fileURLToPath(new URL("../", import.meta.url)); function sourceFiles(directory: string): string[] { const files: string[] = []; for (const name of readdirSync(directory)) { const path = join(directory, name); if (statSync(path).isDirectory()) files.push(...sourceFiles(path)); else if (/\.(ts|tsx)$/.test(path)) files.push(path); } return files; } function actionsRef(): RectificationMessageEntryProps["actionsRef"] { const ref = createRef() as RectificationMessageEntryProps["actionsRef"]; ref.current = { submitChoice() {}, submitStop() {}, copyMessage() {}, onFeedback() {} }; return ref; } test("a settled rectification reply shows 赞 / 踩 / 复制 and no 重新生成", () => { const html = renderToString(createElement(RectificationMessageEntry, { message: { role: "assistant", text: "记下了:2016 年 9 月入学。", renderKey: "a1", state: "settled", turnId: "t1" }, busy: false, readonly: false, currentQuestionFocusId: null, interactive: true, liveChoiceCard: null, choiceNonce: 0, savedTime: null, copied: false, feedback: undefined, actionsRef: actionsRef(), })); assert.match(html, /aria-label="赞"/); assert.match(html, /aria-label="复制回答"/); assert.doesNotMatch(html, /重新生成/); }); test("the ordinary consultation action bar still offers 重新生成回答", () => { const html = renderToString(createElement(ChatMessageActions, { canRegenerate: true, onFeedback() {}, onCopy() {}, onRegenerate() {}, })); assert.match(html, /aria-label="重新生成回答"/); const transcript = readFileSync(new URL("../src/components/chat-transcript.tsx", import.meta.url), "utf8"); assert.match(transcript, /onRegenerate=\{\(\) => actionsRef\.current\.onRegenerate\(message\.renderKey\)\}/); }); test("the rectification regenerate endpoint and its server code are removed; the DB function is kept", () => { assert.equal(existsSync(join(frontendRoot, "src/app/api/rectification/cases/[caseId]/turns")), false); assert.equal(existsSync(join(frontendRoot, "src/lib/rectification-agentic/v9/regenerate-turn.ts")), false); for (const file of sourceFiles(join(frontendRoot, "src"))) { const text = readFileSync(file, "utf8"); assert.doesNotMatch(text, /regenerate_agentic_rectification_turn|get_agentic_rectification_turn_regeneration/, file); assert.doesNotMatch(text, /\/api\/rectification\/cases\/[^"`']*\/regenerate/, file); assert.doesNotMatch(text, /getRectificationV9RegenerationAgent|regenerateV9AssistantTurn/, file); } const migration = readFileSync( join(frontendRoot, "supabase/migrations/20260813060000_rectification_turn_regeneration.sql"), "utf8", ); assert.match(migration, /function public\.regenerate_agentic_rectification_turn/i); });