About 30 fixed rectification lines lose the receipt tone: no 「已记录。」 openings (收到 when the range holds, 好 when it moves), 麻烦 instead of 请, 时间 instead of 候选 in what the user reads, and no 「新建对话」 after adoption. Numbers, clocks, ranges and the honesty boundaries are unchanged. CHOICE_ACK_RE also recognises the new acknowledgements, so a model sentence that repeats one is still dropped; the delivery guard matches the delivery sentence by containment and does not depend on the opening. Full suite 4973 / fail 24, identical to 61d6f248; build keeps / Static. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
125 lines
7.5 KiB
TypeScript
125 lines
7.5 KiB
TypeScript
import assert from "node:assert/strict";
|
||
import { readFileSync } from "node:fs";
|
||
import test from "node:test";
|
||
import { createElement } from "react";
|
||
import { renderToStaticMarkup } from "react-dom/server";
|
||
|
||
import {
|
||
deliveryTurnNarration,
|
||
postAdoptVerifyDoneCopy,
|
||
RECTIFICATION_USER_COPY,
|
||
} from "../src/lib/rectification-agentic/user-copy.ts";
|
||
import {
|
||
alreadyDelivered,
|
||
markDeliveryTurn,
|
||
resetDeliveryTurnGuardForTests,
|
||
} from "../src/lib/rectification-agentic/v9/delivery-turn-guard.ts";
|
||
import { RectificationVerificationReport } from "../src/components/rectification-verification-report.tsx";
|
||
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
|
||
import { rectificationAgentRouteSurface } from "./rectification-agent-route-surface.ts";
|
||
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
|
||
|
||
test("delivery body is three sentences and omits the eight-method report", () => {
|
||
const text = deliveryTurnNarration({
|
||
credibleRange: ["04:51", "04:59"],
|
||
representativeTime: "04:53",
|
||
eventCount: 7,
|
||
fitPercent: 80,
|
||
});
|
||
// 原值: 这次给出的范围 04:51–04:59。对照了 7 件经历…
|
||
// 新值: 目前范围 04:51–04:59。对照了 7 件经历…
|
||
// 原因: BUG-654 禁用「这次给出」
|
||
// 原值(2): 「目前范围 04:51–04:59。对照了 7 件经历,事件吻合率 80%。这只是代表性候选…」
|
||
// 新值(2): 「目前范围 04:51–04:59。这只是代表性候选…」(没有计分选择题时第二句省略)
|
||
// 原因(2): BUG-1085 交付正文不数口述经历、不写吻合率(2026-09-29 D2)
|
||
assert.equal(text, "目前范围 04:51–04:59。这只是代表性候选,不是已确认的唯一出生分钟。");
|
||
assert.doesNotMatch(text, /方法1/);
|
||
assert.doesNotMatch(text, /Technique Audit/);
|
||
assert.doesNotMatch(text, /排盘用/);
|
||
});
|
||
|
||
test("skipping the last reverse-verify keeps the original post-adopt sentence", () => {
|
||
assert.equal(
|
||
postAdoptVerifyDoneCopy("05:07", true),
|
||
RECTIFICATION_USER_COPY.postAdoptVerifyDone,
|
||
);
|
||
assert.equal(
|
||
postAdoptVerifyDoneCopy("05:07", false),
|
||
// 原值: 已采用 05:07,新建对话即按这个时间排盘。 / 新值: 已采用 05:07,之后的对话都按这个时间来看。 / 原因: BUG-1258:校正固定文案改为知心姐姐口吻(产品 2026-10-07 认可样稿)
|
||
"已采用 05:07,之后的对话都按这个时间来看。",
|
||
);
|
||
});
|
||
|
||
test("verification report fold is closed and renders a real table", () => {
|
||
const html = renderToStaticMarkup(createElement(RectificationVerificationReport, {
|
||
markdown: "| 方法 | 结果 |\n| --- | --- |\n| 1 | 吻合 |",
|
||
}));
|
||
assert.match(html, /<details/);
|
||
assert.doesNotMatch(html, /<details[^>]*\sopen/);
|
||
assert.match(html, /查看验证报告/);
|
||
assert.match(html, /<table/);
|
||
});
|
||
|
||
test("already_delivered blocks a second no-message run for the same result within 60s", () => {
|
||
resetDeliveryTurnGuardForTests();
|
||
const caseId = "11111111-1111-4111-8111-111111111111";
|
||
const resultId = "22222222-2222-4222-8222-222222222222";
|
||
assert.equal(alreadyDelivered({ caseId, resultId, hasUserMessage: false, now: 1_000 }), false);
|
||
markDeliveryTurn({ caseId, resultId, now: 1_000 });
|
||
assert.equal(alreadyDelivered({ caseId, resultId, hasUserMessage: false, now: 1_500 }), true);
|
||
assert.equal(alreadyDelivered({ caseId, resultId, hasUserMessage: true, now: 1_500 }), false);
|
||
assert.equal(alreadyDelivered({
|
||
caseId,
|
||
resultId: "33333333-3333-4333-8333-333333333333",
|
||
hasUserMessage: false,
|
||
now: 1_500,
|
||
}), false);
|
||
assert.equal(alreadyDelivered({ caseId, resultId, hasUserMessage: false, now: 62_000 }), false);
|
||
resetDeliveryTurnGuardForTests();
|
||
});
|
||
|
||
test("agent-run returns already_delivered before billing and the route skips a second turn", () => {
|
||
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
|
||
const agentRun = rectificationAgentRunSurface;
|
||
// 原值: 读 app/api/rectification/agent/route.ts 单文件。新值: rectificationAgentRouteSurface(route.ts + 拆出的 agent-route-*.ts,见 rectification-agent-route-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
|
||
const route = rectificationAgentRouteSurface;
|
||
const exit = readFileSync(new URL("../src/lib/rectification-agentic/v9/turn-exit.ts", import.meta.url), "utf8");
|
||
const guardStart = agentRun.indexOf("alreadyDelivered({");
|
||
const reserve = agentRun.indexOf("const reserve = await billing.reserve()");
|
||
assert.ok(guardStart >= 0 && guardStart < reserve);
|
||
assert.match(agentRun, /errorCode: "already_delivered"/);
|
||
assert.match(route, /result\.errorCode === "already_delivered"/);
|
||
assert.match(route, /trigger: "opening"/);
|
||
assert.match(exit, /trigger: "finalizeSuccessfulTurnExit"/);
|
||
// 原值: assert.match(regenerate 路由, /trigger: "regenerate"/)。新值: 删除该断言。
|
||
// 原因: BUG-1056(TASK-rectification-grounding-20260927 P1)删除生时校正的重新生成接口与按钮,交付轮守卫的 "regenerate" 触发源随之删除;其余三处触发源断言不变。
|
||
assert.match(route, /logRectificationDeliveryTurn/);
|
||
});
|
||
|
||
test("agent-run trims evidence after persistNextInterviewIfIdle so delivery can keep three sentences", () => {
|
||
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
|
||
const agentRun = rectificationAgentRunSurface;
|
||
const persistIdx = agentRun.lastIndexOf("persistNextInterviewIfIdle({");
|
||
const trimIdx = agentRun.lastIndexOf("trimSpokenTurnForInterview(");
|
||
const replaceIdx = agentRun.lastIndexOf('type: "answer.delta", text: spokenAnswer, replace: true');
|
||
assert.ok(persistIdx >= 0 && trimIdx > persistIdx);
|
||
assert.ok(replaceIdx > trimIdx);
|
||
assert.match(agentRun, /trimSpokenTurnForInterview\(answerText, interviewIdle\?\.terminalNote === true\)/);
|
||
});
|
||
|
||
test("composer wrap no longer hosts 先这样 or the adopt status bar", () => {
|
||
// 原值: 读 rectification-agentic-chat.tsx 单文件。新值: rectificationChatSurface(容器 + 拆出的 hook / 参数函数 / 块,见 rectification-chat-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改为调用函数。
|
||
const chat = rectificationChatSurface;
|
||
const wrap = chat.slice(chat.indexOf("className=\"composer-wrap\""), chat.indexOf("className=\"composer-footer\""));
|
||
assert.doesNotMatch(wrap, /CHOICE_STOP_LABEL/);
|
||
assert.doesNotMatch(wrap, /rectification-collect-stop/);
|
||
assert.doesNotMatch(wrap, /rectification-composer-meta/);
|
||
assert.doesNotMatch(wrap, /rectification-adopt-status/);
|
||
// 原值: 容器源码含 CHOICE_STOP_LABEL(嵌入卡 stop 文案)。
|
||
// 新值: 常量在行组件的 choiceCardFromQuestion 里;composer-wrap 仍不得托管。
|
||
// 原因: BUG-725。
|
||
const messageEntry = readFileSync(new URL("../src/components/rectification-message-entry.tsx", import.meta.url), "utf8");
|
||
assert.match(messageEntry, /CHOICE_STOP_LABEL/);
|
||
assert.match(chat, /postAdoptVerifyDoneCopy/);
|
||
});
|