fix(rectification): ask unused style probes before every range card
Hold delivery whenever tie-break questions remain, not only on a one-point lead. Drop the event-anchor discard for varga_style; keep the two-sign split gate.
This commit is contained in:
+2
-2
@@ -1,8 +1,8 @@
|
||||
# 印度占星 Skill 更新日志
|
||||
|
||||
## 2026-09-14 — 分数接近时先问两道参考题,再给出范围卡
|
||||
## 2026-09-14 — 给范围卡之前先问两道风格参考题
|
||||
|
||||
生时校正里,前两名只差一分或打平时,会先连着问两道参考题(相处方式、做事风格),再给范围卡。点「再答两道参考题」后,题会出现在对话里。范围卡不再列出你已经说过没有或跳过的线。Skill 版本不变。
|
||||
生时校正里,带年月的题问完后、给出范围卡之前,会先连着问还没用过的风格参考题(相处方式、做事风格)。某条线答过「没有」不会把对应的风格题一起关掉。点「再答两道参考题」后,题会出现在对话里。范围卡不再列出你已经说过没有或跳过的线。Skill 版本不变。
|
||||
|
||||
|
||||
## 2026-09-14 — 并列不等于可以结束:还有题就继续问,区间没收窄不说可采用
|
||||
|
||||
+6
-6
@@ -10684,12 +10684,12 @@
|
||||
- 首次发现:2026-09-14
|
||||
- 最近更新:2026-09-14
|
||||
- 影响面:`decideRectification` `heldForTieBreak`、`persistNextInterviewAfterChoice` / `persistNextInterviewIfIdle`、范围卡按钮
|
||||
- 用户现象:带年月题和定向补事问完后,前两名 16/16 并列,直接出范围卡让人在 26%/26%/20% 里选;参考题要自己点按钮。
|
||||
- 触发条件:前两名 posterior 分差 ≤ 1,D9/D10 参考题仍可用且未用过。
|
||||
- 根因:性格题被设计成出卡后的可选入口,而不是出卡前的必经步骤。
|
||||
- 修复:产品拍板方案 A(2026-09-14)。分差 ≤ 1 且参考题可用时先连问最多两道 `varga_style`(仍只 ±1、不淘汰),两道用完再出卡。仍平时卡上写「这两分钟按现有信息分不开,参考题已经用过。」用过之后不再显示入口按钮。
|
||||
- 验证:`frontend/tests/rectification-tiebreak-before-card-20260914.test.ts`:16/16 不交付且下一问是 `varga_style`;两道用完才交付且按钮关闭;lead=2 不插参考题。
|
||||
- 防复发:不得把性格题改成淘汰候选。自动进入参考题时不得再写第二条交付消息。
|
||||
- 用户现象:带年月题和定向补事问完后直接出范围卡;「你平时做事」「感情里你倾向」没被问。答「没结过婚」后连感情风格题也不再出现。
|
||||
- 触发条件:带年月池空、即将交付;参考题仍可用且未用过。或该领域没有带年月事件。
|
||||
- 根因:性格题被设计成出卡后的可选入口。另:`varga_style` 被错误要求该领域账本有带年月事件作锚,答「没有」会连带关掉风格题。
|
||||
- 修复:产品拍板放宽版 A(2026-09-14)。出卡前只要参考题可用且没用过就先连问最多两道 `varga_style`(不看分差;仍只 ±1、不淘汰)。删除风格题的事件锚点丢弃;分盘上升星座少于两种时该题仍不出现。仍平时卡上写「这两分钟按现有信息分不开,参考题已经用过。」用过之后不再显示入口按钮。
|
||||
- 验证:`frontend/tests/rectification-tiebreak-before-card-20260914.test.ts`:16/16 与 lead=2 都先问参考题;无事件锚点时风格题仍出现;两道用完才交付。
|
||||
- 防复发:不得把性格题改成淘汰候选。不得为凑满两道而放宽「该分盘至少两种上升星座」。自动进入参考题时不得再写第二条交付消息。
|
||||
- 相关记录:BUG-663、BUG-667、BUG-629、BUG-597
|
||||
- 复发自:BUG-663(入口做成出卡后可选)
|
||||
- 修复版本:待发布
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## 完成
|
||||
|
||||
- T1:`requestTieBreak` 成功路径用 `applySnapshotTurnsToMessages` 把新 turn 并进对话。缺口里未答焦点优先于「等读者点卡」。`refetchQuestion` 与 repair 失败回拉同样合并(服务端可能已写 turn)。
|
||||
- T2:`pendingTieBreak && lead ≤ 1` 时 `heldForTieBreak`,先持久化 D9/D10 `varga_style`(沿用 `tie_break_round`),用完再交付。仍平时卡上写「参考题已经用过」,按钮随 `tie_break_available` 收起。
|
||||
- T2:出卡前只要 `pendingTieBreak`(不看分差)就 `heldForTieBreak`,先持久化 D9/D10 `varga_style`(沿用 `tie_break_round`),用完再交付。删除 `varga_style` 的 `unanchored_varga_style` 锚点丢弃。仍平时卡上写「参考题已经用过」,按钮随 `tie_break_available` 收起。
|
||||
- T3:`rangeNarrowHint` 传入真实拒答;空池写「能问的都问完了」。
|
||||
- T4:`tieBreakGateInput` 供 GET 与 POST 共用。GET 本轮仍不另载 `birthDate`(与原先一致);POST 继续从 compute 读取。
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
## BUG-686
|
||||
|
||||
- [ ] 前两名分数打平或只差一分、参考题还没用过时:先连着出两道参考题(相处方式、做事风格),再出范围卡。
|
||||
- [ ] 两道答完后才出范围卡;卡上不再有「再答两道参考题微调排序」。
|
||||
- [ ] 带年月题问完、参考题还没用过时:先连着出风格参考题(相处方式、做事风格),再出范围卡。不平局的案子也一样。
|
||||
- [ ] 某条线答过「没有」后,对应分盘如果候选上升星座仍有两种以上,风格题仍要出现。
|
||||
- [ ] 某分盘上候选上升星座只有一种(例如本案 D9 全是天蝎)时,该题不出现属正常。
|
||||
- [ ] 两道答完(或没有可问的)后才出范围卡;卡上不再有「再答两道参考题微调排序」。
|
||||
- [ ] 若仍分不开,卡上有一句「这两分钟按现有信息分不开,参考题已经用过。」
|
||||
- [ ] 同一轮不会出现两条交付结论。
|
||||
|
||||
|
||||
+1
-1
@@ -234,7 +234,7 @@ The birth-time rectification session is the consultation transcript plus a house
|
||||
| `question-gap`, delivered | no current question, or the current question is a dead choice card, and the session already delivered a range (`completed_with_range` / `provisional_range` / adopt outcomes, or `tied_first`); range card or range line plus an exit note. Never “没有拿到下一个问题。” | enabled |
|
||||
| `verified_idle` | one closing line `postAdoptVerifyDone` under the still-visible range card (same assistant column); no spinner, no reload | enabled |
|
||||
| `choice-pending` | the answered card (`data-selected` fill, a top row “正在记录…”) and the same live row from “正在记录本次选择…” through the follow-up turn | enabled (typing queues), stop visible |
|
||||
| `candidates` | one range-delivery card titled “目前范围 …(对照了 N 件经历)”, a caption under the title that only names still-open lines (or “能问的都问完了”); optional “再答两道参考题微调排序” only when unused D9/D10 remain; if those were already asked and the top two are still within one point, a line “这两分钟按现有信息分不开,参考题已经用过。”; then up to three compare columns (highest posterior first; “更像这个” adopts); a closed “查看验证报告” fold. A close lead asks the two personality cards before this card appears. | enabled |
|
||||
| `candidates` | one range-delivery card titled “目前范围 …(对照了 N 件经历)”, a caption under the title that only names still-open lines (or “能问的都问完了”); optional “再答两道参考题微调排序” only when unused D9/D10 remain; if those were already asked and the top two are still within one point, a line “这两分钟按现有信息分不开,参考题已经用过。”; then up to three compare columns (highest posterior first; “更像这个” adopts); a closed “查看验证报告” fold. Unused style questions are asked before this card appears, whether or not the top two are tied. | enabled |
|
||||
| `adopting` | “正在采用 HH:MM…” through the follow-up turn | enabled (typing queues), stop visible |
|
||||
| `confirmed` | “已确认校正时间:HH:MM” | enabled |
|
||||
| `readonly` | “该校正已结束,只能查看历史。” and “再次校正” | disabled |
|
||||
|
||||
@@ -411,6 +411,9 @@ export function decideRectification(input: DecideRectificationInput): Rectificat
|
||||
return deliverRange(input, separation, holdout, range, "exhausted", rangeDeliveryCapability, stopClass.reason);
|
||||
}
|
||||
if (rangeDeliveryCapability.canAdopt && input.methodCoverageAll) {
|
||||
if (shouldHoldForTieBreak(input, separation)) {
|
||||
return holdForTieBreak(separation, holdout, range, rangeDeliveryCapability, stopReason);
|
||||
}
|
||||
return finish("adopt_representative", {
|
||||
input,
|
||||
separation,
|
||||
@@ -427,6 +430,9 @@ export function decideRectification(input: DecideRectificationInput): Rectificat
|
||||
return deliverRange(input, separation, holdout, range, "exhausted", rangeDeliveryCapability, stopClass.reason);
|
||||
}
|
||||
if (input.accepted) {
|
||||
if (shouldHoldForTieBreak(input, separation)) {
|
||||
return holdForTieBreak(separation, holdout, range, rangeDeliveryCapability, stopReason);
|
||||
}
|
||||
return finish(confirmationAllowed ? "awaiting_confirmation" : "adopt_representative", {
|
||||
input,
|
||||
separation,
|
||||
@@ -440,6 +446,9 @@ export function decideRectification(input: DecideRectificationInput): Rectificat
|
||||
return collect(separation, holdout, range, null, capability, stopReason);
|
||||
}
|
||||
if (confirmationAllowed) {
|
||||
if (shouldHoldForTieBreak(input, separation)) {
|
||||
return holdForTieBreak(separation, holdout, range, rangeDeliveryCapability, stopReason);
|
||||
}
|
||||
return finish("awaiting_confirmation", {
|
||||
input,
|
||||
separation,
|
||||
@@ -464,6 +473,9 @@ export function decideRectification(input: DecideRectificationInput): Rectificat
|
||||
}
|
||||
return offerRangeWithoutAdopt(separation, holdout, range, rangeDeliveryCapability);
|
||||
}
|
||||
if (shouldHoldForTieBreak(input, separation)) {
|
||||
return holdForTieBreak(separation, holdout, range, rangeDeliveryCapability, stopReason);
|
||||
}
|
||||
return finish("adopt_representative", {
|
||||
input,
|
||||
separation,
|
||||
@@ -583,8 +595,7 @@ export function shouldHoldForTieBreak(
|
||||
): boolean {
|
||||
if (kind === "user_stopped" || input.userStopped === true) return false;
|
||||
if (input.pendingTieBreak !== true) return false;
|
||||
if (separation.ranked.length < 2) return false;
|
||||
return separation.lead <= 1;
|
||||
return separation.ranked.length >= 2;
|
||||
}
|
||||
|
||||
function holdForTieBreak(
|
||||
|
||||
@@ -1259,14 +1259,6 @@ function rankRenderableDiscriminators(input: {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (probe.choiceKind === "varga_style") {
|
||||
const domain = contrastFollowupDomain(probe.domain);
|
||||
const anchor = datedLedgerAnchor(input.evidence, domain);
|
||||
if (!anchor) {
|
||||
dropped.push(droppedFromProbe(probe.semanticKey, probe.informationGain, "unanchored_varga_style"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!isStructuredDiscriminator(probe) && probe.domain && provided.has(canonicalCollectDomain(probe.domain))) {
|
||||
const year = probe.year ?? 0;
|
||||
if (year <= 0) continue;
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
existenceProbeAsked,
|
||||
remainingReverseVerifyProbes,
|
||||
sameYearProbeAsked,
|
||||
tieBreakPersonalityFollowup,
|
||||
type MethodFollowupEvidence,
|
||||
} from "../src/lib/rectification-agentic/v9/method-followup.ts";
|
||||
import type { DiscriminatingEventProbe } from "../src/lib/rectification-agentic/v9/refinement-packet.ts";
|
||||
@@ -155,11 +156,21 @@ test("unanchored D10 varga_style cards are dropped; anchored cards mention the l
|
||||
contrastPacket: packet,
|
||||
candidatesSeparated: false,
|
||||
});
|
||||
// 原值: dropped_probes 含 unanchored_varga_style,无事业账本就不出 D10 风格题
|
||||
// 新值: 风格题不再因缺事件锚点被丢;未点参考题入口时仍 deferred,不挡交付
|
||||
// 原因: BUG-686 D1c 风格题只看分盘上升星座,不依赖该领域事件
|
||||
assert.equal(
|
||||
unanchored.dropped_probes.some((item) => item.reason === "unanchored_varga_style"),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
assert.notEqual(unanchored.next_followup?.semantic_key, D10_STYLE.semanticKey);
|
||||
const requested = tieBreakPersonalityFollowup({
|
||||
evidence: baseEvidence,
|
||||
contrastPacket: packet,
|
||||
candidatesSeparated: false,
|
||||
});
|
||||
assert.equal(requested?.choice_kind, "varga_style");
|
||||
assert.equal(requested?.semantic_key, D10_STYLE.semanticKey);
|
||||
|
||||
const anchored = buildMethodFollowupPlan({
|
||||
evidence: [...baseEvidence, dated("e-career", "career", "career_entry", "2018-07-01")],
|
||||
|
||||
@@ -230,7 +230,7 @@ test("the same tie delivers after both personality questions are used", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("a two-point lead does not insert personality questions before delivery", () => {
|
||||
test("a two-point lead still asks personality questions before delivery", () => {
|
||||
const decision = tiedDecision({
|
||||
pendingTieBreak: true,
|
||||
candidateScores: [
|
||||
@@ -239,8 +239,25 @@ test("a two-point lead does not insert personality questions before delivery", (
|
||||
{ time: "05:06", score: 13 },
|
||||
],
|
||||
});
|
||||
assert.equal(decision.heldForTieBreak ?? false, false);
|
||||
assert.equal(sessionOutcomeAllowsDelivery(decision.sessionOutcome), true);
|
||||
assert.equal(decision.heldForTieBreak, true);
|
||||
assert.equal(sessionOutcomeAllowsDelivery(decision.sessionOutcome), false);
|
||||
assert.equal(tieBreakPersonalityFollowup(planInput())?.choice_kind, "varga_style");
|
||||
});
|
||||
|
||||
test("a layer with only one rising sign does not occupy the style slot", () => {
|
||||
const onlyD10 = {
|
||||
...planInput(),
|
||||
remainingLayers: ["d10"],
|
||||
contrastPacket: {
|
||||
candidateSetVersion: "04:48-05:07",
|
||||
vargaDifferences: [],
|
||||
probes: [D10],
|
||||
},
|
||||
};
|
||||
const followup = tieBreakPersonalityFollowup(onlyD10);
|
||||
assert.equal(followup?.choice_kind, "varga_style");
|
||||
assert.match(followup?.semantic_key ?? "", /varga\.d10/);
|
||||
assert.doesNotMatch(followup?.semantic_key ?? "", /varga\.d9/);
|
||||
});
|
||||
|
||||
test("holding for a tie-break does not mark a delivery turn", () => {
|
||||
|
||||
Reference in New Issue
Block a user