Files
Jyotisha/frontend/tests/rectification-guided-collect-20260916.test.ts
T
jesse-ux cfb41daf3d
Independent Staging Quality Gate / validate (push) Failing after 6m28s
Independent Staging Quality Gate / publish (push) Skipped
feat(rectification): 出卡加精度门槛,补经历改成系统点名
宽度超过 10 分钟或头名并列时不再出交付卡,改为按大运边界逐条问、
用类型芯片和年/月选择器录入。跳过的线换问法再问一次;答「这类事
都没有过」的不再问。用户说「没有了」仍立刻给目前范围。Skill 10.0.27。

BUG-740~743
2026-09-16 18:35:27 +08:00

216 lines
8.9 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 {
COLLECT_KIND_ORDER,
GUIDED_NARROW_HINT,
TARGETED_EXISTENCE_PROMPT,
TARGETED_EXISTENCE_PROMPT_RETRY,
guidedCollectExhausted,
guidedRetryPool,
guidedWindowPool,
guidedWindowPrompt,
parseYearEntryQuestionId,
targetedCollectPool,
rangeNarrowHint,
remainingGuidedLine,
type CollectionEvidence,
type GuidedCollectWindow,
} from "../src/lib/rectification-agentic/v9/collection-question-pool.ts";
import { targetedCollectFollowup } from "../src/lib/rectification-agentic/v9/method-followup.ts";
import {
TARGETED_COLLECT_OPTION_B,
GUIDED_WINDOW_OPTION_B,
} from "../src/lib/rectification-agentic/v9/choice-card.ts";
import { RECTIFICATION_USER_COPY } from "../src/lib/rectification-agentic/user-copy.ts";
import { eventDateYearRange, formatEventDateEntryMessage } from "../src/components/event-date-entry-card.tsx";
const WINDOWS: readonly GuidedCollectWindow[] = [
{ year: 2018, month_lo: 3, month_hi: 6, domain: "career", split: { left: 2, right: 3 } },
{ year: 2021, month_lo: 8, month_hi: 8, domain: "relationship", split: { left: 3, right: 2 } },
];
test("guided pool order is windows, skip retry, then uncovered domains", () => {
const evidence: CollectionEvidence[] = [
{ status: "confirmed", domain: "education", datePrecision: "month", occurredFrom: "2014-09-01", occurredTo: "2014-09-01" },
];
const skipped = [
{
questionId: "collect:targeted:family",
target_domain: "family",
status: "skipped",
intent: "collect_method_evidence",
target_kind: "targeted:family",
},
];
const windows = guidedWindowPool(WINDOWS, evidence, skipped);
const retry = guidedRetryPool(evidence, skipped);
const open = targetedCollectPool(["d9", "d10"], evidence, skipped);
assert.equal(windows[0]?.guidedSource, "window");
assert.equal(windows[0]?.domain, "career");
assert.equal(retry[0]?.guidedSource, "retry");
assert.equal(retry[0]?.domain, "family");
assert.ok(open.some((item) => item.domain === "relationship"));
assert.equal(open.some((item) => item.domain === "family"), false);
const followup = targetedCollectFollowup(["d9"], evidence, skipped, ["04:51", "05:11"], 5, ["04:51", "05:11"], WINDOWS);
assert.equal(followup?.collection_key, windows[0]?.key);
});
test("A on a window yields a year-entry card, B/C close only that window", () => {
const evidence: CollectionEvidence[] = [];
const afterYes = [
{
questionId: "collect:guided:window:2018:3:6:career",
target_domain: "career",
status: "resolved",
intent: "collect_method_evidence",
target_kind: "guided:window:career",
},
];
const year = parseYearEntryQuestionId("collect:guided:window:2018:3:6:career:year");
assert.equal(year?.domain, "career");
assert.equal(year?.year, 2018);
const pending = targetedCollectFollowup([], evidence, afterYes, null, 5, ["04:51", "05:11"], WINDOWS);
assert.equal(pending?.collection_key?.endsWith(":year"), true);
const afterNo = [
{
questionId: "collect:guided:window:2018:3:6:career",
target_domain: "career",
status: "declined",
intent: "collect_method_evidence",
target_kind: "guided:window:career",
},
];
const next = targetedCollectFollowup(["d10"], evidence, afterNo, null, 5, null, WINDOWS);
assert.notEqual(next?.collection_key, "collect:guided:window:2018:3:6:career");
assert.ok(targetedCollectPool(["d10"], evidence, afterNo).some((item) => item.domain === "career"));
});
test("skipped once retries, declined does not, second skip closes", () => {
const evidence: CollectionEvidence[] = [];
const skipped = [{
questionId: "collect:targeted:relationship",
target_domain: "relationship",
status: "skipped",
intent: "collect_method_evidence",
target_kind: "targeted:relationship",
}];
const retry = guidedRetryPool(evidence, skipped);
assert.equal(retry[0]?.domain, "relationship");
assert.match(retry[0]?.prompt ?? "", /认真关系/);
const declined = [{
questionId: "collect:targeted:relationship",
target_domain: "relationship",
status: "declined",
intent: "collect_method_evidence",
target_kind: "targeted:relationship",
}];
assert.equal(guidedRetryPool(evidence, declined).length, 0);
assert.equal(targetedCollectPool(["d9"], evidence, declined).some((item) => item.domain === "relationship"), false);
const skippedTwice = [
...skipped,
{
questionId: "collect:targeted:relationship:retry",
target_domain: "relationship",
status: "skipped",
intent: "collect_method_evidence",
target_kind: "targeted:relationship:retry",
retry: true,
},
];
assert.equal(guidedRetryPool(evidence, skippedTwice).length, 0);
assert.equal(targetedCollectPool(["d9"], evidence, skippedTwice).some((item) => item.domain === "relationship"), false);
});
test("existence prompts cover the whole domain for every collect kind", () => {
for (const domain of COLLECT_KIND_ORDER) {
assert.match(TARGETED_EXISTENCE_PROMPT[domain], /哪一年都算/);
assert.ok(TARGETED_EXISTENCE_PROMPT_RETRY[domain].length > 8);
}
assert.equal(TARGETED_COLLECT_OPTION_B, "这类事都没有过");
assert.equal(GUIDED_WINDOW_OPTION_B, "这段没有");
assert.match(RECTIFICATION_USER_COPY.collectDeclinedAck, /没有发生过/);
assert.match(RECTIFICATION_USER_COPY.collectSkippedAck, /换个问法再问一次/);
});
test("a layer that cannot split candidates still leaves that domain in the open pool", () => {
const evidence: CollectionEvidence[] = [];
const open = targetedCollectPool([], evidence, []);
assert.deepEqual(open.map((item) => item.domain), [...COLLECT_KIND_ORDER]);
});
test("time-point no does not close a domain", () => {
const evidence: CollectionEvidence[] = [];
const probeNo = [{
questionId: "distinguish:relationship.2018.05.dasha_boundary",
target_domain: "relationship",
status: "declined",
source: "event_probe",
}];
assert.ok(targetedCollectPool(["d9"], evidence, probeNo).some((item) => item.domain === "relationship"));
});
test("unmet-gate hint has no free-text invite", () => {
const declined = COLLECT_KIND_ORDER.map((domain) => ({
questionId: `collect:targeted:${domain}`,
target_domain: domain,
status: "declined",
intent: "collect_method_evidence",
target_kind: `targeted:${domain}`,
}));
const copy = rangeNarrowHint(
["d9", "d10", "d4", "d5", "d7", "d2", "d30"],
[],
declined,
["04:51", "05:11"],
5,
["04:51", "05:11"],
);
assert.match(copy, /再对照几件经历会更准/);
assert.doesNotMatch(copy, /你要是还记得确切哪一天/);
assert.doesNotMatch(copy, /不限领域/);
assert.ok(copy.includes(GUIDED_NARROW_HINT));
const line = remainingGuidedLine(["04:51", "05:11"], 5, ["04:51", "05:11"]);
assert.equal(line, "现在还剩 04:5105:11 里 5 个候选,再对照几件经历会更准");
});
test("window prompt uses oral phrases, not server labels", () => {
const prompt = guidedWindowPrompt({
year: 2018,
month_lo: 3,
month_hi: 6,
domain: "career",
});
assert.equal(prompt, "2018 年 3 到 6 月之间,有没有入职、换工作或职责变重?");
assert.doesNotMatch(prompt, /event_family|career_entry/);
});
test("date entry submit walks the dated evidence path as year-month text", () => {
assert.equal(
formatEventDateEntryMessage({ domain: "relationship", year: 2018, month: 3, day: null }),
"2018 年 3 月,开始认真关系、分手或结婚",
);
});
test("date picker year range is birth year through this year", () => {
assert.deepEqual(eventDateYearRange(1961, 2026), { minYear: 1961, maxYear: 2026 });
assert.deepEqual(eventDateYearRange(null, 2026), { minYear: 1946, maxYear: 2026 });
});
test("guided collect is not exhausted while a window remains", () => {
assert.equal(guidedCollectExhausted([], [], [], WINDOWS, ["04:51", "05:11"], 5), false);
assert.equal(guidedCollectExhausted(["d9", "d10", "d4", "d5", "d7", "d2", "d30"], [
{ status: "confirmed", domain: "education", datePrecision: "month", occurredFrom: "2014-09-01", occurredTo: "2014-09-01" },
{ status: "confirmed", domain: "career", datePrecision: "month", occurredFrom: "2018-07-01", occurredTo: "2018-07-01" },
{ status: "confirmed", domain: "relocation", datePrecision: "month", occurredFrom: "2016-08-01", occurredTo: "2016-08-01" },
{ status: "confirmed", domain: "relationship", datePrecision: "month", occurredFrom: "2021-08-01", occurredTo: "2021-08-01" },
{ status: "confirmed", domain: "family", datePrecision: "month", occurredFrom: "2019-01-01", occurredTo: "2019-01-01" },
{ status: "confirmed", domain: "finance", datePrecision: "month", occurredFrom: "2020-03-01", occurredTo: "2020-03-01" },
{ status: "confirmed", domain: "health_pressure", datePrecision: "month", occurredFrom: "2022-04-01", occurredTo: "2022-04-01" },
], [], [], ["04:51", "05:11"], 5), true);
});