feat(rectification): 出卡加精度门槛,补经历改成系统点名
Independent Staging Quality Gate / validate (push) Failing after 6m28s
Independent Staging Quality Gate / publish (push) Skipped

宽度超过 10 分钟或头名并列时不再出交付卡,改为按大运边界逐条问、
用类型芯片和年/月选择器录入。跳过的线换问法再问一次;答「这类事
都没有过」的不再问。用户说「没有了」仍立刻给目前范围。Skill 10.0.27。

BUG-740~743
This commit is contained in:
jesse-ux
2026-09-16 18:35:27 +08:00
parent 317e9f1886
commit cfb41daf3d
75 changed files with 4763 additions and 383 deletions
@@ -119,30 +119,6 @@ export const USER_COLLECT_QUESTION_RETRY: Readonly<Record<string, string>> = {
health_pressure: "身体或压力这边再问一次:哪年生病、受伤,或特别难熬?",
};
/** Exact-day examples asked first after the seven targeted-collect lines close. */
export const RANGE_DELIVERY_OPEN_COLLECT_DAY_EXAMPLES = [
"登记结婚那天",
"入职第一天",
"手术那天",
"孩子出生那天",
"拿到录取通知那天",
] as const;
/** Year-month examples outside the seven targeted-collect lines. */
export const RANGE_DELIVERY_OPEN_COLLECT_EXAMPLES = [
"换专业",
"出国",
"打官司",
"创业",
"重病",
"亲人离世",
] as const;
/** Stable invite stem after the count-dependent subject. */
export const RANGE_DELIVERY_COLLECT_CLOSED_INVITE = `你要是还记得确切哪一天的事,不限领域,说出来我接着算——${RANGE_DELIVERY_OPEN_COLLECT_DAY_EXAMPLES.join("、")}都成。记不得哪一天的,有年月也行,比如${RANGE_DELIVERY_OPEN_COLLECT_EXAMPLES.join("、")}。`;
const CLOSED_INVITE_HEAD = /(?:这两分钟|这几个候选)?按现有信息分不开。$/u;
/** Count=2 → 「这两分钟」; >2 → 「这几个候选」; unknown / 0 / 1 → omit. */
export function rangeDeliveryCollectClosedSubject(candidateCount?: number): string | null {
if (candidateCount === 2) return "这两分钟";
@@ -154,16 +130,7 @@ export function rangeDeliveryCollectClosedSubject(candidateCount?: number): stri
export function rangeDeliveryCollectClosed(candidateCount?: number): string {
const subject = rangeDeliveryCollectClosedSubject(candidateCount);
const head = subject ? `${subject}按现有信息分不开。` : "按现有信息分不开。";
return `${head}${RANGE_DELIVERY_COLLECT_CLOSED_INVITE}`;
}
export function rangeDeliveryClosedInviteFromHint(hint: string | null | undefined): string | null {
if (!hint) return null;
const at = hint.indexOf(RANGE_DELIVERY_COLLECT_CLOSED_INVITE);
if (at < 0) return null;
const head = hint.slice(0, at).match(CLOSED_INVITE_HEAD);
return `${head?.[0] ?? ""}${RANGE_DELIVERY_COLLECT_CLOSED_INVITE}`;
return subject ? `${subject}按现有信息分不开。` : "按现有信息分不开。";
}
export const RECTIFICATION_USER_COPY = {
@@ -179,8 +146,8 @@ export const RECTIFICATION_USER_COPY = {
hostNarrationFallback: "我按现有材料继续往下收。",
collectHandoff: "接下来我们继续。",
evidenceNotRecorded: "这件我还没记上。请再说一次大概年月和发生的事。",
collectDeclinedAck: "记下了,这方面先跳过。",
collectSkippedAck: "记下了,这题先放着。",
collectDeclinedAck: "记下了,这条按没有发生过记。",
collectSkippedAck: "记下了,这题先放着,后面换个问法再问一次。",
firstDatedCollectInvite: FIRST_DATED_COLLECT_INVITE,
uncertaintyStop: "前面几道题你多半选了\"说不好\",再问下去也分不开,先停在这里。",
tiedFirstStop: "几个候选打成平手,问题已经分不开它们。",
@@ -220,26 +187,6 @@ export function rangeDeliveryTopTwoTied(
<= RANGE_DELIVERY_TIE_PERCENT;
}
export function rangeDeliveryCaptionWithoutClosedInvite(
hint: string | null | undefined,
): string | null {
if (!hint) return null;
const invite = rangeDeliveryClosedInviteFromHint(hint);
if (!invite) return hint;
const stripped = hint.replaceAll(invite, "").replace(/[。.]\s*$/u, "").trim();
return stripped.length > 0 ? stripped : null;
}
export function rangeDeliveryShowsOpenCollectInvite(input: {
hint: string | null | undefined;
columns: readonly { probability_percent: number }[];
}): boolean {
return Boolean(
rangeDeliveryClosedInviteFromHint(input.hint)
&& rangeDeliveryTopTwoTied(input.columns),
);
}
export const RANGE_DELIVERY_DOMAIN_LABEL: Readonly<Record<string, string>> = {
education: "学业",
career: "事业",