Files
Jyotisha/frontend/tests/rectification-grounding-regenerate-20260927.test.ts
T
Jesse_ChenandClaude Opus 5.5 84eb23152a feat(rectification): remove the rectification regenerate button and endpoint (BUG-1056)
T2 of TASK-rectification-grounding-20260927 (product decision P1).
The rectification 「重试(重新生成)」 was a blind agent.generate rewrite that
persisted unchecked text (invented range / fit rate reproduced). Removed:
the regenerate route, regenerate-turn.ts, the regeneration agent and its
read-only tool set, the client regenerate action/state and the
regenerating/canRegenerate props. ChatMessageActions renders the regenerate
button only when onRegenerate is passed; ordinary consultation is unchanged.
The DB function regenerate_agentic_rectification_turn is kept (AGENTS §7.6,
retire in a later round). DESIGN.md / VOICE.md updated; source-contract
tests follow with 原值/新值/原因 notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-09-27 03:28:27 +08:00

88 lines
3.8 KiB
TypeScript

/**
* 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<RectificationMessageActions>() 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);
});