Files
Jyotisha/frontend/tests/rectification-choice-card.test.ts
T
Jesse_Chen a03756bf18
Independent Staging Quality Gate / validate (push) Failing after 11m51s
Independent Staging Quality Gate / publish (push) Has been skipped
feat(web): hang Agent-authored A/B/C/D cards under rectification replies
Conflict nodes stay server-owned; the Agent writes the question and option copy so users can tap instead of typing through an interrogation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-21 17:36:32 +08:00

158 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import test from "node:test";
import {
buildChoiceFrame,
choiceCardUserMessage,
HOLDOUT_MESSAGE_PREFIX,
isHoldoutVerificationQuote,
lifePeriodLabel,
mergeChoiceCard,
parseAgentChoiceCopy,
parseRectificationChoiceCard,
} from "../src/lib/rectification-agentic/v9/choice-card.ts";
import { buildMethodFollowupPlan, projectRectificationChoiceCard } from "../src/lib/rectification-agentic/v9/method-followup.ts";
const SAMPLE_COPY = {
prompt: "2016 年前后,更像哪一件?",
option_a: "那时开始一段认真交往",
option_b: "那时工作突然压上来",
option_c: "那几年感情和工作都没什么明显变化",
option_d: "不记得那几年发生过什么",
} as const;
test("choice frames reverse-infer competing relationship vs career when D9 and D10 both differ", () => {
const frame = buildChoiceFrame(
{
method_id: "d9_relationship",
ask_theme: "relationship_style",
domain: "relationship",
user_prompt_hint: "unused",
},
{
observations: [
{ layer: "d9", candidates_differ: true, ask_theme: "relationship_style" },
{ layer: "d10", candidates_differ: true, ask_theme: "career_style" },
],
evidence: [{
status: "confirmed",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
},
);
assert.equal(frame.period, "2016 年前后");
assert.match(frame.option_a_hint, /认真关系/);
assert.match(frame.option_b_hint, /职责/);
assert.equal(/外貌|疤痕|胎记/.test(frame.option_a_hint + frame.option_b_hint), false);
assert.equal(frame.scoring, true);
assert.equal(mergeChoiceCard(frame, null), null);
});
test("the visible card uses Agent copy for A/B/C/D, not the server hint", () => {
const frame = buildChoiceFrame({
method_id: "d9_relationship",
ask_theme: "relationship_style",
domain: "relationship",
user_prompt_hint: "unused",
}, {
evidence: [{
status: "confirmed",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
}],
});
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?.options[2]?.label === frame.neither_label, false);
assert.equal(card?.options[3]?.label === frame.unsure_label, false);
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);
});
test("life period collapses a single dated year and skips unknown dates", () => {
assert.equal(lifePeriodLabel([
{ status: "confirmed", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
]), "按还在比的两套盘");
assert.equal(lifePeriodLabel([
{ status: "confirmed", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null },
{ status: "confirmed", datePrecision: "year", occurredFrom: "2020-06-01", occurredTo: null },
]), "20182020 年这段里");
});
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 appears only after the Agent writes choice copy onto the focus", () => {
const withoutCopy = projectRectificationChoiceCard({ evidence: [] });
assert.equal(withoutCopy, null);
const withCopy = projectRectificationChoiceCard({
evidence: [],
activeFocus: {
intent: "collect_method_evidence",
targetDomain: null,
targetKind: null,
expectedAnswerSchema: { choice: SAMPLE_COPY },
},
});
assert.equal(withCopy?.prompt, SAMPLE_COPY.prompt);
assert.equal(withCopy?.options[2]?.label, SAMPLE_COPY.option_c);
assert.ok(parseRectificationChoiceCard(withCopy));
});
test("opening follow-up ships a discriminator frame, not a canned user card", () => {
const plan = buildMethodFollowupPlan({ evidence: [] });
assert.equal(plan.next_followup?.method_id, "dasha_events");
assert.ok(plan.next_followup?.choice_frame);
assert.equal(plan.next_followup?.choice_frame.choice_mode, "A/B/C/D");
assert.doesNotMatch(plan.next_followup?.user_prompt_hint ?? "", /请用 A\/B\/C\/D 回答/);
assert.match(plan.next_followup?.user_prompt_hint ?? "", /expectedAnswerSchema\.choice/);
});