Dated-choice exhaustion is not convergence. Refresh probes from remaining active candidates, then ask a targeted collect, then deliver. Skill 10.0.24. Co-authored-by: Cursor <cursoragent@cursor.com>
181 lines
6.7 KiB
TypeScript
181 lines
6.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
COLLECT_FLOW_BANNED_PHRASES,
|
|
anchoredFollowups,
|
|
collectionQuestionPool,
|
|
inviteDeclined,
|
|
moreCollectHint,
|
|
preciseGapNarration,
|
|
rangeNarrowHint,
|
|
remainingSplitLayers,
|
|
targetedCollectPool,
|
|
} from "../src/lib/rectification-agentic/v9/collection-question-pool.ts";
|
|
import { USER_COLLECT_QUESTION, USER_COLLECT_QUESTION_RETRY } from "../src/lib/rectification-agentic/user-copy.ts";
|
|
import { isOccupationCollectFocus } from "../src/lib/rectification-agentic/v9/evidence-model.ts";
|
|
import { followupFromPoolItem } from "../src/lib/rectification-agentic/v9/method-followup.ts";
|
|
import { stableFollowupQuestionId } from "../src/lib/rectification-agentic/v9/server-focus.ts";
|
|
|
|
const educationStart = {
|
|
status: "confirmed",
|
|
domain: "education",
|
|
datePrecision: "month",
|
|
occurredFrom: "2016-09-01",
|
|
occurredTo: "2016-09-30",
|
|
eventKind: "education_start",
|
|
summary: "上大学",
|
|
} as const;
|
|
|
|
const educationEnd = {
|
|
status: "confirmed",
|
|
domain: "education",
|
|
datePrecision: "month",
|
|
occurredFrom: "2020-06-01",
|
|
occurredTo: "2020-06-30",
|
|
eventKind: "education_completion",
|
|
summary: "毕业",
|
|
} as const;
|
|
|
|
const inviteDeclinedTopic = {
|
|
target_domain: "other",
|
|
status: "declined",
|
|
intent: "collect_method_evidence",
|
|
questionId: "collect:invite:more",
|
|
target_kind: "invite_more",
|
|
};
|
|
|
|
function assertNoBanned(text: string) {
|
|
for (const phrase of COLLECT_FLOW_BANNED_PHRASES) {
|
|
assert.equal(text.includes(phrase), false, phrase);
|
|
}
|
|
}
|
|
|
|
test("invite stays first while it is producing, then drops after a decline", () => {
|
|
const first = collectionQuestionPool([educationStart]);
|
|
assert.equal(first[0]?.kind, "invite");
|
|
assert.equal(first[0]?.key, "collect:invite:more");
|
|
assert.match(first[0]?.prompt ?? "", /^还有吗?/);
|
|
const still = collectionQuestionPool([educationStart, educationEnd]);
|
|
assert.equal(still[0]?.kind, "invite");
|
|
assert.equal(inviteDeclined([inviteDeclinedTopic]), true);
|
|
const afterDecline = collectionQuestionPool(
|
|
[educationStart, educationEnd],
|
|
[inviteDeclinedTopic],
|
|
);
|
|
assert.notEqual(afterDecline[0]?.kind, "invite");
|
|
assert.equal(afterDecline.some((item) => item.kind === "invite"), false);
|
|
});
|
|
|
|
test("education completion anchors to the graduation job question from the user year", () => {
|
|
const asked = new Set<string>();
|
|
const anchors = anchoredFollowups([educationStart, educationEnd], new Set(), asked);
|
|
assert.match(anchors[0]?.prompt ?? "", /2020 年毕业后第一份工作/);
|
|
assert.doesNotMatch(anchors.map((item) => item.prompt).join("\n"), /年前后/);
|
|
const declinedAnchor = collectionQuestionPool(
|
|
[educationStart, educationEnd],
|
|
[
|
|
inviteDeclinedTopic,
|
|
{
|
|
target_domain: "career",
|
|
status: "declined",
|
|
intent: "collect_method_evidence",
|
|
questionId: "collect:anchor:education_completion:2020",
|
|
target_kind: "anchor:education_completion:2020",
|
|
},
|
|
],
|
|
);
|
|
assert.equal(
|
|
declinedAnchor.some((item) => item.key === "collect:anchor:education_completion:2020"),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("precise gap names the recorded events and at least two uncovered kinds", () => {
|
|
const gap = preciseGapNarration([educationStart, educationEnd], [inviteDeclinedTopic]);
|
|
assert.match(gap, /现在记下的是/);
|
|
assert.match(gap, /2016 年 9 月上大学/);
|
|
assert.match(gap, /2020 年 6 月毕业/);
|
|
assert.match(gap, /都是上学的事/);
|
|
assert.match(gap, /就能开始筛/);
|
|
const examples = (gap.match(/第一份工作|搬到别的城市|谈恋爱|家里添丁|生病受伤/g) ?? []).length;
|
|
assert.ok(examples >= 2, gap);
|
|
assertNoBanned(gap);
|
|
});
|
|
|
|
test("more-collect hint after delivery never uses banned collect-flow phrases", () => {
|
|
const hint = moreCollectHint([educationStart, educationEnd], [inviteDeclinedTopic]);
|
|
assert.match(hint, /范围还能再收一截/);
|
|
assertNoBanned(hint);
|
|
});
|
|
|
|
test("generic occupation collect does not ask for a year", () => {
|
|
assert.match(USER_COLLECT_QUESTION.occupation, /你平时主要做什么工作/);
|
|
assert.doesNotMatch(USER_COLLECT_QUESTION.occupation, /哪年|哪一年|年份/);
|
|
assert.doesNotMatch(USER_COLLECT_QUESTION_RETRY.occupation, /哪年|哪一年|年份/);
|
|
});
|
|
|
|
test("graduation job follow-up is a career anchor, not occupation collect", () => {
|
|
const asked = new Set<string>();
|
|
const anchors = anchoredFollowups([educationStart, educationEnd], new Set(), asked);
|
|
const job = anchors.find((item) => item.key === "collect:anchor:education_completion:2020");
|
|
assert.ok(job);
|
|
assert.equal(job?.domain, "career");
|
|
assert.match(job?.prompt ?? "", /2020 年毕业后第一份工作/);
|
|
const followup = followupFromPoolItem(job!);
|
|
assert.equal(followup.domain, "career");
|
|
const questionId = stableFollowupQuestionId(followup);
|
|
assert.equal(questionId, "collect:anchor:education_completion:2020");
|
|
assert.equal(questionId.startsWith("collect:occupation:"), false);
|
|
assert.equal(isOccupationCollectFocus({
|
|
intent: followup.intent,
|
|
targetDomain: followup.domain,
|
|
targetKind: followup.kind_hint,
|
|
questionId,
|
|
}), false);
|
|
});
|
|
|
|
test("targeted collect names remaining split layers without inferred years", () => {
|
|
const career = {
|
|
status: "confirmed",
|
|
domain: "career",
|
|
datePrecision: "month",
|
|
occurredFrom: "2020-04-01",
|
|
occurredTo: null,
|
|
eventKind: "career_entry",
|
|
summary: "入职实习",
|
|
} as const;
|
|
const layers = remainingSplitLayers({
|
|
transitions: [
|
|
{ layer: "d9", at: "04:52" },
|
|
{ layer: "d10", at: "05:00" },
|
|
{ layer: "d4", at: "05:00" },
|
|
{ layer: "d12", at: "05:00" },
|
|
{ layer: "d2", at: "05:00" },
|
|
{ layer: "d11", at: "05:07" },
|
|
],
|
|
activeTimes: ["04:48", "04:53", "05:06", "05:07"],
|
|
});
|
|
const pool = targetedCollectPool(
|
|
layers,
|
|
[educationStart, educationEnd, career],
|
|
[],
|
|
["04:48", "05:07"],
|
|
);
|
|
assert.equal(pool.length, 1);
|
|
assert.equal(pool[0]?.kind, "targeted");
|
|
assert.equal(pool[0]?.key, "collect:targeted:relationship");
|
|
assert.match(pool[0]?.prompt ?? "", /能把 04:48 和 05:07 分开/);
|
|
assert.doesNotMatch(pool[0]?.prompt ?? "", /1997|2012|推算/);
|
|
assert.ok((pool[0]?.examples?.length ?? 0) >= 2);
|
|
const followup = followupFromPoolItem(pool[0]!);
|
|
assert.equal(stableFollowupQuestionId(followup), "collect:targeted:relationship");
|
|
assert.match(rangeNarrowHint(layers, [educationStart, educationEnd, career], [], ["04:48", "05:07"]), /还能再收窄:如果记得/);
|
|
const declined = targetedCollectPool(layers, [educationStart, educationEnd, career], [{
|
|
questionId: "collect:targeted:relationship",
|
|
status: "declined",
|
|
target_kind: "targeted:relationship",
|
|
}]);
|
|
assert.equal(declined.length, 0);
|
|
});
|