import assert from "node:assert/strict"; import test from "node:test"; import { buildChoiceFrame, choiceCardUserMessage, HOLDOUT_MESSAGE_PREFIX, isHoldoutVerificationQuote, lifePeriodLabel, 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 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 = {}) { 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("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("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 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 preserve order and carry their own answer class", () => { 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", ...styleOptions[0] }, { key: "B", ...styleOptions[1] }, { key: "C", ...styleOptions[2] }, { key: "D", ...styleOptions[3] }, ]); }); 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.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); });