Files
Jyotisha/frontend/tests/rectification-choice-card.test.ts
T
Jesse_Chen 591ffd21a1
Independent Staging Quality Gate / validate (push) Successful in 11m52s
Independent Staging Quality Gate / publish (push) Successful in 1m46s
fix(rectification): keep an open out-of-sample A–D card tappable
GET was dropping choice_card when the recast had no choice_frame, so the
embedded options rendered disabled. Replay the persisted reverse-verify /
out-of-sample schema instead of silencing the live question.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:30:49 +08:00

1666 lines
59 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,
preferConcreteChoicePrompt,
mergeChoiceCard,
parseAgentChoiceCopy,
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 { QUALITY_STYLE_OPTIONS } from "../src/lib/rectification-agentic/v9/probe-question-contract.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,
options: DYNAMIC_STYLE_OPTIONS.map((option, index) => ({
key: (["A", "B", "C", "D"] as const)[index]!,
label: option.label,
answer_class: option.answer_class,
})),
} 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,
};
const IDENTITY_PROBE: DiscriminatingEventProbe = {
...MOVE_PROBE,
semantic_key: "relocation.2016",
candidate_split_hash: "relocation.2016:05:00|05:20",
};
function identityCard(schemaOverrides: Record<string, unknown> = {}) {
return projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
eventProbes: [IDENTITY_PROBE],
activeFocus: {
id: FOCUS_ID,
questionId: "persisted-question-id",
intent: "distinguish_candidates",
targetDomain: "relocation",
targetKind: "relocation_change",
expectedAnswerSchema: {
choice: SAMPLE_COPY,
semantic_key: IDENTITY_PROBE.semantic_key,
candidate_split_hash: IDENTITY_PROBE.candidate_split_hash,
...schemaOverrides,
},
},
});
}
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("choice frames keep the engine month lock instead of collapsing to a year", () => {
const frame = buildChoiceFrame(
{
method_id: "d10_career",
ask_theme: "dated_event",
domain: "career",
user_prompt_hint: "unused",
},
{
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
probes: [{
...MOVE_PROBE,
year: 2018,
month: 3,
year_label: "2018 年 3 月前后",
domain: "career",
event_family: "入职、升职或职责明显加重",
source: "dasha_boundary",
user_meaning: "时间范围锁定 2018 年 3 月前后;领域锁定 career。",
semantic_key: "career.2018.03.dasha_boundary",
}],
},
);
assert.ok(frame);
assert.equal(frame.period, "2018 年 3 月前后");
assert.match(frame.prompt, /2018 年 3 月前后/);
assert.match(frame.why, /2018 年 3 月前后/);
assert.doesNotMatch(frame.period, /^2018 年前后$/);
});
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"), "那段时间");
assert.equal(lifePeriodLabel([
{ status: "confirmed", domain: "career", datePrecision: "month", occurredFrom: "2020-04-01", occurredTo: null },
], "career"), "2020 年 4 月前后");
assert.equal(lifePeriodLabel([
{ status: "confirmed", domain: "relationship", datePrecision: "day", occurredFrom: "2024-08-08", occurredTo: null },
], "relationship"), "2024 年 8 月前后");
});
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("yearless D24 quality cards do not borrow a recorded education year", () => {
const frame = buildChoiceFrame({
method_id: "d5_education",
ask_theme: "education_style",
domain: "education",
user_prompt_hint: "unused",
choice_kind: "event_quality",
semantic_key: "varga.d24.05:00/05:07",
style_options: QUALITY_STYLE_OPTIONS,
}, {
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
probes: [{
...MOVE_PROBE,
year: 0,
year_label: "当前这几个候选",
domain: "education",
event_family: "学业或考试发挥失常、压力特别大",
choice_kind: "event_quality",
semantic_key: "varga.d24.05:00/05:07",
style_options: QUALITY_STYLE_OPTIONS,
}],
});
const period = frame?.period ?? "";
const prompt = frame?.prompt ?? "";
assert.equal(frame, null);
assert.doesNotMatch(period, /2016/);
assert.doesNotMatch(prompt, /2016/);
});
test("yearless family cards no longer mint a scoring frame", () => {
const frame = buildChoiceFrame({
method_id: "relatives",
ask_theme: "family_event",
domain: "family",
user_prompt_hint: "unused",
choice_kind: "existence",
semantic_key: "varga.d12.05:00/05:07",
}, {
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
probes: [{
...MOVE_PROBE,
year: 0,
year_label: "当前这几个候选",
domain: "family",
event_family: "家人结婚、添丁或住院",
choice_kind: "existence",
semantic_key: "varga.d12.05:00/05:07",
}],
});
const period = frame?.period ?? "";
const prompt = frame?.prompt ?? "";
assert.equal(frame, null);
assert.doesNotMatch(period, /2016/);
assert.doesNotMatch(prompt, /2016/);
});
test("yearless family cards do not borrow a recorded family year", () => {
const frame = buildChoiceFrame({
method_id: "relatives",
ask_theme: "family_event",
domain: "family",
user_prompt_hint: "unused",
choice_kind: "existence",
semantic_key: "varga.d12.05:00/05:07",
}, {
evidence: [{
status: "confirmed",
domain: "family",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
}],
probes: [{
...MOVE_PROBE,
year: 0,
year_label: "当前这几个候选",
domain: "family",
event_family: "家人结婚、添丁或住院",
choice_kind: "existence",
semantic_key: "varga.d12.05:00/05:07",
}],
});
const period = frame?.period ?? "";
const prompt = frame?.prompt ?? "";
assert.equal(frame, null);
assert.doesNotMatch(period, /2018/);
assert.doesNotMatch(prompt, /2018/);
});
test("GET upgrades a persisted placeholder stem to the recorded year lock", () => {
assert.equal(
preferConcreteChoicePrompt(
"2016 年前后,有没有学业或考试发挥失常、压力特别大的时候?",
"当前这几个候选 · 学业、考试发挥或学习压力出现明显变化",
),
"2016 年前后,有没有学业或考试发挥失常、压力特别大的时候?",
);
assert.equal(
preferConcreteChoicePrompt(
"2016 年前后,有没有学业或考试发挥失常、压力特别大的时候?",
"2016 年前后 · 学业、考试发挥或学习压力出现明显变化",
),
"2016 年前后,有没有学业或考试发挥失常、压力特别大的时候?",
);
assert.equal(
preferConcreteChoicePrompt(
"2018 年前后,有没有开始一段认真关系、分手或结婚?",
"2018 年前后,有没有认真关系进入、结束或关系观明显转变?",
),
"2018 年前后,有没有开始一段认真关系、分手或结婚?",
);
assert.equal(
preferConcreteChoicePrompt(
"2018 年前后,有没有家人结婚、添丁或住院?",
"那段时间,有没有家人相关的明显变化?",
),
"2018 年前后,有没有家人结婚、添丁或住院?",
);
});
test("choice frame completes existence options and rejects illegal or non-renderable varga styles", () => {
const build = (style_options: DiscriminatingEventProbe["style_options"], extra: Partial<DiscriminatingEventProbe> = {}) => buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
}, { probes: [{ ...MOVE_PROBE, style_options, ...extra }] });
assert.ok(build(DYNAMIC_STYLE_OPTIONS.slice(0, 3)));
assert.ok(build(undefined));
assert.equal(build([
{ label: "巨蟹相处主动热情", answer_class: "yes" },
], { choice_kind: "varga_style" }), null);
});
test("python-shaped existence probe without style_options still builds a four-option card", () => {
const { style_options: _unused, ...pythonProbe } = MOVE_PROBE;
const frame = buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
}, { probes: [pythonProbe] });
assert.ok(frame);
assert.equal(frame.option_a_answer_class, "yes");
assert.equal(frame.option_b_answer_class, "weak_yes");
assert.equal(frame.option_c_answer_class, "no");
assert.equal(frame.option_d_answer_class, "unsure");
assert.equal(frame.unsure_label, "这段记不清楚");
});
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 ?? "", /spokenPrompt/);
assert.doesNotMatch(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 ?? "", /spokenPrompt 写出题干/);
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("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",
candidate_split_hash: "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 shows an engine-dated career dasha instead of locking D24 to the recorded education year", () => {
const careerKey = "career.2012.11.dasha_boundary";
const careerCopy = {
prompt: "2012 年 11 月前后,有没有入职、升职或职责明显加重?",
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,
options: DYNAMIC_STYLE_OPTIONS.map((option, index) => ({
key: (["A", "B", "C", "D"] as const)[index]!,
label: option.label,
answer_class: option.answer_class,
})),
};
const inferenceCandidates = [
{ id: "04:47", time: "04:47", cluster_range: ["04:47", "04:47"] as const, prior_score: 5, posterior_score: 5, probability: 0.05, status: "active", rank: 6, strong_conflict_count: 0 },
{ id: "05:00", time: "05:00", cluster_range: ["05:00", "05:00"] as const, prior_score: 21, posterior_score: 21, probability: 0.21, status: "active", rank: 1, strong_conflict_count: 0 },
{ id: "05:07", time: "05:07", cluster_range: ["05:07", "05:07"] as const, prior_score: 18, posterior_score: 18, probability: 0.18, status: "active", rank: 2, strong_conflict_count: 0 },
{ id: "05:12", time: "05:12", cluster_range: ["05:12", "05:12"] as const, prior_score: 18, posterior_score: 18, probability: 0.18, status: "active", rank: 3, strong_conflict_count: 0 },
{ id: "05:14", time: "05:14", cluster_range: ["05:14", "05:14"] as const, prior_score: 17, posterior_score: 17, probability: 0.17, status: "active", rank: 4, strong_conflict_count: 0 },
{ id: "05:15", time: "05:15", cluster_range: ["05:15", "05:15"] as const, prior_score: 3, posterior_score: 3, probability: 0.03, status: "active", rank: 9, strong_conflict_count: 0 },
];
const candidateSetId = "04:45-05:15:04:47,05:00,05:07,05:12,05:14,05:15";
const d24Key = "varga.d24.04:47/04:51|04:53/04:59/05:00/05:07|05:12/05:14|05:15";
const d24Split = `${candidateSetId}:${d24Key}`;
const d24Outcomes = [
{ answer_class: "yes", supports: ["04:47"], conflicts: ["05:00", "05:07", "05:12", "05:14", "05:15"] },
{ answer_class: "weak_yes", supports: ["05:00", "05:07"], conflicts: ["04:47", "05:12", "05:14", "05:15"] },
{ answer_class: "no", supports: ["05:12", "05:14"], conflicts: ["04:47", "05:00", "05:07", "05:15"] },
{ answer_class: "unsure", supports: [], conflicts: [] },
];
const card = choiceCardFromCaseDossier({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: "2016-01-01",
eventKind: "education_start",
}, {
status: "confirmed",
domain: "career",
datePrecision: "month",
occurredFrom: "2020-04-01",
occurredTo: "2020-04-01",
eventKind: "career_entry",
}, {
status: "confirmed",
domain: "career",
datePrecision: "month",
occurredFrom: "2020-10-01",
occurredTo: "2020-10-01",
eventKind: "career_exit",
}, {
status: "confirmed",
domain: "career",
datePrecision: "day",
occurredFrom: "2024-04-07",
occurredTo: null,
eventKind: "career_entry",
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "month",
occurredFrom: "2024-05-01",
occurredTo: null,
eventKind: "relationship_start",
}, {
status: "confirmed",
domain: "relationship",
datePrecision: "day",
occurredFrom: "2024-08-08",
occurredTo: null,
eventKind: "relationship_end",
}],
conversationSummary: {
activeFocus: {
id: FOCUS_ID,
questionId: `probe:${careerKey}`,
intent: "distinguish_candidates",
targetDomain: "career",
targetKind: null,
expectedAnswerSchema: {
choice: careerCopy,
probe_id: `event:${careerKey}`,
semantic_key: careerKey,
candidate_split_hash: "career.2012.11",
choice_kind: "existence",
},
},
declinedSkippedTopics: [],
},
latestResult: {
resultId: "result-d24-open",
selectionAllowed: true,
confirmationAllowed: false,
candidates: inferenceCandidates.map((item) => ({
time: item.time,
rank: item.rank,
relativeSupport: item.posterior_score,
})),
decisionReceipt: {
discriminating_event_probes: [{
year: 2012,
year_label: "2012 年 11 月前后",
domain: "career",
event_family: "入职、升职或职责明显加重",
source: "dasha_boundary",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "时间范围锁定 2012 年 11 月前后。",
role: "distinguish",
phase: "candidate_discriminator",
information_gain: 1.3356,
semantic_key: careerKey,
candidate_split_hash: "career.2012.11",
candidate_ids: ["04:47", "05:00", "05:15"],
expected_outcomes: [
{ answer_class: "yes", supports: ["04:47"], conflicts: ["05:00", "05:15"] },
{ answer_class: "weak_yes", supports: ["04:47"], conflicts: ["05:00", "05:15"] },
{ answer_class: "no", supports: ["05:00", "05:15"], conflicts: ["04:47"] },
{ answer_class: "unsure", supports: [], conflicts: [] },
],
style_options: DYNAMIC_STYLE_OPTIONS,
}],
inference_state: {
algorithm_version: "rectification-inference-v1",
candidate_set_id: candidateSetId,
revision: 1,
phase: "discrimination",
result_status: "discriminating",
range_start: "04:45",
range_end: "05:15",
candidates: inferenceCandidates,
events: [],
probes: [{
id: `contrast:${d24Key}`,
year: 0,
domain: "education",
source: "varga_contrast",
question: "引擎给出的区分机会绑定 D24。",
semantic_key: d24Key,
candidate_ids: inferenceCandidates.map((item) => item.time),
information_gain: 2.503258334775646,
expected_outcomes: d24Outcomes,
candidate_split_hash: d24Split,
}],
answered_probes: [],
rounds: [],
entropy: 2.0,
representative_time: "05:00",
credible_range: ["05:00", "05:14"],
},
},
},
case: { acceptedTime: null, status: "collecting_evidence" },
turns: [],
});
assert.ok(card);
assert.equal(card.focus_id, FOCUS_ID);
assert.match(card.prompt, /2012 年 11 月前后,有没有入职、升职或职责明显加重?/);
assert.doesNotMatch(card.prompt, /2016 年前后/);
assert.doesNotMatch(card.prompt, /当前这几个候选/);
assert.doesNotMatch(card.prompt, / · /);
assert.equal(card.options.length, 4);
assert.equal(card.options.every((option) => option.role === "primary"), true);
});
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 canonicalize to yes/weak_yes/no/unsure order", () => {
const styleOptions = [
{ label: "明确没有发生", answer_class: "no" },
{ label: "这段记不清楚", answer_class: "unsure" },
{ label: "明确发生且时间吻合", answer_class: "yes" },
{ label: "发生过但程度较弱", answer_class: "weak_yes" },
] as const;
const frame = buildChoiceFrame({
method_id: "d4_home",
ask_theme: "home_change",
domain: "relocation",
user_prompt_hint: "unused",
choice_kind: "existence",
style_options: styleOptions,
}, { probes: [{ ...MOVE_PROBE, style_options: styleOptions }] });
assert.ok(frame);
const copy = serverOwnedChoiceCopy(frame);
assert.deepEqual(copy?.options, [
{ key: "A", label: "明确发生且时间吻合", answer_class: "yes" },
{ key: "B", label: "发生过但程度较弱", answer_class: "weak_yes" },
{ key: "C", label: "明确没有发生", answer_class: "no" },
{ key: "D", label: "这段记不清楚", answer_class: "unsure" },
]);
});
test("choice card requires the persisted focus to match the discriminator identity", () => {
assert.equal(identityCard({ semantic_key: "relationship.2016" }), null);
assert.equal(identityCard({ candidate_split_hash: "different-split" }), null);
const card = identityCard();
assert.ok(card);
assert.equal(card.focus_id, FOCUS_ID);
assert.equal(card.question_id, "persisted-question-id");
});
test("GET card prompt keeps the agent's year-locked question", () => {
const fallback = {
...SAMPLE_COPY,
prompt: "2018 年前后 · 认真关系进入、结束或关系观明显转变",
};
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.match(copy.prompt, /有没有/);
assert.doesNotMatch(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);
});
const D9_STALE_KEY = "varga.d9.05:00|05:06/05:07";
const CURRENT_SET_ID = "05:00-05:07:05:00,05:06,05:07";
// The probe was minted while the candidate set still held 05:05 instead of 05:06.
const STALE_SET_ID = "05:00-05:07:05:00,05:05,05:07";
const D9_STYLE_OPTIONS = [
{ sign: "天秤座", label: "和谐、美感、合作", answer_class: "yes" },
{ sign: "天蝎座", label: "深刻、占有欲、转化", answer_class: "weak_yes" },
{ sign: "射手座", label: "自由、哲学、冒险", answer_class: "no" },
{ label: "一时说不好", answer_class: "unsure" },
] as const;
function staleSplitDossier(schemaHash: string) {
return {
evidence: [
{ id: "e-career-a", status: "confirmed", domain: "career", datePrecision: "month", occurredFrom: "2020-04-01", occurredTo: "2020-04-01", eventKind: "career_entry" },
{ id: "e-career-b", status: "confirmed", domain: "career", datePrecision: "month", occurredFrom: "2020-10-01", occurredTo: "2020-10-01", eventKind: "career_exit" },
{ id: "e-rel-end", status: "confirmed", domain: "relationship", datePrecision: "day", occurredFrom: "2024-08-08", occurredTo: "2024-08-08", eventKind: "relationship_end" },
{ id: "e-rel-start", status: "confirmed", domain: "relationship", datePrecision: "month", occurredFrom: "2024-05-01", occurredTo: "2024-05-31", eventKind: "relationship_start" },
],
conversationSummary: {
activeFocus: {
id: FOCUS_ID,
questionId: `probe:${D9_STALE_KEY}`,
intent: "distinguish_candidates",
targetDomain: "relationship",
targetKind: null,
expectedAnswerSchema: {
choice: {
prompt: "2024 年前后,相处方式更接近其中一种?",
option_a: D9_STYLE_OPTIONS[0].label,
option_b: D9_STYLE_OPTIONS[1].label,
option_c: D9_STYLE_OPTIONS[2].label,
option_d: D9_STYLE_OPTIONS[3].label,
options: [
{ key: "A", label: D9_STYLE_OPTIONS[0].label, answer_class: "yes" },
{ key: "B", label: D9_STYLE_OPTIONS[1].label, answer_class: "weak_yes" },
{ key: "C", label: D9_STYLE_OPTIONS[2].label, answer_class: "no" },
{ key: "D", label: D9_STYLE_OPTIONS[3].label, answer_class: "unsure" },
],
},
probe_id: `contrast:${D9_STALE_KEY}`,
semantic_key: D9_STALE_KEY,
candidate_split_hash: schemaHash,
choice_kind: "varga_style",
},
},
declinedSkippedTopics: [],
},
latestResult: {
resultId: "66666666-6666-4666-8666-666666666666",
selectionAllowed: false,
candidates: [
{ time: "05:00", relativeSupport: 34 },
{ time: "05:06", relativeSupport: 33 },
{ time: "05:07", relativeSupport: 33 },
],
decisionReceipt: {
window_scan: {
scanned: true,
d9_candidates_differ: true,
d9_sign_names: ["天秤座", "天蝎座", "射手座"],
transitions: [
{ layer: "d9", at: "05:06", from_sign: "天秤座", to_sign: "天蝎座" },
{ layer: "d9", at: "05:07", from_sign: "天蝎座", to_sign: "射手座" },
],
},
inference_state: {
algorithm_version: "rectification-inference-v1",
candidate_set_id: CURRENT_SET_ID,
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: [
{ id: "e-career-a", domain: "career", year: 2020, precision: "month", usage: "training" },
{ id: "e-career-b", domain: "career", year: 2020, precision: "month", usage: "training" },
{ id: "e-rel-end", domain: "relationship", year: 2024, precision: "day", usage: "training" },
{ id: "e-rel-start", domain: "relationship", year: 2024, precision: "month", usage: "holdout" },
],
answered_probes: [],
probes: [{
id: `contrast:${D9_STALE_KEY}`,
semantic_key: D9_STALE_KEY,
// minted one scoring round earlier, so the prefix names the old set
candidate_split_hash: `${STALE_SET_ID}:${D9_STALE_KEY}`,
domain: "relationship",
year: 0,
question: "亲密关系里更接近下面哪一种相处方式?",
candidate_ids: ["05:00", "05:06", "05:07"],
information_gain: 1.53,
source: "varga_contrast",
choice_kind: "varga_style",
style_options: D9_STYLE_OPTIONS,
expected_outcomes: [
{ answer_class: "yes", supports: ["05:00"], conflicts: ["05:06", "05:07"] },
{ answer_class: "weak_yes", supports: ["05:06"], conflicts: ["05:00", "05:07"] },
{ answer_class: "no", supports: ["05:07"], conflicts: ["05:00", "05:06"] },
],
}],
rounds: [],
entropy: 1.5,
representative_time: "05:00",
credible_range: ["05:00", "05:07"],
},
},
},
case: { acceptedTime: null },
turns: [],
};
}
test("GET still renders the card when the probe hash carries an older candidate set prefix", () => {
const card = choiceCardFromCaseDossier(
staleSplitDossier(`${STALE_SET_ID}:${D9_STALE_KEY}`) as never,
);
assert.ok(card, "a stale candidate-set prefix must not silence the persisted card");
assert.equal(card.probe_id, `contrast:${D9_STALE_KEY}`);
assert.equal(card.question_id, `probe:${D9_STALE_KEY}`);
});
test("GET keeps hiding the card when the persisted split belongs to another probe", () => {
const card = choiceCardFromCaseDossier(
staleSplitDossier(`${CURRENT_SET_ID}:varga.d10.05:00|05:06/05:07`) as never,
);
assert.equal(card, null);
});
test("GET still renders an open out-of-sample existence card without probe_year", () => {
const oosCopy = {
prompt: "2020 年前后,有没有开始一段认真关系、分手或结婚?",
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,
options: DYNAMIC_STYLE_OPTIONS.map((option, index) => ({
key: (["A", "B", "C", "D"] as const)[index]!,
label: option.label,
answer_class: option.answer_class,
})),
};
const card = choiceCardFromCaseDossier({
evidence: [{
id: "ev-education",
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: "2016-01-01",
eventKind: "education_start",
summary: "2016 年开始上大学",
}, {
id: "ev-career-entry",
status: "confirmed",
domain: "career",
datePrecision: "month",
occurredFrom: "2020-04-01",
occurredTo: null,
eventKind: "career_entry",
summary: "2020年4月开始实习",
}, {
id: "ev-career-exit",
status: "confirmed",
domain: "career",
datePrecision: "month",
occurredFrom: "2020-10-01",
occurredTo: null,
eventKind: "career_exit",
summary: "2020年10月实习离职",
}],
conversationSummary: {
activeFocus: {
id: FOCUS_ID,
questionId: "oos_blind:relationship_style:score",
intent: "out_of_sample_check",
targetDomain: "relationship",
targetKind: null,
expectedAnswerSchema: {
choice: oosCopy,
},
},
declinedSkippedTopics: [],
},
latestResult: {
resultId: "result-oos-open",
selectionAllowed: false,
confirmationAllowed: false,
candidates: [
{ time: "05:12", rank: 1, relativeSupport: 15, tiedMinuteCount: 1 },
{ time: "05:14", rank: 2, relativeSupport: 15, tiedMinuteCount: 1 },
],
decisionReceipt: {
selection_allowed: false,
propose_allowed: false,
confirmation_allowed: false,
precision_stage: { current: "d9_refine", can_stop: true },
discriminating_event_probes: [],
oos_blind_prompts: [{
domain: "relationship",
user_meaning: "校时还没用过感情这条线。有没有一件没提过、但记得大概时间的关系变化?",
used_for_scoring: false,
}],
inference_state: {
algorithm_version: "rectification-inference-v1",
candidate_set_id: "04:45-05:15:05:12,05:14",
revision: 1,
phase: "event_collection",
result_status: "insufficient_evidence",
range_start: "04:45",
range_end: "05:15",
candidates: [
{ id: "05:12", time: "05:12", rank: 1, status: "equivalent", prior_score: 15, posterior_score: 15, probability: 0.5, cluster_range: ["05:12", "05:14"], strong_conflict_count: 0 },
{ id: "05:14", time: "05:14", rank: 2, status: "equivalent", prior_score: 15, posterior_score: 15, probability: 0.5, cluster_range: ["05:12", "05:14"], strong_conflict_count: 0 },
],
events: [
{ id: "ev-education", year: 2016, usage: "training", domain: "education", precision: "year" },
{ id: "ev-career-entry", year: 2020, usage: "training", domain: "career", precision: "month" },
{ id: "ev-career-exit", year: 2020, usage: "holdout", domain: "career", precision: "month" },
],
probes: [],
answered_probes: [],
rounds: [],
entropy: 1,
representative_time: "05:12",
credible_range: ["05:12", "05:14"],
},
},
},
case: { acceptedTime: null, status: "collecting_evidence" },
turns: [{
role: "assistant",
text: "2020 年 4 月开始实习、同年 10 月离职,两件都记下了。接下来我们继续。",
}],
});
assert.ok(card, "an already-open AD out-of-sample card must stay tappable");
assert.equal(card.focus_id, FOCUS_ID);
assert.equal(card.question_id, "oos_blind:relationship_style:score");
assert.equal(card.prompt, oosCopy.prompt);
assert.equal(card.options.length, 4);
assert.equal(card.options[0]?.label, "明确发生且时间吻合");
assert.equal(parseRectificationChoiceCard(card)?.focus_id, FOCUS_ID);
});
test("GET does not mint a tap card from an out-of-sample spoken collect focus", () => {
const card = projectRectificationChoiceCard({
evidence: [{
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
activeFocus: {
id: FOCUS_ID,
questionId: "oos_blind:holdout",
intent: "out_of_sample_check",
targetDomain: "career",
targetKind: null,
expectedAnswerSchema: {
collect: true,
prompt: "上大学之外,你还记得第一份正式工作大概是从哪一年开始干的吗?",
},
},
});
assert.equal(card, null);
});