Files
Jyotisha/frontend/tests/rectification-targeted-collect-cards-20260913.test.ts
T
Jesse_ChenandCursor 530f260f76
Independent Staging Quality Gate / validate (push) Successful in 12m9s
Independent Staging Quality Gate / publish (push) Successful in 2m17s
fix(rectification): ask remaining split lines one card at a time (BUG-661~663)
Dated-pool empty now yields existence cards per remaining layer, leftover-candidate copy, and an optional D9/D10 tie-break on the range card. Skill 10.0.25.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 16:50:25 +08:00

223 lines
7.7 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 { applyProbeOutcome } from "../src/lib/rectification-agentic/core/apply-probe-outcome.ts";
import type { ConflictProbe } from "../src/lib/rectification-agentic/core/types.ts";
import { RECTIFICATION_USER_COPY } from "../src/lib/rectification-agentic/user-copy.ts";
import {
SPLIT_ENDPOINT_PHRASE,
pendingTargetedYearDomain,
targetedCollectExhausted,
targetedCollectPool,
} from "../src/lib/rectification-agentic/v9/collection-question-pool.ts";
import { rangeDeliveryForSnapshot } from "../src/lib/rectification-agentic/v9/divergence-panel.ts";
import {
buildMethodFollowupPlan,
targetedCollectFollowup,
tieBreakPersonalityFollowup,
} from "../src/lib/rectification-agentic/v9/method-followup.ts";
const TIMES = ["04:48", "04:53", "05:06", "05:07"] as const;
const LAYERS = ["d9", "d10", "d4", "d12", "d2", "d11"];
const EVIDENCE = [
{
status: "confirmed",
domain: "education",
datePrecision: "month",
occurredFrom: "2016-09-01",
occurredTo: "2016-09-30",
eventKind: "education_start",
summary: "上大学",
},
{
status: "confirmed",
domain: "career",
datePrecision: "month",
occurredFrom: "2020-04-01",
occurredTo: null,
eventKind: "career_entry",
summary: "入职实习",
},
] as const;
function declined(domain: string) {
return {
target_domain: domain,
status: "declined" as const,
intent: "collect_method_evidence",
questionId: `collect:targeted:${domain}`,
target_kind: `targeted:${domain}`,
};
}
test("A resolves existence then asks the year; B/C close only that line", () => {
const first = targetedCollectFollowup(LAYERS, EVIDENCE, [], ["04:48", "05:07"], 4);
assert.equal(first?.collection_key, "collect:targeted:relationship");
assert.equal(first?.choice_frame?.scoring, false);
assert.equal(first?.choice_frame?.prompt, "结过婚或订过婚吗?");
assert.match(first?.spoken_prompt ?? "", /现在还剩 04:4805:07 里 4 个候选/);
assert.doesNotMatch(first?.spoken_prompt ?? "", SPLIT_ENDPOINT_PHRASE);
const afterYes = targetedCollectFollowup(
LAYERS,
EVIDENCE,
[{ ...declined("relationship"), status: "resolved", questionId: "collect:targeted:relationship" }],
["04:48", "05:07"],
4,
);
assert.equal(pendingTargetedYearDomain([{
...declined("relationship"),
status: "resolved",
}], EVIDENCE), "relationship");
assert.equal(afterYes?.collection_key, "collect:targeted:relationship:year");
assert.equal(afterYes?.choice_frame, null);
assert.equal(afterYes?.spoken_prompt, "大概哪年几月?");
const afterNo = targetedCollectPool(
LAYERS,
EVIDENCE,
[declined("relationship")],
["04:48", "05:07"],
4,
);
assert.equal(afterNo[0]?.domain, "relocation");
assert.equal(targetedCollectExhausted(LAYERS, EVIDENCE, [
declined("relationship"),
declined("relocation"),
declined("family"),
declined("finance"),
], ["04:48", "05:07"], 4), true);
});
test("personality D9/D10 stay out of the scoring pool until the range-card opt-in", () => {
const ledger = [
...EVIDENCE,
{
status: "confirmed",
domain: "relationship",
datePrecision: "month",
occurredFrom: "2024-05-01",
occurredTo: null,
eventKind: "relationship_start",
summary: "确定关系",
},
{
status: "confirmed",
domain: "family",
datePrecision: "month",
occurredFrom: "2016-05-01",
occurredTo: null,
eventKind: "family_event",
summary: "家里添丁",
},
];
const d9 = {
probeId: "contrast:varga.d9.巨蟹座/狮子座",
candidateSetVersion: "04:48-05:07",
question: "亲密关系里更接近下面哪一种相处方式?",
expectedOutcomes: [
{ outcomeId: "yes", supportsCandidateIds: ["04:53"], conflictsCandidateIds: ["05:06"] },
{ outcomeId: "weak_yes", supportsCandidateIds: ["05:06"], conflictsCandidateIds: ["04:53"] },
],
candidateSplitHash: "varga.d9.巨蟹座/狮子座",
informationGain: 1.4,
sourceFeatures: [{ technique: "D9", calculationResultId: null }],
domain: "relationship",
year: null,
semanticKey: "varga.d9.巨蟹座/狮子座",
choiceKind: "varga_style" as const,
styleOptions: [
{ label: "相处里更在意照顾对方的感受", answerClass: "yes" as const, sign: "巨蟹座" },
{ label: "习惯带头,也不排斥站到台前", answerClass: "weak_yes" as const, sign: "狮子座" },
],
};
const planInput = {
evidence: ledger,
declinedTopics: ["relocation", "finance"].map(declined),
sessionOutcome: "discriminate_candidates" as const,
candidatesSeparated: false,
remainingLayers: LAYERS,
remainingSplitTimes: ["04:48", "05:07"] as const,
remainingCandidateCount: 4,
topCandidateTimes: [...TIMES],
contrastPacket: {
candidateSetVersion: "04:48-05:07",
vargaDifferences: [],
probes: [d9],
},
};
const auto = buildMethodFollowupPlan(planInput);
assert.notEqual(auto.next_followup?.choice_kind, "varga_style");
const opted = tieBreakPersonalityFollowup(planInput);
assert.equal(opted?.choice_kind, "varga_style");
assert.match(opted?.semantic_key ?? "", /varga\.d9/);
const d10: ConflictProbe = {
id: "probe:varga.d10.狮子座/处女座",
semantic_key: "varga.d10.狮子座/处女座",
candidate_split_hash: "04:48-05:07:varga.d10",
domain: "career",
year: 0,
question: "平时做事,你更接近下面哪一种?",
candidate_ids: [...TIMES],
expected_outcomes: [
{ answer_class: "yes", supports: ["04:53"], conflicts: ["05:06"] },
{ answer_class: "weak_yes", supports: ["05:06"], conflicts: ["04:53"] },
{ answer_class: "no", supports: [], conflicts: [] },
{ answer_class: "unsure", supports: [], conflicts: [] },
],
information_gain: 0.9,
source: "varga_contrast",
choice_kind: "varga_style",
style_options: [
{ label: "相处里更在意照顾对方的感受", answer_class: "yes", sign: "巨蟹座" },
{ label: "习惯带头,也不排斥站到台前", answer_class: "weak_yes", sign: "狮子座" },
],
};
const scored = applyProbeOutcome(
{ "04:48": 10, "04:53": 12, "05:06": 11, "05:07": 10 },
d10,
"yes",
);
assert.equal(scored.kind, "tie_break");
assert.equal(scored.deltas["04:53"], 1);
assert.equal(scored.eliminated_ids.length, 0);
const delivery = rangeDeliveryForSnapshot({
candidates: TIMES.map((time, index) => ({
candidateId: `00000000-0000-4000-8000-${String(index + 1).padStart(12, "0")}`,
time,
})),
representativeTime: "04:53",
credibleRange: ["04:48", "05:07"],
evidence: ledger,
declinedTopics: ["relocation", "finance"].map(declined),
window_scan: {
scanned: true,
d9_lagna_count: 2,
d10_lagna_count: 2,
d9_candidates_differ: true,
d10_candidates_differ: true,
d4_candidates_differ: true,
d12_candidates_differ: true,
d2_candidates_differ: true,
d11_candidates_differ: true,
},
});
assert.match(delivery.narrow_hint ?? "", /还能再收窄/);
assert.doesNotMatch(delivery.narrow_hint ?? "", SPLIT_ENDPOINT_PHRASE);
assert.equal(delivery.tie_break_available, true);
const incompleteScan = rangeDeliveryForSnapshot({
candidates: TIMES.map((time, index) => ({
candidateId: `00000000-0000-4000-8000-${String(index + 1).padStart(12, "0")}`,
time,
})),
representativeTime: "04:53",
credibleRange: ["04:48", "05:07"],
window_scan: {
d9_candidates_differ: true,
d10_candidates_differ: false,
},
});
assert.equal(incompleteScan.tie_break_available, true);
assert.equal(RECTIFICATION_USER_COPY.rangeDeliveryTieBreakEntry, "再答两道参考题微调排序");
});