import assert from "node:assert/strict"; import test from "node:test"; import { buildChoiceFrame, choiceCardUserMessage, HOLDOUT_MESSAGE_PREFIX, isHoldoutVerificationQuote, lifePeriodLabel, mergeChoiceCard, parseAgentChoiceCopy, parseChoiceKeyFromUserMessage, parseRectificationChoiceCard, serverOwnedChoiceCopy, } from "../src/lib/rectification-agentic/v9/choice-card.ts"; import { buildMethodFollowupPlan, projectRectificationChoiceCard } from "../src/lib/rectification-agentic/v9/method-followup.ts"; import { choiceCardFromCaseDossier } from "../src/lib/rectification-agentic/v9/interview-state.ts"; import type { DiscriminatingEventProbe } from "../src/lib/rectification-agentic/v9/refinement-packet.ts"; const SAMPLE_COPY = { prompt: "2016 年前后,有没有明显搬家、离乡或长期异地?", option_a: "是,大概就在那段时间", option_b: "有类似,但年份不对或不够重大", option_c: "没有明显发生", option_d: "不记得 / 不确定", } as const; const 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"] }, ], }; test("choice frames ask one biographical event from a server probe, not competing charts", () => { const frame = buildChoiceFrame( { method_id: "d4_home", ask_theme: "home_change", domain: "relocation", user_prompt_hint: "unused", }, { observations: [ { layer: "d9", candidates_differ: true, ask_theme: "relationship_style" }, { layer: "d10", candidates_differ: true, ask_theme: "career_style" }, { layer: "d4", candidates_differ: true, ask_theme: "home_change" }, ], evidence: [{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null, }], probes: [MOVE_PROBE], }, ); assert.equal(frame.period, "2016 年前后"); assert.match(frame.why, /年份锁定/); assert.match(frame.why, /请写成/); assert.doesNotMatch(frame.why, /^2016 年前后,有没有明显/); assert.match(frame.option_a_hint, /是,大概就在那段时间/); assert.match(frame.option_b_hint, /年份不对或不够重大/); assert.doesNotMatch(frame.option_a_hint + frame.option_b_hint, /更像哪一件|可能性/); assert.doesNotMatch(frame.why, /更像哪一件/); assert.equal(/外貌|疤痕|胎记/.test(frame.option_a_hint + frame.option_b_hint), false); assert.equal(frame.scoring, true); assert.equal(mergeChoiceCard(frame, null), null); }); test("server-owned card names the event family, not a generic 有没有这件事", () => { const frame = buildChoiceFrame( { method_id: "d4_home", ask_theme: "home_change", domain: "relocation", user_prompt_hint: "unused", }, { probes: [MOVE_PROBE] }, ); const copy = serverOwnedChoiceCopy(frame); assert.ok(copy); assert.match(copy.prompt, /搬家、离乡或长期异地/); assert.doesNotMatch(copy.prompt, /有没有这件事/); assert.doesNotMatch(copy.prompt, /有没有明显/); }); test("the visible card uses Agent copy for A/B/C/D, not the server hint", () => { const frame = buildChoiceFrame({ method_id: "d4_home", ask_theme: "home_change", domain: "relocation", user_prompt_hint: "unused", }, { evidence: [{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null, }], probes: [MOVE_PROBE], }); const card = mergeChoiceCard(frame, SAMPLE_COPY); assert.equal(card?.prompt, SAMPLE_COPY.prompt); assert.equal(card?.options[0]?.label, SAMPLE_COPY.option_a); assert.equal(card?.options[1]?.label, SAMPLE_COPY.option_b); assert.equal(card?.options[2]?.label, SAMPLE_COPY.option_c); assert.equal(card?.options[3]?.label, SAMPLE_COPY.option_d); assert.equal(card?.stop_label, frame.stop_label); }); test("holdout messages are prefixed and do not count as scoring quotes", () => { const frame = buildChoiceFrame( { method_id: "oos_blind", ask_theme: "oos_blind", domain: "family", user_prompt_hint: "unused", }, { scoring: false }, ); const card = mergeChoiceCard(frame, { prompt: "家人这条线还没用过,有没有一件记得大概年份的变化?", option_a: "是,大概就在那段时间", option_b: "有类似,但年份不对或不够重大", option_c: "没有明显发生", option_d: "不记得 / 不确定", }); assert.ok(card); const message = choiceCardUserMessage(card, "A"); assert.match(message, new RegExp(HOLDOUT_MESSAGE_PREFIX)); assert.equal(isHoldoutVerificationQuote(message), true); assert.equal(isHoldoutVerificationQuote("A. 认真关系进入或结婚"), false); assert.equal(card.scoring, false); assert.match(frame.why, /盘外核对/); }); test("reverse-verify after adopt scores the predicted event and invites 改选 on mismatch", () => { const frame = buildChoiceFrame( { method_id: "reverse_verify", ask_theme: "career_style", domain: "career", user_prompt_hint: "unused", }, { probes: [{ year: 2018, year_label: "2018 年前后", domain: "career", event_family: "入职、升职或职责明显加重", source: "dasha_activation", tracks: ["vimshottari", "narayana"], tracks_agree: true, unique_minute_claim: false, user_meaning: "年份锁定 2018 年前后。", role: "reverse_verify", }], }, ); assert.equal(frame.period, "2018 年前后"); assert.equal(frame.scoring, true); assert.match(frame.why, /按当前采用时间核对/); assert.match(frame.why, /改选/); assert.doesNotMatch(frame.why, /盘外核对/); const card = mergeChoiceCard(frame, SAMPLE_COPY); assert.ok(card); assert.equal(card.scoring, true); assert.doesNotMatch(choiceCardUserMessage(card, "A"), new RegExp(HOLDOUT_MESSAGE_PREFIX)); }); test("life period only reuses years from the same domain", () => { assert.equal(lifePeriodLabel([ { status: "confirmed", datePrecision: "unknown", occurredFrom: null, occurredTo: null }, ]), "那段时间"); assert.equal(lifePeriodLabel([ { status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null }, { status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2020-06-01", occurredTo: null }, ], "education"), "2016 年前后"); assert.equal(lifePeriodLabel([ { status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null }, ], "relocation"), "那段时间"); }); test("known-event quality probe uses the recorded year, not a discriminator split", () => { const frame = buildChoiceFrame({ method_id: "d5_education", ask_theme: "education_style", domain: "education", user_prompt_hint: "unused", choice_kind: "event_quality", }, { evidence: [{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2015-06-01", occurredTo: null, }], probes: [{ year: 2015, year_label: "2015 年前后", domain: "education", event_family: "高考或重要考试发挥明显失常、压力很大", source: "known_event_quality", tracks: ["vimshottari", "narayana"], tracks_agree: true, unique_minute_claim: false, user_meaning: "年份锁定 2015 年前后。已有相关经历。请按事件家族改写成自然语言。不得改年份。", role: "clarify", phase: "event_clarification", choice_kind: "event_quality", }], }); assert.equal(frame.period, "2015 年前后"); assert.equal(frame.choice_kind, "event_quality"); assert.doesNotMatch(`${frame.why}${frame.option_a_hint}${frame.option_b_hint}`, /更像哪一件/); }); test("age-band fallback uses birth date, not another event's year", () => { const frame = buildChoiceFrame({ method_id: "d4_home", ask_theme: "home_change", domain: "relocation", user_prompt_hint: "unused", }, { birthDate: "1997-08-08", evidence: [{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null, }], }); assert.equal(frame.period, "2018 年前后"); assert.match(frame.option_a_hint, /是,大概就在那段时间/); assert.doesNotMatch(frame.period, /2016/); }); test("agent choice copy rejects appearance, scars, clock times, and incomplete C/D", () => { assert.equal(parseAgentChoiceCopy({ choice: { prompt: "你长得更像哪一种?", option_a: "外貌偏圆", option_b: "有疤痕", option_c: "都没有", option_d: "不记得" }, }), null); assert.equal(parseAgentChoiceCopy({ ...SAMPLE_COPY, prompt: "08:12 换升时发生了什么?", }), null); assert.equal(parseAgentChoiceCopy({ prompt: SAMPLE_COPY.prompt, option_a: SAMPLE_COPY.option_a, option_b: SAMPLE_COPY.option_b, }), null); assert.equal(parseAgentChoiceCopy({ ...SAMPLE_COPY, option_c: SAMPLE_COPY.option_a, }), null); assert.ok(parseAgentChoiceCopy(SAMPLE_COPY)); }); test("GET card 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 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", 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", }], 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. 没有明显发生"), "C"); assert.equal(parseChoiceKeyFromUserMessage("D、不记得 / 不确定"), "D"); assert.equal(parseChoiceKeyFromUserMessage(`${HOLDOUT_MESSAGE_PREFIX}:B. 有类似但年份不对`), "B"); assert.equal(parseChoiceKeyFromUserMessage("没有明显发生"), null); }); test("GET choice_card stays after coverage when remaining minutes still split on D24", () => { const card = choiceCardFromCaseDossier({ evidence: [{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null, summary: "2016 年大学入学", }, { status: "confirmed", domain: "relationship", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null, }, { status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2019-01-01", occurredTo: null, }, { status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null, }, { status: "draft", domain: "occupation", datePrecision: "unknown", occurredFrom: null, occurredTo: null, eventKind: "occupation_note", summary: "医疗器械算法,第三个(技术执行)", }], conversationSummary: { activeFocus: { id: "abababab-abab-4bab-8bab-abababababab", intent: "distinguish_candidates", targetDomain: "education", targetKind: "education_milestone", expectedAnswerSchema: { choice: SAMPLE_COPY, probe_id: "contrast:varga.d24.05:00/05:06|05:07", semantic_key: "varga.d24.05:00/05:06|05:07", }, }, declinedSkippedTopics: [], }, latestResult: { resultId: "55555555-5555-4555-8555-555555555555", selectionAllowed: true, candidates: [ { time: "05:00", relativeSupport: 34 }, { time: "05:06", relativeSupport: 33 }, { time: "05:07", relativeSupport: 33 }, ], decisionReceipt: { propose_allowed: true, window_scan: { scanned: true, d9_lagna_count: 1, d10_lagna_count: 3, d10_candidates_differ: true, d10_sign_names: ["巨蟹座", "狮子座", "处女座"], d24_candidates_differ: true, transitions: [ { layer: "d10", at: "05:00" }, { layer: "d10", at: "05:15" }, { layer: "d24", at: "05:00" }, { layer: "d24", at: "05:06" }, ], }, inference_state: { answered_probes: [{ probe_id: "probe:education.2016", semantic_key: "education.2016", answer_class: "yes", }], probes: [{ id: "probe:education.2016", semantic_key: "education.2016", information_gain: 0, source: "known_event_quality", expected_outcomes: [], }], }, }, }, case: { acceptedTime: null }, turns: [], }); assert.ok(card); assert.equal(card.probe_id, "contrast:varga.d24.05:00/05:06|05:07"); assert.equal(card.prompt, SAMPLE_COPY.prompt); }); test("GET choice_card stays hidden after 没有了 when selection is allowed", () => { const card = choiceCardFromCaseDossier({ evidence: [{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null, }, { status: "confirmed", domain: "relationship", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null, }, { status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2019-01-01", occurredTo: null, }, { status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null, }, { status: "draft", domain: "occupation", datePrecision: "unknown", occurredFrom: null, occurredTo: null, eventKind: "occupation_note", }], conversationSummary: { activeFocus: null, declinedSkippedTopics: [], }, latestResult: { selectionAllowed: true, candidates: [ { time: "05:00", relativeSupport: 34 }, { time: "05:06", relativeSupport: 33 }, { time: "05:07", relativeSupport: 33 }, ], decisionReceipt: { propose_allowed: true }, }, case: { acceptedTime: null }, turns: [{ role: "user", text: "没有了" }], }); assert.equal(card, null); }); test("remaining D10 style card uses type-table labels instead of existence", () => { const frame = buildChoiceFrame({ method_id: "d10_career", ask_theme: "career_style", domain: "career", user_prompt_hint: "unused", choice_kind: "varga_style", style_options: [ { label: "照顾、情感", answer_class: "yes", sign: "巨蟹座" }, { label: "领导、表演", answer_class: "weak_yes", sign: "狮子座" }, { label: "细致、服务", answer_class: "no", sign: "处女座" }, ], }); const copy = serverOwnedChoiceCopy(frame); assert.ok(copy); assert.equal(frame.choice_kind, "varga_style"); assert.match(copy.prompt, /长期工作更接近哪一类/); assert.doesNotMatch(copy.prompt, /有没有明显/); assert.equal(copy.option_a, "照顾、情感"); assert.equal(copy.option_b, "领导、表演"); assert.equal(copy.option_c, "细致、服务"); }); test("two-way remaining D9 puts 都不像 on C", () => { const frame = buildChoiceFrame({ method_id: "d9_relationship", ask_theme: "relationship_style", domain: "relationship", user_prompt_hint: "unused", choice_kind: "varga_style", style_options: [ { label: "情感丰富、家庭导向", answer_class: "yes", sign: "巨蟹座" }, { label: "骄傲、戏剧性、领导欲", answer_class: "weak_yes", sign: "狮子座" }, ], }); const copy = serverOwnedChoiceCopy(frame); assert.ok(copy); assert.match(copy.prompt, /相处/); assert.match(copy.option_c, /都不像/); assert.match(copy.option_d, /不确定/); }); test("known exam quality card keeps the recorded year and quality mapping", () => { const frame = buildChoiceFrame({ method_id: "d5_education", ask_theme: "education_style", domain: "education", user_prompt_hint: "unused", choice_kind: "event_quality", }, { probes: [{ year: 2015, year_label: "2015 年前后", domain: "education", event_family: "高考或重要考试发挥明显失常、压力很大", source: "known_event_quality", tracks: ["vimshottari", "narayana"], tracks_agree: true, unique_minute_claim: false, user_meaning: "年份锁定 2015 年前后。已有相关经历。请按事件家族改写成自然语言。不得改年份。", role: "clarify", phase: "event_clarification", choice_kind: "event_quality", }], }); const copy = serverOwnedChoiceCopy(frame); assert.ok(copy); assert.equal(frame.period, "2015 年前后"); assert.equal(frame.choice_kind, "event_quality"); assert.doesNotMatch(copy.prompt, /有没有/); }); 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", }], 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("GET without agent speech shows the lock label, not a yes/no template", () => { 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", }], activeFocus: { id: FOCUS_ID, intent: "distinguish_candidates", targetDomain: "relationship", targetKind: "relationship_change", expectedAnswerSchema: { choice: fallback, probe_id: "relationship.2018" }, }, }); assert.ok(card); assert.equal(card.prompt, fallback.prompt); assert.doesNotMatch(card.prompt, /有没有/); }); test("career event_quality does not reuse the exam underperformance copy", () => { 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", choice_kind: "event_quality", }], }); const copy = serverOwnedChoiceCopy(frame); assert.ok(copy); assert.match(copy.prompt, /2020 年前后/); assert.match(copy.prompt, /入职、升职或职责明显加重/); assert.doesNotMatch(copy.prompt, /高考|失常|压力很大/); assert.doesNotMatch(copy.prompt, /有没有/); assert.match(copy.prompt, /·/); assert.equal(copy.option_a, "是,大概就在那段时间"); assert.doesNotMatch(copy.option_a, /失常/); }); test("dasha existence cards lock the year and family, not a yes/no template", () => { const frame = buildChoiceFrame({ method_id: "d4_home", ask_theme: "home_change", domain: "relocation", user_prompt_hint: "unused", choice_kind: "existence", }, { probes: [MOVE_PROBE] }); const copy = serverOwnedChoiceCopy(frame); assert.ok(copy); assert.match(copy.prompt, /2016 年前后/); assert.match(copy.prompt, /搬家、离乡或长期异地/); assert.doesNotMatch(copy.prompt, /有没有明显/); assert.match(copy.option_a, /是,大概就在那段时间/); assert.match(copy.option_c, /没有明显发生/); });