Files
Jyotisha/frontend/tests/rectification-choice-card.test.ts
T
Jesse_Chen f3327235ea
Independent Staging Quality Gate / validate (push) Successful in 10m50s
Independent Staging Quality Gate / publish (push) Successful in 13m29s
fix(rectification): remove static interview fallbacks
2026-08-27 16:40:47 +08:00

1004 lines
33 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 DYNAMIC_STYLE_OPTIONS = [
{ label: "明确发生且时间吻合", answer_class: "yes" },
{ label: "发生过但程度较弱", answer_class: "weak_yes" },
{ label: "明确没有发生", answer_class: "no" },
{ label: "这段记不清楚", answer_class: "unsure" },
] as const;
const SAMPLE_COPY = {
prompt: "2016 年前后,有没有明显搬家、离乡或长期异地?",
option_a: DYNAMIC_STYLE_OPTIONS[0].label,
option_b: DYNAMIC_STYLE_OPTIONS[1].label,
option_c: DYNAMIC_STYLE_OPTIONS[2].label,
option_d: DYNAMIC_STYLE_OPTIONS[3].label,
} as const;
const FOCUS_ID = "abababab-abab-4bab-8bab-abababababab";
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: "distinguish",
information_gain: 0.4,
candidate_ids: ["05:00", "05:20"],
expected_outcomes: [
{ answer_class: "yes", supports: ["05:00"], conflicts: ["05:20"] },
{ answer_class: "no", supports: ["05:20"], conflicts: ["05:00"] },
],
style_options: DYNAMIC_STYLE_OPTIONS,
};
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.ok(frame);
assert.equal(frame.period, "2016 年前后");
assert.match(frame.why, /年份锁定/);
assert.match(frame.why, /请写成/);
assert.doesNotMatch(frame.why, /^2016 年前后,有没有明显/);
assert.equal(frame.option_a_hint, DYNAMIC_STYLE_OPTIONS[0].label);
assert.equal(frame.option_b_hint, DYNAMIC_STYLE_OPTIONS[1].label);
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] },
);
assert.ok(frame);
if (!frame) assert.fail("expected dynamic choice frame");
const copy = serverOwnedChoiceCopy(frame!);
assert.ok(copy);
assert.match(copy.prompt, /搬家、离乡或长期异地/);
assert.doesNotMatch(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],
});
assert.ok(frame);
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,
probes: [{
...MOVE_PROBE,
domain: "family",
event_family: "家人结婚、添丁或住院等重要变动",
user_meaning: "盘外核对:只核对家人事件,不计入候选评分。",
}],
},
);
assert.ok(frame);
const card = mergeChoiceCard(frame, {
...SAMPLE_COPY,
prompt: "家人这条线还没用过,有没有一件记得大概年份的变化?",
});
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",
style_options: DYNAMIC_STYLE_OPTIONS,
}],
},
);
assert.ok(frame);
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("choice frame requires a real non-empty event family", () => {
const missingFamily = { ...MOVE_PROBE, event_family: undefined } as unknown as DiscriminatingEventProbe;
assert.equal(buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
}, { probes: [missingFamily] }), null);
assert.equal(buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
}, { probes: [{ ...MOVE_PROBE, event_family: " " }] }), null);
});
test("choice frame rejects incomplete, duplicate-class, illegal, and duplicate-label style options", () => {
const build = (style_options: DiscriminatingEventProbe["style_options"]) => buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
}, { probes: [{ ...MOVE_PROBE, style_options }] });
assert.equal(build(DYNAMIC_STYLE_OPTIONS.slice(0, 3)), null);
assert.equal(build([
...DYNAMIC_STYLE_OPTIONS,
{ label: "另一种明确发生", answer_class: "yes" },
]), null);
assert.equal(build([
{ ...DYNAMIC_STYLE_OPTIONS[0], label: "外貌更接近第一种" },
...DYNAMIC_STYLE_OPTIONS.slice(1),
]), null);
assert.equal(build([
DYNAMIC_STYLE_OPTIONS[0],
{ ...DYNAMIC_STYLE_OPTIONS[1], label: DYNAMIC_STYLE_OPTIONS[0].label },
DYNAMIC_STYLE_OPTIONS[2],
DYNAMIC_STYLE_OPTIONS[3],
]), null);
});
test("style options without a real event probe do not generate a card", () => {
assert.equal(buildChoiceFrame({
method_id: "d10_career",
ask_theme: "career_style",
domain: "career",
user_prompt_hint: "unused",
choice_kind: "varga_style",
style_options: DYNAMIC_STYLE_OPTIONS,
}), null);
});
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 without a persisted focus UUID is not tappable", () => {
const distinguish = projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2019-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
}],
eventProbes: [MOVE_PROBE],
candidateScores: [
{ time: "05:00", score: 34 },
{ time: "05:20", score: 33 },
],
activeFocus: {
intent: "distinguish_candidates",
targetDomain: "relocation",
targetKind: "home_change",
expectedAnswerSchema: { choice: SAMPLE_COPY },
},
});
assert.equal(distinguish, null);
});
test("GET hides a legacy known-event quality focus and resumes evidence collection", () => {
const card = projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "month",
occurredFrom: "2016-09-01",
occurredTo: null,
}],
eventClarificationProbes: [{
year: 2016,
year_label: "2016 年前后",
domain: "education",
event_family: "升学结果或学习环境出现明显变化",
source: "known_event_quality",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "已知事件质量",
role: "clarify",
style_options: DYNAMIC_STYLE_OPTIONS,
phase: "event_clarification",
choice_kind: "event_quality",
semantic_key: "education.2016",
candidate_split_hash: "education:2016",
candidate_ids: [],
expected_outcomes: [],
information_gain: 0,
}],
activeFocus: {
id: FOCUS_ID,
intent: "clarify_event",
targetDomain: "education",
targetKind: null,
expectedAnswerSchema: {
choice: SAMPLE_COPY,
probe_id: "probe:education.2016",
semantic_key: "education.2016",
candidate_split_hash: "education:2016",
scoring: true,
choice_kind: "event_quality",
},
},
});
assert.equal(card, null);
const plan = buildMethodFollowupPlan({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "month",
occurredFrom: "2016-09-01",
occurredTo: null,
}],
activeFocus: {
id: FOCUS_ID,
intent: "clarify_event",
targetDomain: "education",
targetKind: null,
expectedAnswerSchema: { choice: SAMPLE_COPY },
},
});
assert.equal(plan.next_followup?.method_id, "d9_relationship");
assert.equal(plan.next_followup?.choice_frame, null);
});
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: {
id: FOCUS_ID,
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: "distinguish",
style_options: DYNAMIC_STYLE_OPTIONS,
information_gain: 0.4,
candidate_ids: ["05:00", "05:20"],
expected_outcomes: [
{ answer_class: "yes", supports: ["05:00"], conflicts: ["05:20"] },
{ answer_class: "no", supports: ["05:20"], conflicts: ["05:00"] },
],
}],
activeFocus: {
id: FOCUS_ID,
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: {
id: FOCUS_ID,
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",
style_options: DYNAMIC_STYLE_OPTIONS,
}],
activeFocus: {
id: FOCUS_ID,
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. ${DYNAMIC_STYLE_OPTIONS[2].label}`), "C");
assert.equal(parseChoiceKeyFromUserMessage(`D、${DYNAMIC_STYLE_OPTIONS[3].label}`), "D");
assert.equal(parseChoiceKeyFromUserMessage(`${HOLDOUT_MESSAGE_PREFIX}B. ${DYNAMIC_STYLE_OPTIONS[1].label}`), "B");
assert.equal(parseChoiceKeyFromUserMessage(DYNAMIC_STYLE_OPTIONS[2].label), 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,
discriminating_event_probes: [{
year: 2017,
year_label: "2017 年前后",
domain: "education",
event_family: "学习方向或升学环境出现明显变化",
source: "dasha_activation",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "年份锁定 2017 年前后,核对学习方向或升学环境变化。",
role: "distinguish",
phase: "candidate_discriminator",
information_gain: 0.16,
semantic_key: "varga.d24.05:00/05:06|05:07",
candidate_split_hash: "varga.d24.05:00/05:06|05:07",
candidate_ids: ["05:00", "05:06", "05:07"],
expected_outcomes: [
{ answer_class: "yes", supports: ["05:00"], conflicts: ["05:06", "05:07"] },
{ answer_class: "no", supports: ["05:06", "05:07"], conflicts: ["05:00"] },
],
style_options: DYNAMIC_STYLE_OPTIONS,
}],
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: {
algorithm_version: "rectification-inference-v1",
candidate_set_id: "05:00-05:07:05:00,05:06,05:07",
revision: 1,
phase: "discrimination",
result_status: "discriminating",
range_start: "05:00",
range_end: "05:07",
candidates: [
{ id: "05:00", time: "05:00", cluster_range: ["05:00", "05:00"], prior_score: 34, posterior_score: 34, probability: 0.34, status: "active", rank: 1, strong_conflict_count: 0 },
{ id: "05:06", time: "05:06", cluster_range: ["05:06", "05:06"], prior_score: 33, posterior_score: 33, probability: 0.33, status: "active", rank: 2, strong_conflict_count: 0 },
{ id: "05:07", time: "05:07", cluster_range: ["05:07", "05:07"], prior_score: 33, posterior_score: 33, probability: 0.33, status: "active", rank: 3, strong_conflict_count: 0 },
],
events: [],
answered_probes: [{
probe_id: "probe:education.2016",
semantic_key: "education.2016",
candidate_split_hash: "education.2016",
answer_class: "yes",
classified_from: "evidence",
}],
probes: [{
id: "probe:education.2016",
semantic_key: "education.2016",
candidate_split_hash: "education.2016",
domain: "education",
year: 2016,
question: "2016 年是否入学?",
candidate_ids: ["05:00", "05:06", "05:07"],
information_gain: 0,
source: "known_event_quality",
expected_outcomes: [],
}],
rounds: [],
entropy: 1,
representative_time: "05:00",
credible_range: ["05:00", "05:07"],
},
},
},
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 without a persisted focus after 没有了", () => {
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);
});
test("dynamic option labels keep the A/B/C/D answer-class contract", () => {
const frame = buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
choice_kind: "existence",
style_options: [
{ label: "明确没有发生", answer_class: "no" },
{ label: "这段记不清楚", answer_class: "unsure" },
{ label: "明确发生且时间吻合", answer_class: "yes" },
{ label: "发生过但程度较弱", answer_class: "weak_yes" },
],
}, { probes: [MOVE_PROBE] });
assert.ok(frame);
const copy = serverOwnedChoiceCopy(frame);
assert.ok(copy);
assert.equal(copy.option_a, "明确发生且时间吻合");
assert.equal(copy.option_b, "发生过但程度较弱");
assert.equal(copy.option_c, "明确没有发生");
assert.equal(copy.option_d, "这段记不清楚");
});
test("GET card prompt keeps the agent's year-locked question", () => {
const fallback = {
prompt: "2018 年前后 · 认真关系进入、结束或关系观明显转变",
option_a: SAMPLE_COPY.option_a,
option_b: SAMPLE_COPY.option_b,
option_c: SAMPLE_COPY.option_c,
option_d: SAMPLE_COPY.option_d,
};
const card = projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2017-01-01",
occurredTo: null,
}, {
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2019-01-01",
occurredTo: null,
}],
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",
style_options: DYNAMIC_STYLE_OPTIONS,
}],
activeFocus: {
id: FOCUS_ID,
intent: "distinguish_candidates",
targetDomain: "relationship",
targetKind: "relationship_change",
expectedAnswerSchema: { choice: fallback, probe_id: "relationship.2018" },
},
latestAssistantText: "记下了。2018 年前后,有没有一段认真的关系开始或结束?",
});
assert.ok(card);
assert.equal(card.prompt, "2018 年前后,有没有一段认真的关系开始或结束?");
assert.doesNotMatch(card.prompt, /有没有明显认真关系进入/);
});
test("career event_quality uses the supplied dynamic labels", () => {
const frame = buildChoiceFrame({
method_id: "d10_career",
ask_theme: "career_style",
domain: "career",
user_prompt_hint: "unused",
choice_kind: "event_quality",
}, {
probes: [{
year: 2020,
year_label: "2020 年前后",
domain: "career",
event_family: "入职、升职或职责明显加重",
source: "known_event_quality",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "年份锁定 2020 年前后。已有相关经历。请写成一句自然语言,问入职、升职或职责明显加重有没有发生过。不得改年份。",
role: "distinguish",
style_options: DYNAMIC_STYLE_OPTIONS,
choice_kind: "event_quality",
}],
});
assert.ok(frame);
const copy = serverOwnedChoiceCopy(frame);
assert.ok(copy);
assert.match(copy.prompt, /2020 年前后/);
assert.match(copy.prompt, /入职、升职或职责明显加重/);
assert.doesNotMatch(copy.prompt, /有没有/);
assert.match(copy.prompt, /·/);
assert.equal(copy.option_a, DYNAMIC_STYLE_OPTIONS[0].label);
assert.doesNotMatch(copy.option_a, /失常/);
});
test("dasha existence cards lock the event and use supplied dynamic labels", () => {
const frame = buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
choice_kind: "existence",
}, { probes: [MOVE_PROBE] });
assert.ok(frame);
const copy = serverOwnedChoiceCopy(frame);
assert.ok(copy);
assert.match(copy.prompt, /2016 年前后/);
assert.match(copy.prompt, /搬家、离乡或长期异地/);
assert.doesNotMatch(copy.prompt, /有没有明显/);
assert.equal(copy.option_a, DYNAMIC_STYLE_OPTIONS[0].label);
assert.equal(copy.option_c, DYNAMIC_STYLE_OPTIONS[2].label);
});