Files
Jyotisha/frontend/tests/rectification-collect-prompt.test.ts
T
Jesse_ChenandCursor 5c476ba784
Independent Staging Quality Gate / validate (push) Failing after 9m31s
Independent Staging Quality Gate / publish (push) Has been skipped
fix(rectification): cover all candidate minutes on the compare card and keep delivery narration (BUG-614/615)
Engine by_time ledgers now include the full candidate set so posterior columns can look up fit and windows. Delivery turns keep up to three sentences after idle persist; the card subtracts repeated traits and folds the board chart.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 18:32:03 +08:00

153 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { composeCollectSpokenAssistantText, detachCollectSpokenAssistantText, stripQuestionSentences, trimEvidenceTurnBody, trimSpokenTurnForInterview } from "../src/lib/rectification-agentic/v9/collect-prompt.ts";
import { attachQuestionsToTurns } from "../src/lib/rectification-agentic/v9/turn-question.ts";
import { GENERIC_COLLECT_QUESTION, RECTIFICATION_USER_COPY, USER_COLLECT_QUESTION } from "../src/lib/rectification-agentic/user-copy.ts";
import { CASE_ID, FOCUS_ID, TURN_ID } from "./rectification-v9-test-support.ts";
test("composeCollectSpokenAssistantText joins by exact prompt identity", () => {
const greeting = "你好,我是生时校正助手。";
const combined = `${greeting}\n\n${GENERIC_COLLECT_QUESTION}`;
assert.equal(composeCollectSpokenAssistantText(greeting, GENERIC_COLLECT_QUESTION), combined);
assert.equal(composeCollectSpokenAssistantText(combined, GENERIC_COLLECT_QUESTION), combined);
assert.equal(composeCollectSpokenAssistantText("", GENERIC_COLLECT_QUESTION), GENERIC_COLLECT_QUESTION);
assert.equal(composeCollectSpokenAssistantText(GENERIC_COLLECT_QUESTION, GENERIC_COLLECT_QUESTION), GENERIC_COLLECT_QUESTION);
assert.equal(composeCollectSpokenAssistantText(greeting, " "), greeting);
assert.equal(
composeCollectSpokenAssistantText(` ${greeting} `, ` ${GENERIC_COLLECT_QUESTION} `),
combined,
);
});
test("detachCollectSpokenAssistantText removes only an exact trailing stem", () => {
const greeting = "你好,我是生时校正助手。";
const combined = `${greeting}\n\n${GENERIC_COLLECT_QUESTION}`;
assert.equal(detachCollectSpokenAssistantText(combined, GENERIC_COLLECT_QUESTION), greeting);
assert.equal(detachCollectSpokenAssistantText(greeting, GENERIC_COLLECT_QUESTION), greeting);
assert.equal(detachCollectSpokenAssistantText(GENERIC_COLLECT_QUESTION, GENERIC_COLLECT_QUESTION), GENERIC_COLLECT_QUESTION);
assert.equal(detachCollectSpokenAssistantText("", GENERIC_COLLECT_QUESTION), "");
});
test("GET rebuild detaches a legacy composed suffix only when asked_turn_id matches", () => {
const greeting = "你好,我是生时校正助手。";
const combined = composeCollectSpokenAssistantText(greeting, GENERIC_COLLECT_QUESTION);
const turns = [{
id: TURN_ID,
role: "assistant" as const,
text: combined,
status: "completed",
}];
const linked = attachQuestionsToTurns(turns, [{
id: FOCUS_ID,
caseId: CASE_ID,
questionId: "collect:unknown:collect_method_evidence",
intent: "collect_method_evidence",
targetEvidenceId: null,
targetDomain: null,
targetKind: null,
expectedAnswerSchema: { prompt: GENERIC_COLLECT_QUESTION, collect: true },
status: "active",
askedAt: "2026-09-02T00:00:00.000Z",
resolvedAt: null,
askedTurnId: TURN_ID,
}]);
assert.equal(linked[0]?.text, greeting);
assert.equal(linked[0]?.question?.prompt, GENERIC_COLLECT_QUESTION);
const unlinked = attachQuestionsToTurns(turns, [{
id: FOCUS_ID,
caseId: CASE_ID,
questionId: "collect:unknown:collect_method_evidence",
intent: "collect_method_evidence",
targetEvidenceId: null,
targetDomain: null,
targetKind: null,
expectedAnswerSchema: { prompt: GENERIC_COLLECT_QUESTION, collect: true },
status: "active",
askedAt: "2026-09-02T00:00:00.000Z",
resolvedAt: null,
askedTurnId: null,
}]);
assert.equal(unlinked[0]?.text, combined);
assert.equal(unlinked[0]?.question, null);
});
test("composeCollectSpokenAssistantText drops a near-duplicate restatement of the stem", () => {
const stem = USER_COLLECT_QUESTION.education;
const restated = `${stem.slice(0, 12)}还记得大概哪一年吗?`;
const body = `这条记下了。${restated}`;
const composed = composeCollectSpokenAssistantText(body, stem);
assert.equal(composed.includes(restated), false);
assert.equal(composed.split(stem).length - 1, 1);
assert.ok(composed.endsWith(stem));
});
test("runtime no longer composes the stem into assistant_message", () => {
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
const attach = readFileSync(new URL("../src/lib/rectification-agentic/v9/turn-question.ts", import.meta.url), "utf8");
assert.doesNotMatch(agentRun, /composeCollectSpokenAssistantText/);
assert.match(attach, /stripQuestionSentences/);
assert.match(attach, /if \(!focus\.askedTurnId\) continue/);
});
test("stripQuestionSentences drops a rewritten trailing question and its tag", () => {
const body = "范围已经收到,收在 05:00–05:10。你大概哪一年搬过家?有年份就行。";
const stem = "你大概是哪一年搬的家?";
assert.equal(stripQuestionSentences(body, stem), "范围已经收到,收在 05:0005:10。");
});
test("stripQuestionSentences drops a mid-body stem and keeps the surrounding sentences", () => {
const stem = "你大概是哪一年搬的家?";
const body = "记下了。你大概是哪一年搬的家?范围还在 05:00–05:10。";
assert.equal(stripQuestionSentences(body, stem), "记下了。范围还在 05:0005:10。");
});
test("stripQuestionSentences returns empty when the body is only a question", () => {
const stem = "你大概是哪一年搬的家?";
assert.equal(stripQuestionSentences("你大概哪一年搬过家?", stem), "");
assert.equal(RECTIFICATION_USER_COPY.collectHandoff.includes("请回答下面的问题"), false);
});
test("attachQuestionsToTurns leaves body unchanged when the turn has no focus", () => {
const body = "范围收到 05:0005:10。你大概哪一年搬过家?";
const turns = attachQuestionsToTurns([{
id: TURN_ID,
role: "assistant" as const,
text: body,
}], []);
assert.equal(turns[0]?.text, body);
assert.equal(turns[0]?.question, null);
});
test("trimEvidenceTurnBody keeps the first sentence and strips value judgments", () => {
const three = trimEvidenceTurnBody("记下了:2016 年 9 月入学。这对校正很有帮助。接下来我们继续。");
assert.equal(three, "记下了:2016 年 9 月入学。");
assert.doesNotMatch(three, /很有帮助|很有价值|很有分量|特别有用/);
const judged = trimEvidenceTurnBody("记下了:2020 年 6 月毕业,特别有用。");
assert.doesNotMatch(judged, /特别有用/);
const two = trimEvidenceTurnBody("记下了:2016 年 9 月入学。\n\n范围收到 04:5005:10。");
assert.equal(two, "记下了:2016 年 9 月入学。\n\n范围收到 04:5005:10。");
});
test("trimSpokenTurnForInterview keeps three delivery sentences and one evidence sentence", () => {
const four = [
"这次给出的范围 04:4904:53。",
"对照了 8 件经历,事件吻合率 80%。",
"这只是代表性候选,不是已确认的唯一出生分钟。",
"还可以再看一眼分盘。",
].join("");
const delivery = trimSpokenTurnForInterview(four, true);
assert.equal(delivery.split(/(?<=。)/).filter(Boolean).length, 3);
assert.match(delivery, /这次给出的范围 04:4904:53/);
assert.match(delivery, /对照了 8 件经历/);
assert.doesNotMatch(delivery, /还可以再看一眼分盘/);
const evidence = trimSpokenTurnForInterview(
"记下了:2016 年 9 月入学。这对校正很有帮助。接下来我们继续。",
false,
);
assert.equal(evidence, "记下了:2016 年 9 月入学。");
});