fix(rectification): ask a ready discriminator before dated collect

Family coverage still continues dated collection when nothing can split leftover minutes, but an already-built choice card must not be displaced by spoken finance collect.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-05 10:34:24 +08:00
parent 309c74249e
commit cd704a92e7
8 changed files with 232 additions and 70 deletions
@@ -1974,8 +1974,98 @@ export function buildMethodFollowupPlan(input: {
const candidate = followupFromRanked(ranked);
return candidate.choice_frame ? candidate : null;
};
const firstRenderableYearlessFollowup = (): MethodFollowup | null => {
for (const ranked of yearlessDiscriminators) {
const domain = contrastFollowupDomain(
ranked.eventProbe?.domain ?? ranked.contrastProbe?.domain ?? null,
);
if (declined.has(domain)) continue;
const key = rankedDiscriminatorKey(ranked);
if (key && askedKeys.has(key)) continue;
const candidate = followupFromRanked(ranked);
if (candidate.choice_frame) return candidate;
}
return null;
};
const precisionStageFollowup = (): MethodFollowup | null => {
if (stage === "lagna_frame") {
return makeFollowup({
method_id: "dasha_events",
intent: "distinguish_candidates",
ask_theme: "dated_event",
domain: null,
kind_hint: null,
user_prompt_hint: ask(
"窗口里本命上升还可能落在两段。按 choice_frame.period 与探针问一件前事是否发生,用来筛这两段。不要问两套盘哪个更像。",
"本命上升 / Dasha",
),
source: "precision_stage",
});
}
if (stage === "d9_refine" && !relationshipCovered && !declined.has("relationship")) {
return makeFollowup({
method_id: "d9_relationship",
intent: "distinguish_candidates",
ask_theme: "relationship_style",
domain: "relationship",
kind_hint: "relationship_change",
user_prompt_hint: ask(
"关系盘仍会换升。按探针年份问感情这条线的前事是否发生,用来筛窗。不要问两套盘哪个更像。",
"D9",
"对照 D9 上升类型表只作校时方法。",
),
source: "precision_stage",
});
}
if (stage === "d10_refine" && !declined.has("career")) {
return makeFollowup({
method_id: "d10_career",
intent: "distinguish_candidates",
ask_theme: "career_style",
domain: "career",
kind_hint: "career_change",
user_prompt_hint: ask(
"事业盘仍会换升。按探针年份问事业这条线的前事是否发生,用来筛窗。不要问两套盘哪个更像。",
"D10",
"可用 D10 事业类型表作校时对照。",
),
source: "precision_stage",
});
}
if ((stage === "d4_refine" || stage === "theme_refine") && !declined.has("relocation")) {
return makeFollowup({
method_id: "d4_home",
intent: "distinguish_candidates",
ask_theme: "home_change",
domain: "relocation",
kind_hint: "home_change",
user_prompt_hint: ask(
"居所盘仍会换升。按探针年份问搬家或住处变化是否发生,用来筛窗。不要问两套盘哪个更像。",
"D4",
),
source: "precision_stage",
});
}
if (stage === "d5_refine" && !declined.has("education")) {
return makeFollowup({
method_id: "d5_education",
intent: "distinguish_candidates",
ask_theme: "education_style",
domain: "education",
kind_hint: "education_milestone",
user_prompt_hint: ask(
"成就盘或学业盘仍会换升。按探针年份问学业或考试变化是否发生,用来筛窗。不要问两套盘哪个更像。",
"D5 / D24",
),
source: "precision_stage",
});
}
return null;
};
let datedCollect: MethodFollowup | null = null;
let renderableYearless: MethodFollowup | null = null;
if (!next) {
const precisionCard = precisionStageFollowup();
if (!relationshipCovered && !declined.has("relationship")) {
next = sameDomainYearlessCard("relationship") ?? makeFollowup({
method_id: "d9_relationship",
@@ -2020,6 +2110,16 @@ export function buildMethodFollowupPlan(input: {
source: "method_coverage",
...collectionYearFields(familyCollect),
});
} else if ((renderableYearless = firstRenderableYearlessFollowup())) {
next = renderableYearless;
} else if (
// BUG-547: a renderable precision card beats dated collect, but only
// after the training gate is open. lagna_frame can borrow an unrelated
// probe into choice_frame while holdout is still short.
meetsAcceptanceEventQuality(input.evidence)
&& precisionCard?.choice_frame
) {
next = precisionCard;
} else if ((datedCollect = nextDatedCollectFollowup(input.evidence, declined))) {
next = makeFollowup(datedCollect);
} else if (!occupationCovered) {
@@ -2063,73 +2163,8 @@ export function buildMethodFollowupPlan(input: {
&& !declined.has("relocation"))
) {
next = makeFollowup(otherCollectFollowup(input.evidence));
} else if (stage === "lagna_frame") {
next = makeFollowup({
method_id: "dasha_events",
intent: "distinguish_candidates",
ask_theme: "dated_event",
domain: null,
kind_hint: null,
user_prompt_hint: ask(
"窗口里本命上升还可能落在两段。按 choice_frame.period 与探针问一件前事是否发生,用来筛这两段。不要问两套盘哪个更像。",
"本命上升 / Dasha",
),
source: "precision_stage",
});
} else if (stage === "d9_refine" && !relationshipCovered && !declined.has("relationship")) {
next = makeFollowup({
method_id: "d9_relationship",
intent: "distinguish_candidates",
ask_theme: "relationship_style",
domain: "relationship",
kind_hint: "relationship_change",
user_prompt_hint: ask(
"关系盘仍会换升。按探针年份问感情这条线的前事是否发生,用来筛窗。不要问两套盘哪个更像。",
"D9",
"对照 D9 上升类型表只作校时方法。",
),
source: "precision_stage",
});
} else if (stage === "d10_refine" && !declined.has("career")) {
next = makeFollowup({
method_id: "d10_career",
intent: "distinguish_candidates",
ask_theme: "career_style",
domain: "career",
kind_hint: "career_change",
user_prompt_hint: ask(
"事业盘仍会换升。按探针年份问事业这条线的前事是否发生,用来筛窗。不要问两套盘哪个更像。",
"D10",
"可用 D10 事业类型表作校时对照。",
),
source: "precision_stage",
});
} else if ((stage === "d4_refine" || stage === "theme_refine") && !declined.has("relocation")) {
next = makeFollowup({
method_id: "d4_home",
intent: "distinguish_candidates",
ask_theme: "home_change",
domain: "relocation",
kind_hint: "home_change",
user_prompt_hint: ask(
"居所盘仍会换升。按探针年份问搬家或住处变化是否发生,用来筛窗。不要问两套盘哪个更像。",
"D4",
),
source: "precision_stage",
});
} else if (stage === "d5_refine" && !declined.has("education")) {
next = makeFollowup({
method_id: "d5_education",
intent: "distinguish_candidates",
ask_theme: "education_style",
domain: "education",
kind_hint: "education_milestone",
user_prompt_hint: ask(
"成就盘或学业盘仍会换升。按探针年份问学业或考试变化是否发生,用来筛窗。不要问两套盘哪个更像。",
"D5 / D24",
),
source: "precision_stage",
});
} else if (precisionCard) {
next = precisionCard;
} else {
const d9 = input.observations?.find((item) => item.layer === "d9");
const d10 = input.observations?.find((item) => item.layer === "d10");