Files
Jyotisha/frontend/tests/rectification-choice-card.test.ts
T
Jesse_Chen a658ef8d2b fix(web): stop rectification interview when remaining minutes no longer split
Coverage-complete ties stayed in discrimination because whole-window D9/D24 follow-ups were treated as probes, and restated dates inserted duplicate evidence. Skip encoded remaining layers, ask leftover D4 or offer a provisional range, and dedupe dated rows by kind and date.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 19:02:35 +08:00

688 lines
22 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 test from "node:test";
import {
buildChoiceFrame,
choiceCardUserMessage,
HOLDOUT_MESSAGE_PREFIX,
isHoldoutVerificationQuote,
lifePeriodLabel,
mergeChoiceCard,
parseAgentChoiceCopy,
parseChoiceKeyFromUserMessage,
parseRectificationChoiceCard,
serverOwnedChoiceCopy,
} from "../src/lib/rectification-agentic/v9/choice-card.ts";
import { buildMethodFollowupPlan, projectRectificationChoiceCard } from "../src/lib/rectification-agentic/v9/method-followup.ts";
import { choiceCardFromCaseDossier } from "../src/lib/rectification-agentic/v9/interview-state.ts";
import type { DiscriminatingEventProbe } from "../src/lib/rectification-agentic/v9/refinement-packet.ts";
const SAMPLE_COPY = {
prompt: "2016 年前后,有没有明显搬家、离乡或长期异地?",
option_a: "是,大概就在那段时间",
option_b: "有类似,但年份不对或不够重大",
option_c: "没有明显发生",
option_d: "不记得 / 不确定",
} as const;
const MOVE_PROBE: DiscriminatingEventProbe = {
year: 2016,
year_label: "2016 年前后",
domain: "relocation",
event_family: "搬家、离乡或长期异地",
source: "dasha_activation",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "年份锁定 2016 年前后。事件家族:搬家、离乡或长期异地。请写成一句自然语言是/否题。不得改年份。",
role: "reverse_verify",
};
test("choice frames ask one biographical event from a server probe, not competing charts", () => {
const frame = buildChoiceFrame(
{
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
},
{
observations: [
{ layer: "d9", candidates_differ: true, ask_theme: "relationship_style" },
{ layer: "d10", candidates_differ: true, ask_theme: "career_style" },
{ layer: "d4", candidates_differ: true, ask_theme: "home_change" },
],
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
probes: [MOVE_PROBE],
},
);
assert.equal(frame.period, "2016 年前后");
assert.match(frame.why, /年份锁定/);
assert.match(frame.why, /请写成/);
assert.doesNotMatch(frame.why, /^2016 年前后,有没有明显/);
assert.match(frame.option_a_hint, /是,大概就在那段时间/);
assert.match(frame.option_b_hint, /年份不对或不够重大/);
assert.doesNotMatch(frame.option_a_hint + frame.option_b_hint, /更像哪一件|可能性/);
assert.doesNotMatch(frame.why, /更像哪一件/);
assert.equal(/外貌|疤痕|胎记/.test(frame.option_a_hint + frame.option_b_hint), false);
assert.equal(frame.scoring, true);
assert.equal(mergeChoiceCard(frame, null), null);
});
test("server-owned card names the event family, not a generic 有没有这件事", () => {
const frame = buildChoiceFrame(
{
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
},
{ probes: [MOVE_PROBE] },
);
const copy = serverOwnedChoiceCopy(frame);
assert.ok(copy);
assert.match(copy.prompt, /搬家、离乡或长期异地/);
assert.doesNotMatch(copy.prompt, /有没有这件事/);
});
test("the visible card uses Agent copy for A/B/C/D, not the server hint", () => {
const frame = buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
}, {
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
probes: [MOVE_PROBE],
});
const card = mergeChoiceCard(frame, SAMPLE_COPY);
assert.equal(card?.prompt, SAMPLE_COPY.prompt);
assert.equal(card?.options[0]?.label, SAMPLE_COPY.option_a);
assert.equal(card?.options[1]?.label, SAMPLE_COPY.option_b);
assert.equal(card?.options[2]?.label, SAMPLE_COPY.option_c);
assert.equal(card?.options[3]?.label, SAMPLE_COPY.option_d);
assert.equal(card?.stop_label, frame.stop_label);
});
test("holdout messages are prefixed and do not count as scoring quotes", () => {
const frame = buildChoiceFrame(
{
method_id: "oos_blind",
ask_theme: "oos_blind",
domain: "family",
user_prompt_hint: "unused",
},
{ scoring: false },
);
const card = mergeChoiceCard(frame, {
prompt: "家人这条线还没用过,有没有一件记得大概年份的变化?",
option_a: "是,大概就在那段时间",
option_b: "有类似,但年份不对或不够重大",
option_c: "没有明显发生",
option_d: "不记得 / 不确定",
});
assert.ok(card);
const message = choiceCardUserMessage(card, "A");
assert.match(message, new RegExp(HOLDOUT_MESSAGE_PREFIX));
assert.equal(isHoldoutVerificationQuote(message), true);
assert.equal(isHoldoutVerificationQuote("A. 认真关系进入或结婚"), false);
assert.equal(card.scoring, false);
assert.match(frame.why, /盘外核对/);
});
test("reverse-verify after adopt scores the predicted event and invites 改选 on mismatch", () => {
const frame = buildChoiceFrame(
{
method_id: "reverse_verify",
ask_theme: "career_style",
domain: "career",
user_prompt_hint: "unused",
},
{
probes: [{
year: 2018,
year_label: "2018 年前后",
domain: "career",
event_family: "入职、升职或职责明显加重",
source: "dasha_activation",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "年份锁定 2018 年前后。",
role: "reverse_verify",
}],
},
);
assert.equal(frame.period, "2018 年前后");
assert.equal(frame.scoring, true);
assert.match(frame.why, /按当前采用时间核对/);
assert.match(frame.why, /改选/);
assert.doesNotMatch(frame.why, /盘外核对/);
const card = mergeChoiceCard(frame, SAMPLE_COPY);
assert.ok(card);
assert.equal(card.scoring, true);
assert.doesNotMatch(choiceCardUserMessage(card, "A"), new RegExp(HOLDOUT_MESSAGE_PREFIX));
});
test("life period only reuses years from the same domain", () => {
assert.equal(lifePeriodLabel([
{ status: "confirmed", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
]), "那段时间");
assert.equal(lifePeriodLabel([
{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null },
{ status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2020-06-01", occurredTo: null },
], "education"), "2016 年前后");
assert.equal(lifePeriodLabel([
{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null },
], "relocation"), "那段时间");
});
test("known-event quality probe asks 发挥失常 instead of existence", () => {
const frame = buildChoiceFrame({
method_id: "d5_education",
ask_theme: "education_style",
domain: "education",
user_prompt_hint: "unused",
}, {
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2015-06-01",
occurredTo: null,
}],
probes: [{
year: 2015,
year_label: "2015 年前后",
domain: "education",
event_family: "高考或重要考试发挥明显失常、压力很大",
source: "known_event_quality",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "年份锁定 2015 年前后。已有高考或考试经历。请写成一句自然语言,问那次是否发挥失常或压力特别大。不得改年份。",
role: "distinguish",
}],
});
assert.equal(frame.period, "2015 年前后");
assert.match(frame.why, /发挥失常/);
assert.match(frame.why, /请写成/);
assert.doesNotMatch(`${frame.why}${frame.option_a_hint}${frame.option_b_hint}`, /更像哪一件/);
});
test("age-band fallback uses birth date, not another event's year", () => {
const frame = buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
}, {
birthDate: "1997-08-08",
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
});
assert.equal(frame.period, "2018 年前后");
assert.match(frame.option_a_hint, /是,大概就在那段时间/);
assert.doesNotMatch(frame.period, /2016/);
});
test("agent choice copy rejects appearance, scars, clock times, and incomplete C/D", () => {
assert.equal(parseAgentChoiceCopy({
choice: { prompt: "你长得更像哪一种?", option_a: "外貌偏圆", option_b: "有疤痕", option_c: "都没有", option_d: "不记得" },
}), null);
assert.equal(parseAgentChoiceCopy({
...SAMPLE_COPY,
prompt: "08:12 换升时发生了什么?",
}), null);
assert.equal(parseAgentChoiceCopy({
prompt: SAMPLE_COPY.prompt,
option_a: SAMPLE_COPY.option_a,
option_b: SAMPLE_COPY.option_b,
}), null);
assert.equal(parseAgentChoiceCopy({
...SAMPLE_COPY,
option_c: SAMPLE_COPY.option_a,
}), null);
assert.ok(parseAgentChoiceCopy(SAMPLE_COPY));
});
test("GET card stays hidden on an empty ledger even if the Agent already wrote choice copy", () => {
const withoutCopy = projectRectificationChoiceCard({ evidence: [] });
assert.equal(withoutCopy, null);
const leftoverOpening = projectRectificationChoiceCard({
evidence: [],
activeFocus: {
intent: "collect_method_evidence",
targetDomain: null,
targetKind: null,
expectedAnswerSchema: { choice: SAMPLE_COPY },
},
});
assert.equal(leftoverOpening, null);
const distinguish = projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2019-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "family",
datePrecision: "year",
occurredFrom: "2020-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "occupation",
datePrecision: "unknown",
occurredFrom: null,
occurredTo: null,
}],
observations: [{ layer: "d9", candidates_differ: true, ask_theme: "relationship_style" }],
eventProbes: [{
year: 2018,
year_label: "2018 年前后",
domain: "relationship",
event_family: "认真关系进入、结束或关系观明显转变",
source: "dasha_activation",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "年份锁定 2018 年前后。事件家族:认真关系进入、结束或关系观明显转变。请写成一句自然语言是/否题。不得改年份。",
role: "reverse_verify",
}],
activeFocus: {
intent: "distinguish_candidates",
targetDomain: "relationship",
targetKind: "relationship_change",
expectedAnswerSchema: { choice: SAMPLE_COPY },
},
});
assert.equal(distinguish?.prompt, SAMPLE_COPY.prompt);
assert.equal(distinguish?.options[2]?.label, SAMPLE_COPY.option_c);
assert.ok(parseRectificationChoiceCard(distinguish));
});
test("opening follow-up asks for a dated event in natural language, not a choice card", () => {
const plan = buildMethodFollowupPlan({ evidence: [] });
assert.equal(plan.next_followup?.method_id, "dasha_events");
assert.equal(plan.next_followup?.choice_frame, null);
assert.doesNotMatch(plan.next_followup?.user_prompt_hint ?? "", /A\/B\/C\/D/);
assert.match(plan.next_followup?.user_prompt_hint ?? "", /自然语言/);
assert.match(plan.next_followup?.user_prompt_hint ?? "", /不要调用 set-focus/);
});
test("distinguish follow-up copy forbids competing-chart ranking", () => {
const plan = buildMethodFollowupPlan({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2019-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "family",
datePrecision: "year",
occurredFrom: "2020-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "occupation",
datePrecision: "unknown",
occurredFrom: null,
occurredTo: null,
}, {
status: "confirmed",
domain: "horary",
datePrecision: "day",
occurredFrom: "2024-01-01",
occurredTo: null,
}],
precisionStage: "d4_refine",
eventProbes: [MOVE_PROBE],
birthDate: "1997-08-08",
});
assert.equal(plan.next_followup?.choice_frame?.period, "2016 年前后");
assert.doesNotMatch(plan.next_followup?.choice_frame?.option_a_hint ?? "", /更像哪一件|两套盘|可能性/);
assert.doesNotMatch(plan.next_followup?.choice_frame?.option_b_hint ?? "", /更像哪一件|两套盘|可能性/);
assert.match(plan.next_followup?.user_prompt_hint ?? "", /已持久化的题干/);
assert.match(plan.next_followup?.user_prompt_hint ?? "", /不得发明年份/);
});
test("GET A/B card stays visible after coverage when candidates remain tied", () => {
const card = projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2019-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "family",
datePrecision: "year",
occurredFrom: "2020-01-01",
occurredTo: null,
}, {
status: "draft",
domain: "occupation",
datePrecision: "unknown",
occurredFrom: null,
occurredTo: null,
eventKind: "occupation_note",
}],
eventProbes: [MOVE_PROBE],
selectionAllowed: true,
proposeAllowed: true,
candidateScores: [
{ time: "05:00", score: 34 },
{ time: "05:01", score: 33 },
{ time: "05:02", score: 33 },
],
activeFocus: {
intent: "distinguish_candidates",
targetDomain: "relocation",
targetKind: "home_change",
expectedAnswerSchema: { choice: SAMPLE_COPY },
},
});
assert.ok(card);
assert.equal(card.prompt, SAMPLE_COPY.prompt);
});
test("GET A/B card stays hidden once candidates are separated and ready to offer", () => {
const card = projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2019-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "family",
datePrecision: "year",
occurredFrom: "2020-01-01",
occurredTo: null,
}, {
status: "draft",
domain: "occupation",
datePrecision: "unknown",
occurredFrom: null,
occurredTo: null,
eventKind: "occupation_note",
}],
eventProbes: [MOVE_PROBE],
selectionAllowed: true,
proposeAllowed: true,
holdoutValidation: "passed",
candidateScores: [
{ time: "05:00", score: 62 },
{ time: "05:01", score: 22 },
{ time: "05:02", score: 16 },
],
});
assert.equal(card, null);
});
test("GET reverse-verify card still appears after a time is accepted", () => {
const card = projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "career",
datePrecision: "day",
occurredFrom: "2024-04-07",
occurredTo: null,
}],
accepted: true,
sessionOutcome: "adopt_representative",
selectionAllowed: true,
proposeAllowed: true,
eventProbes: [{
year: 2018,
year_label: "2018 年前后",
domain: "career",
event_family: "入职、升职或职责明显加重",
source: "dasha_activation",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "年份锁定 2018 年前后。请写成一句自然语言,问是否入职或职责加重。",
role: "reverse_verify",
}],
activeFocus: {
intent: "reverse_verify",
targetDomain: "career",
targetKind: "career_change",
expectedAnswerSchema: { choice: SAMPLE_COPY },
},
});
assert.equal(card?.prompt, SAMPLE_COPY.prompt);
assert.ok(parseRectificationChoiceCard(card));
});
test("choice card user messages expose A/B/C/D as a leading key", () => {
assert.equal(parseChoiceKeyFromUserMessage("C. 没有明显发生"), "C");
assert.equal(parseChoiceKeyFromUserMessage("D、不记得 / 不确定"), "D");
assert.equal(parseChoiceKeyFromUserMessage(`${HOLDOUT_MESSAGE_PREFIX}B. 有类似但年份不对`), "B");
assert.equal(parseChoiceKeyFromUserMessage("没有明显发生"), null);
});
test("GET choice_card stays after coverage when remaining minutes still split on D24", () => {
const card = choiceCardFromCaseDossier({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
summary: "2016 年大学入学",
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2019-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "family",
datePrecision: "year",
occurredFrom: "2020-01-01",
occurredTo: null,
}, {
status: "draft",
domain: "occupation",
datePrecision: "unknown",
occurredFrom: null,
occurredTo: null,
eventKind: "occupation_note",
summary: "医疗器械算法,第三个(技术执行)",
}],
conversationSummary: {
activeFocus: {
id: "abababab-abab-4bab-8bab-abababababab",
intent: "distinguish_candidates",
targetDomain: "education",
targetKind: "education_milestone",
expectedAnswerSchema: {
choice: SAMPLE_COPY,
probe_id: "contrast:varga.d24.05:00/05:06|05:07",
semantic_key: "varga.d24.05:00/05:06|05:07",
},
},
declinedSkippedTopics: [],
},
latestResult: {
resultId: "55555555-5555-4555-8555-555555555555",
selectionAllowed: true,
candidates: [
{ time: "05:00", relativeSupport: 34 },
{ time: "05:06", relativeSupport: 33 },
{ time: "05:07", relativeSupport: 33 },
],
decisionReceipt: {
propose_allowed: true,
window_scan: {
scanned: true,
d9_lagna_count: 1,
d10_lagna_count: 3,
d10_candidates_differ: true,
d10_sign_names: ["巨蟹座", "狮子座", "处女座"],
d24_candidates_differ: true,
transitions: [
{ layer: "d10", at: "05:00" },
{ layer: "d10", at: "05:15" },
{ layer: "d24", at: "05:00" },
{ layer: "d24", at: "05:06" },
],
},
inference_state: {
answered_probes: [{
probe_id: "probe:education.2016",
semantic_key: "education.2016",
answer_class: "yes",
}],
probes: [{
id: "probe:education.2016",
semantic_key: "education.2016",
information_gain: 0,
source: "known_event_quality",
expected_outcomes: [],
}],
},
},
},
case: { acceptedTime: null },
turns: [],
});
assert.ok(card);
assert.equal(card.probe_id, "contrast:varga.d24.05:00/05:06|05:07");
assert.equal(card.prompt, SAMPLE_COPY.prompt);
});
test("GET choice_card stays hidden after 没有了 when selection is allowed", () => {
const card = choiceCardFromCaseDossier({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2019-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "family",
datePrecision: "year",
occurredFrom: "2020-01-01",
occurredTo: null,
}, {
status: "draft",
domain: "occupation",
datePrecision: "unknown",
occurredFrom: null,
occurredTo: null,
eventKind: "occupation_note",
}],
conversationSummary: {
activeFocus: null,
declinedSkippedTopics: [],
},
latestResult: {
selectionAllowed: true,
candidates: [
{ time: "05:00", relativeSupport: 34 },
{ time: "05:06", relativeSupport: 33 },
{ time: "05:07", relativeSupport: 33 },
],
decisionReceipt: { propose_allowed: true },
},
case: { acceptedTime: null },
turns: [{ role: "user", text: "没有了" }],
});
assert.equal(card, null);
});