9873ba420a
Empty ledgers stay in natural-language collection. After the first dated event, dasha conflict probes reverse-infer 前事 and block offer until answered. Unique-minute confirmation stays closed at a representative time; adopt reverse-verifies remaining probes. Records BUG-348–351. Co-authored-by: Cursor <cursoragent@cursor.com>
375 lines
13 KiB
TypeScript
375 lines
13 KiB
TypeScript
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";
|
|
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 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: "reverse_verify",
|
|
};
|
|
|
|
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("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 asks 发挥失常 instead of existence", () => {
|
|
const frame = buildChoiceFrame({
|
|
method_id: "d5_education",
|
|
ask_theme: "education_style",
|
|
domain: "education",
|
|
user_prompt_hint: "unused",
|
|
}, {
|
|
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: "distinguish",
|
|
}],
|
|
});
|
|
assert.equal(frame.period, "2015 年前后");
|
|
assert.match(frame.why, /发挥失常/);
|
|
assert.match(frame.why, /请写成/);
|
|
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 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: {
|
|
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: "reverse_verify",
|
|
}],
|
|
activeFocus: {
|
|
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 ?? "", /不要写 expectedAnswerSchema\.choice/);
|
|
});
|
|
|
|
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 ?? "", /不得发明年份/);
|
|
});
|