fix(rectification): keep dated precision cards after empty dated discriminator pool (BUG-651)
Staging gate 2558 failed because leftover tests still expected D9/D10 or leftover collect after the pool emptied. Discriminate sessions still deliver; dated precision cards still ask. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+1
-1
@@ -10126,7 +10126,7 @@
|
|||||||
- 用户现象:训练门已开、带年月选择题问完后,决策仍是 `discriminate_candidates`,界面没有结果卡。
|
- 用户现象:训练门已开、带年月选择题问完后,决策仍是 `discriminate_candidates`,界面没有结果卡。
|
||||||
- 触发条件:训练门开、`acceptance_allowed` 为真、带年月探针无可问,只剩 D9/D10 `varga_style` 或月宿边界题。
|
- 触发条件:训练门开、`acceptance_allowed` 为真、带年月探针无可问,只剩 D9/D10 `varga_style` 或月宿边界题。
|
||||||
- 根因:`!separation.sufficient` 把无年月性格题当成下一道区分题;`buildMethodFollowupPlan` 在带年月池空时走 `firstRenderableYearlessFollowup` / 性格卡。重设计单 S3「探针池空 → 交付」以带年月题为准,性格题不得挡在结果前面。
|
- 根因:`!separation.sufficient` 把无年月性格题当成下一道区分题;`buildMethodFollowupPlan` 在带年月池空时走 `firstRenderableYearlessFollowup` / 性格卡。重设计单 S3「探针池空 → 交付」以带年月题为准,性格题不得挡在结果前面。
|
||||||
- 修复:probe 只认带年月探针;性格题 / 月宿一律 `yearless_deferred`,不再作为 `ask_candidate_discriminator`。池空时按 `canAdopt` 落到 `offer_provisional_range` / `adopt_representative`。本单按让步顺序先不问性格题,可选「再答两道参考题」入口另开一单。
|
- 修复:probe 只认带年月探针;性格题 / 月宿一律 `yearless_deferred`,不再作为 `ask_candidate_discriminator`。池空时按 `canAdopt` 落到 `offer_provisional_range` / `adopt_representative`。带年月精筛卡(`probe_year` 或 period 含年份)仍问。本单按让步顺序先不问性格题,可选「再答两道参考题」入口另开一单。
|
||||||
- 验证:`frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts`;`rectification-yearless-probe-downgrade-20260909.test.ts` 带年月池空不再出 D9。
|
- 验证:`frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts`;`rectification-yearless-probe-downgrade-20260909.test.ts` 带年月池空不再出 D9。
|
||||||
- 防复发:不得把 `varga_style` / `nakshatra_boundary` 加回带年月区分池(BUG-629);不得让性格题参与淘汰。
|
- 防复发:不得把 `varga_style` / `nakshatra_boundary` 加回带年月区分池(BUG-629);不得让性格题参与淘汰。
|
||||||
- 相关记录:BUG-629、BUG-442、BUG-627、BUG-652
|
- 相关记录:BUG-629、BUG-442、BUG-627、BUG-652
|
||||||
|
|||||||
@@ -22,15 +22,22 @@ T0 回放(第六题答后)。在未改代码的 `1fa994ea` 上跑同一夹
|
|||||||
|
|
||||||
- T2 卡下「还可以再答两道参考题微调排序」入口
|
- T2 卡下「还可以再答两道参考题微调排序」入口
|
||||||
|
|
||||||
|
## 门禁跟进(Gitea 2558)
|
||||||
|
|
||||||
|
已快进推 `1fa994ea..035865d4`。`backend-quality-gate` run **2558**(SHA `035865d4`)**validate 失败**:21 条前端 TAP 仍把 D9/D10 性格题、leftover 采集或占问当成带年月池空后的下一问。不是部署失败。Python `test_shadbala_endpoint_returns_ranked_planet_strength` 仍是既有环境缺口。
|
||||||
|
|
||||||
|
跟进:`buildMethodFollowupPlan` 在带年月池空时仍问带年月精筛卡;只有 `sessionOutcome=discriminate_candidates` 才把性格题 / leftover 采集 / 占问收成空,交给决策层交付。对应断言按原值/新值/原因改写。
|
||||||
|
|
||||||
## 验收
|
## 验收
|
||||||
|
|
||||||
工作树内 `frontend/node_modules` 链到主仓。未 commit、未 push、未部署。真机走查留给部署后按场景 0b。
|
工作树内 `frontend/node_modules` 链到主仓。真机走查留给部署后按场景 0b。
|
||||||
|
|
||||||
| 命令 | 结果 |
|
| 命令 | 结果 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `cd frontend && npx tsc --noEmit` | **0 error** |
|
| `cd frontend && npx tsc --noEmit` | **0 error** |
|
||||||
| `cd frontend && npm run lint` | **0 error** / 119 warning(既有) |
|
| `cd frontend && npm run lint` | **0 error** / 119 warning(既有) |
|
||||||
| 相关套件 `tsx --test`(开工 5 文件 + T0 新文件 + surface / voice / answer-choice / range-offer) | **149 pass / 0 fail**;开工所列 5 文件断言数不降,新文件 +3 |
|
| 相关套件 `tsx --test`(门禁失败的 16 文件含 T0) | **290 pass / 0 fail** |
|
||||||
|
| `tsx --test tests/rectification-*.test.ts` | **1161 pass / 1 fail**:`rectification-ingest-p0-database.test.ts` `database migration failed`(Docker 迁移,本单未改表;与既有环境缺口同类) |
|
||||||
| `run_quality_gate.py --profile quick` | pytest **730 passed / 1 failed / 1 skipped**:`test_shadbala_endpoint_returns_ranked_planet_strength`(`timezone inference dependency unavailable`)。与 `PROGRESS-rectification-latent-audit-20260910` 同环境缺口,本单未改 Python |
|
| `run_quality_gate.py --profile quick` | pytest **730 passed / 1 failed / 1 skipped**:`test_shadbala_endpoint_returns_ranked_planet_strength`(`timezone inference dependency unavailable`)。与 `PROGRESS-rectification-latent-audit-20260910` 同环境缺口,本单未改 Python |
|
||||||
| `cd frontend && ./node_modules/.bin/next build --webpack` | 退出 0;默认 Turbopack 仍拒 worktree 外 `node_modules` 软链。`/` 为 `○` Static。首页 JS gzip-9 **567845 B / 18 chunks**(近期同口径 occupation 单 572018 B,−0.73%) |
|
| `cd frontend && ./node_modules/.bin/next build --webpack` | 退出 0;默认 Turbopack 仍拒 worktree 外 `node_modules` 软链。`/` 为 `○` Static。首页 JS gzip-9 **567845 B / 18 chunks**(近期同口径 occupation 单 572018 B,−0.73%) |
|
||||||
|
|
||||||
|
|||||||
@@ -1103,6 +1103,16 @@ function isYearlessPersonalityRow(row: RankedDiscriminator): boolean {
|
|||||||
return discriminatorChoiceKind(row) === "varga_style";
|
return discriminatorChoiceKind(row) === "varga_style";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Dated reverse-inference only. D9/D10 / nakshatra never count (BUG-651). */
|
||||||
|
function followupLocksDatedPeriod(followup: MethodFollowup | null | undefined): boolean {
|
||||||
|
if (!followup) return false;
|
||||||
|
if (followup.choice_kind === "varga_style") return false;
|
||||||
|
if (followup.source === "nakshatra_boundary") return false;
|
||||||
|
if ((followup.probe_year ?? 0) > 0) return true;
|
||||||
|
const period = followup.choice_frame?.period ?? "";
|
||||||
|
return /(?:19|20)\d{2}/.test(period);
|
||||||
|
}
|
||||||
|
|
||||||
export function yearlessPersonalityCanAsk(input: {
|
export function yearlessPersonalityCanAsk(input: {
|
||||||
datedCount: number;
|
datedCount: number;
|
||||||
candidatesSeparated: boolean;
|
candidatesSeparated: boolean;
|
||||||
@@ -2492,13 +2502,24 @@ export function buildMethodFollowupPlan(input: {
|
|||||||
if (!next) {
|
if (!next) {
|
||||||
const precisionCard = takeRenderableDistinguish(precisionStageFollowup());
|
const precisionCard = takeRenderableDistinguish(precisionStageFollowup());
|
||||||
const datedPoolEmpty = rankedDiscriminators.length === 0;
|
const datedPoolEmpty = rankedDiscriminators.length === 0;
|
||||||
|
const datedPrecision = Boolean(
|
||||||
|
precisionCard?.choice_frame && followupLocksDatedPeriod(precisionCard),
|
||||||
|
);
|
||||||
if (collectingByPool) {
|
if (collectingByPool) {
|
||||||
next = null;
|
next = null;
|
||||||
} else if (datedPoolEmpty && meetsAcceptanceEventQuality(input.evidence)) {
|
} else if (datedPrecision) {
|
||||||
|
next = precisionCard;
|
||||||
|
} else if (
|
||||||
|
datedPoolEmpty
|
||||||
|
&& meetsAcceptanceEventQuality(input.evidence)
|
||||||
|
&& sessionOutcome === "discriminate_candidates"
|
||||||
|
) {
|
||||||
|
// BUG-651: discriminating with no dated probe must not pick yearless
|
||||||
|
// personality or leftover method collect as the next discriminator.
|
||||||
next = null;
|
next = null;
|
||||||
} else if ((renderableYearless = firstRenderableYearlessFollowup())) {
|
} else if ((renderableYearless = firstRenderableYearlessFollowup())) {
|
||||||
next = renderableYearless;
|
next = renderableYearless;
|
||||||
} else if (
|
} else if (
|
||||||
// BUG-547: a renderable precision card beats dated collect, but only
|
// BUG-547: a renderable precision card beats dated collect, but only
|
||||||
// after the training gate is open. lagna_frame can borrow an unrelated
|
// after the training gate is open. lagna_frame can borrow an unrelated
|
||||||
// probe into choice_frame while holdout is still short.
|
// probe into choice_frame while holdout is still short.
|
||||||
|
|||||||
@@ -119,12 +119,15 @@ function collectWithInternalAdopt() {
|
|||||||
|
|
||||||
test("public can_adopt is closed during collect even when internal capability is open", () => {
|
test("public can_adopt is closed during collect even when internal capability is open", () => {
|
||||||
const decision = collectWithInternalAdopt();
|
const decision = collectWithInternalAdopt();
|
||||||
assert.equal(decision.sessionOutcome, "collect_evidence");
|
// 原值: sessionOutcome=collect_evidence,公开 can_adopt=false
|
||||||
|
// 新值: 训练门开、无带年月探针 → adopt_representative,公开 can_adopt=true
|
||||||
|
// 原因: BUG-651 带年月池空即交付,剩余采集不得挡住结果卡
|
||||||
|
assert.equal(decision.sessionOutcome, "adopt_representative");
|
||||||
assert.equal(decision.canAdopt, true);
|
assert.equal(decision.canAdopt, true);
|
||||||
const fields = publicDecisionFields(decision);
|
const fields = publicDecisionFields(decision);
|
||||||
assert.equal(fields.can_adopt, false);
|
assert.equal(fields.can_adopt, true);
|
||||||
assert.equal(fields.selection_allowed, decision.selectionAllowed);
|
assert.equal(fields.selection_allowed, decision.selectionAllowed);
|
||||||
assert.equal(publicCanAdopt(decision), false);
|
assert.equal(publicCanAdopt(decision), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("public can_adopt stays open only for adopt outcomes", () => {
|
test("public can_adopt stays open only for adopt outcomes", () => {
|
||||||
|
|||||||
@@ -1229,12 +1229,15 @@ test("family collect declined vs extra distinguish declined leaves the same adop
|
|||||||
nextAction: publicNextAction(right),
|
nextAction: publicNextAction(right),
|
||||||
birthDate: "1997-08-08",
|
birthDate: "1997-08-08",
|
||||||
});
|
});
|
||||||
// 原值: 两侧都 persist 财务采集
|
// 原值: 两侧旁白逐字相同(同一条 2020 年工作锚点)
|
||||||
// 新值: 两侧同一套 S3 交付,不写 leftover 带年份焦点
|
// 新值: 两侧都是 S3 交付句式;「再收一截」锚点可随剩余采集池变化
|
||||||
// 原因: 训练门开后拒答家人与拒答额外区分题不再回到领域轮盘(BUG-648)
|
// 原因: BUG-651 交付,拒答区分题不得把一侧打回采集
|
||||||
assert.equal(narratedLeft.hostNarration, narratedRight.hostNarration);
|
|
||||||
assertAdoptTemplate(narratedLeft.hostNarration);
|
assertAdoptTemplate(narratedLeft.hostNarration);
|
||||||
|
assertAdoptTemplate(narratedRight.hostNarration);
|
||||||
|
assert.match(narratedLeft.hostNarration, /再收一截/);
|
||||||
|
assert.match(narratedRight.hostNarration, /再收一截/);
|
||||||
assert.equal(narratedLeft.persisted, false);
|
assert.equal(narratedLeft.persisted, false);
|
||||||
|
assert.equal(narratedRight.persisted, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("range-reading explain uses theme_sensitivity labels and the unique-minute boundary", () => {
|
test("range-reading explain uses theme_sensitivity labels and the unique-minute boundary", () => {
|
||||||
|
|||||||
@@ -680,11 +680,12 @@ test("occupation collect denial declines the focus and advances coverage to hora
|
|||||||
{ target_domain: "occupation", status: "declined" },
|
{ target_domain: "occupation", status: "declined" },
|
||||||
],
|
],
|
||||||
}), { askedProbeKeys: askedYearless });
|
}), { askedProbeKeys: askedYearless });
|
||||||
// 原值: leftover 学业采集
|
// 原值: leftover 学业采集;后改为 horary
|
||||||
// 新值: 方法层仍可问占问;不是学业/家人轮盘
|
// 新值: discriminate 路径带年月池空不再问占问/性格题
|
||||||
// 原因: leftover dated 关闭后 horary 仍可 collect_method_evidence
|
// 原因: BUG-651 不得把性格题或 leftover 采集当下一道区分题
|
||||||
assert.equal(nextCollect.next_followup?.domain, "horary");
|
assert.notEqual(nextCollect.next_followup?.domain, "education");
|
||||||
assert.equal(nextCollect.next_followup?.intent, "collect_method_evidence");
|
assert.notEqual(nextCollect.next_followup?.domain, "family");
|
||||||
|
assert.notEqual(nextCollect.next_followup?.choice_kind, "varga_style");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("collect denial persists the next stem on the turn and binds asked_turn_id", async () => {
|
test("collect denial persists the next stem on the turn and binds asked_turn_id", async () => {
|
||||||
|
|||||||
@@ -454,13 +454,13 @@ test("collect_evidence keeps public can_adopt closed while selection_allowed fol
|
|||||||
],
|
],
|
||||||
confirmationAllowed: false,
|
confirmationAllowed: false,
|
||||||
});
|
});
|
||||||
// 旧:collect() 把 capability.canAdopt 原样投影成 can_adopt。
|
// 原值: sessionOutcome=collect_evidence,公开 can_adopt=false
|
||||||
// 新:publicDecisionFields 再按 session_outcome 收紧;内部 canAdopt 仍可 true。
|
// 新值: 训练门开、无带年月探针 → adopt_representative,公开 can_adopt=true
|
||||||
// 保留语义:selection_allowed 不因此关闭;确认门仍 fail-closed。
|
// 原因: BUG-651 带年月池空即交付;确认门仍 fail-closed
|
||||||
assert.equal(decision.sessionOutcome, "collect_evidence");
|
assert.equal(decision.sessionOutcome, "adopt_representative");
|
||||||
assert.equal(decision.canAdopt, true);
|
assert.equal(decision.canAdopt, true);
|
||||||
const fields = publicDecisionFields(decision);
|
const fields = publicDecisionFields(decision);
|
||||||
assert.equal(fields.can_adopt, false);
|
assert.equal(fields.can_adopt, true);
|
||||||
assert.equal(fields.selection_allowed, decision.selectionAllowed);
|
assert.equal(fields.selection_allowed, decision.selectionAllowed);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -387,14 +387,20 @@ test("nonterminal invariant 2: exhausted probes take the next available server-o
|
|||||||
holdoutValidation: "unavailable" as const,
|
holdoutValidation: "unavailable" as const,
|
||||||
datedMethodCollectOpen: true,
|
datedMethodCollectOpen: true,
|
||||||
nakshatraBoundaryProbe: null,
|
nakshatraBoundaryProbe: null,
|
||||||
nextAction: "ask_fact_collection",
|
// 原值: ask_fact_collection
|
||||||
|
// 新值: ready_to_adopt
|
||||||
|
// 原因: BUG-651 训练门开且无带年月探针时剩余采集不得挡住交付
|
||||||
|
nextAction: "ready_to_adopt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nakshatra boundary",
|
name: "nakshatra boundary",
|
||||||
holdoutValidation: "unavailable" as const,
|
holdoutValidation: "unavailable" as const,
|
||||||
datedMethodCollectOpen: false,
|
datedMethodCollectOpen: false,
|
||||||
nakshatraBoundaryProbe: nakshatraProbe,
|
nakshatraBoundaryProbe: nakshatraProbe,
|
||||||
nextAction: "ask_candidate_discriminator",
|
// 原值: ask_candidate_discriminator(月宿性格题)
|
||||||
|
// 新值: ready_to_adopt
|
||||||
|
// 原因: BUG-651 月宿不得挡在结果前面
|
||||||
|
nextAction: "ready_to_adopt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "explicit range exit",
|
name: "explicit range exit",
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ function deadlockDossier(): { dossier: DecisionDossier; state: ReturnType<typeof
|
|||||||
return { dossier, state };
|
return { dossier, state };
|
||||||
}
|
}
|
||||||
|
|
||||||
test("deadlock case: decision and followup agree and still render a D9/D10 style card", () => {
|
test("deadlock case: dated pool empty delivers instead of a D9/D10 style card", () => {
|
||||||
const { dossier } = deadlockDossier();
|
const { dossier } = deadlockDossier();
|
||||||
const inspected = inspectDiscriminatorProbes(contrastPacketFromDossier(dossier));
|
const inspected = inspectDiscriminatorProbes(contrastPacketFromDossier(dossier));
|
||||||
assert.equal(inspected.selected?.semanticKey, D9_STYLE_NO_OPTIONS.semantic_key);
|
assert.equal(inspected.selected?.semanticKey, D9_STYLE_NO_OPTIONS.semantic_key);
|
||||||
@@ -281,20 +281,26 @@ test("deadlock case: decision and followup agree and still render a D9/D10 style
|
|||||||
...catalog,
|
...catalog,
|
||||||
candidatesSeparated: false,
|
candidatesSeparated: false,
|
||||||
});
|
});
|
||||||
assert.equal(followupAsksRenderableDiscriminator(plan.next_followup), true);
|
assert.equal(followupAsksRenderableDiscriminator(plan.next_followup), false);
|
||||||
assert.equal(plan.next_followup?.intent, "distinguish_candidates");
|
assert.notEqual(plan.next_followup?.choice_kind, "varga_style");
|
||||||
assert.ok(plan.next_followup?.choice_frame);
|
|
||||||
assert.equal(plan.next_followup?.choice_kind, "varga_style");
|
|
||||||
assert.match(plan.next_followup?.semantic_key ?? "", /^varga\.d(9|10)\./);
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
plan.dropped_probes.some((item) => item.semantic_key.startsWith("varga.d9.") || item.semantic_key.startsWith("varga.d10.")),
|
plan.dropped_probes.some((item) => item.semantic_key.startsWith("varga.d9.") || item.semantic_key.startsWith("varga.d10.")),
|
||||||
false,
|
true,
|
||||||
JSON.stringify(plan.dropped_probes),
|
JSON.stringify(plan.dropped_probes),
|
||||||
);
|
);
|
||||||
|
|
||||||
const decision = decideFromDossier(dossier);
|
const decision = decideFromDossier(dossier);
|
||||||
assert.equal(decision.nextAction, "ask_candidate_discriminator");
|
// 原值: ask_candidate_discriminator,出 D9/D10 性格卡
|
||||||
assert.equal(decision.sessionOutcome, "discriminate_candidates");
|
// 新值: 带年月池空即交付,性格题 yearless_deferred
|
||||||
|
// 原因: BUG-651 性格题不得挡在结果前面
|
||||||
|
assert.ok(
|
||||||
|
decision.nextAction === "offer_provisional_range"
|
||||||
|
|| decision.nextAction === "ready_to_adopt"
|
||||||
|
|| decision.nextAction === "complete_with_range"
|
||||||
|
|| decision.nextAction === "ask_fact_collection",
|
||||||
|
decision.nextAction,
|
||||||
|
);
|
||||||
|
assert.notEqual(decision.sessionOutcome, "discriminate_candidates");
|
||||||
assert.equal(decision.canConfirmExactMinute, false);
|
assert.equal(decision.canConfirmExactMinute, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -319,10 +325,18 @@ test("varga_style probes missing style_options complete the same way in inspect
|
|||||||
probes: [probe],
|
probes: [probe],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
assert.equal(plan.next_followup?.semantic_key, probe.semanticKey);
|
assert.notEqual(plan.next_followup?.semantic_key, probe.semanticKey);
|
||||||
assert.equal(plan.next_followup?.choice_kind, "varga_style");
|
assert.notEqual(plan.next_followup?.choice_kind, "varga_style");
|
||||||
assert.ok(plan.next_followup?.choice_frame);
|
// 原值: 补全 style_options 后仍把 D9 当下一道区分题
|
||||||
assert.equal(plan.dropped_probes.some((item) => item.semantic_key === probe.semanticKey), false);
|
// 新值: D9 记 yearless_deferred,不再占用 discriminator
|
||||||
|
// 原因: BUG-651
|
||||||
|
assert.equal(
|
||||||
|
plan.dropped_probes.some((item) => (
|
||||||
|
item.semantic_key === probe.semanticKey && item.reason === "yearless_deferred"
|
||||||
|
)),
|
||||||
|
true,
|
||||||
|
JSON.stringify(plan.dropped_probes),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("method coverage incomplete still asks a renderable D9 style card", () => {
|
test("method coverage incomplete still asks a renderable D9 style card", () => {
|
||||||
@@ -338,11 +352,18 @@ test("method coverage incomplete still asks a renderable D9 style card", () => {
|
|||||||
});
|
});
|
||||||
assert.equal(plan.methods.find((item) => item.method_id === "relatives")?.status, "uncovered");
|
assert.equal(plan.methods.find((item) => item.method_id === "relatives")?.status, "uncovered");
|
||||||
assert.equal(plan.methods.find((item) => item.method_id === "occupation")?.status, "uncovered");
|
assert.equal(plan.methods.find((item) => item.method_id === "occupation")?.status, "uncovered");
|
||||||
assert.equal(decision.nextAction, "ask_candidate_discriminator");
|
// 原值: 方法覆盖未齐仍问 D9 性格卡
|
||||||
assert.equal(plan.next_followup?.intent, "distinguish_candidates");
|
// 新值: 训练门开、带年月池空即交付
|
||||||
assert.ok(plan.next_followup?.choice_frame);
|
// 原因: BUG-651 性格题不得挡在结果前面
|
||||||
assert.notEqual(plan.next_followup?.source, "method_coverage");
|
assert.ok(
|
||||||
assert.equal(decision.probe?.semanticKey, plan.next_followup?.semantic_key);
|
decision.nextAction === "offer_provisional_range"
|
||||||
|
|| decision.nextAction === "ready_to_adopt"
|
||||||
|
|| decision.nextAction === "complete_with_range"
|
||||||
|
|| decision.nextAction === "ask_fact_collection",
|
||||||
|
decision.nextAction,
|
||||||
|
);
|
||||||
|
assert.notEqual(plan.next_followup?.choice_kind, "varga_style");
|
||||||
|
assert.equal(decision.probe, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("clock-key varga_style without signs fail-closes to dated family collect instead of a dead-end discriminator", () => {
|
test("clock-key varga_style without signs fail-closes to dated family collect instead of a dead-end discriminator", () => {
|
||||||
@@ -491,10 +512,13 @@ test("decision.probe matches the followup the plan will actually ask", () => {
|
|||||||
candidatesSeparated: false,
|
candidatesSeparated: false,
|
||||||
});
|
});
|
||||||
const decision = decideFromDossier(dossier);
|
const decision = decideFromDossier(dossier);
|
||||||
assert.equal(decision.nextAction, "ask_candidate_discriminator");
|
// 原值: 下一问是 D9/D10,decision.probe 对得上
|
||||||
assert.equal(decision.probe?.semanticKey, plan.next_followup?.semantic_key);
|
// 新值: 性格题 deferred,decision.probe 与 next 都不是性格卡
|
||||||
assert.match(plan.next_followup?.semantic_key ?? "", /^varga\.d(9|10)\./);
|
// 原因: BUG-651
|
||||||
assert.notEqual(decision.probe?.semanticKey, D24.semantic_key);
|
assert.notEqual(decision.nextAction, "ask_candidate_discriminator");
|
||||||
|
assert.equal(decision.probe, null);
|
||||||
|
assert.notEqual(plan.next_followup?.choice_kind, "varga_style");
|
||||||
|
assert.notEqual(plan.next_followup?.semantic_key, D24.semantic_key);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("birthDate gating matches the real followup plan on an under-age event probe", () => {
|
test("birthDate gating matches the real followup plan on an under-age event probe", () => {
|
||||||
|
|||||||
@@ -2053,35 +2053,31 @@ test("D9/D10 contrast after occupation coverage asks a discriminator, not adopt"
|
|||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
assert.equal(plan.next_followup?.intent, "distinguish_candidates");
|
// 原值: 职业覆盖后仍问 D10 性格对照
|
||||||
assert.match(plan.next_followup?.user_prompt_hint ?? "", /职业前事|事业盘/);
|
// 新值: 性格题 deferred,不再作为淘汰区分题
|
||||||
assert.equal(conversationalSessionOutcome({
|
// 原因: BUG-651
|
||||||
|
assert.notEqual(plan.next_followup?.choice_kind, "varga_style");
|
||||||
|
assert.notEqual(plan.next_followup?.intent, "distinguish_candidates");
|
||||||
|
const outcome = conversationalSessionOutcome({
|
||||||
selectionAllowed: true,
|
selectionAllowed: true,
|
||||||
proposeAllowed: true,
|
proposeAllowed: true,
|
||||||
confirmationAllowed: false,
|
confirmationAllowed: false,
|
||||||
nextFollowup: plan.next_followup,
|
nextFollowup: plan.next_followup,
|
||||||
methods: plan.methods,
|
methods: plan.methods,
|
||||||
discriminatorProbe: {
|
discriminatorProbe: null,
|
||||||
probeId: "contrast:varga.d10",
|
|
||||||
candidateSetVersion: "05:00-05:02",
|
|
||||||
question: "核对一段还没用进评分的职业前事",
|
|
||||||
expectedOutcomes: [
|
|
||||||
{ outcomeId: "a", supportsCandidateIds: ["05:00"], conflictsCandidateIds: ["05:01"] },
|
|
||||||
{ outcomeId: "b", supportsCandidateIds: ["05:01"], conflictsCandidateIds: ["05:00"] },
|
|
||||||
],
|
|
||||||
candidateSplitHash: "varga.d10",
|
|
||||||
informationGain: 0.12,
|
|
||||||
sourceFeatures: [{ technique: "D10", calculationResultId: RESULT_ID }],
|
|
||||||
domain: "career",
|
|
||||||
year: null,
|
|
||||||
semanticKey: "varga.d10",
|
|
||||||
},
|
|
||||||
candidateScores: [
|
candidateScores: [
|
||||||
{ time: "05:00", score: 34 },
|
{ time: "05:00", score: 34 },
|
||||||
{ time: "05:01", score: 33 },
|
{ time: "05:01", score: 33 },
|
||||||
{ time: "05:02", score: 33 },
|
{ time: "05:02", score: 33 },
|
||||||
],
|
],
|
||||||
}), "discriminate_candidates");
|
});
|
||||||
|
// 原值: discriminate_candidates(把 D10 性格题当区分探针)
|
||||||
|
// 新值: 无带年月探针即交付
|
||||||
|
// 原因: BUG-651
|
||||||
|
assert.ok(
|
||||||
|
outcome === "adopt_representative" || outcome === "provisional_range",
|
||||||
|
outcome,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("answered duty language skips window D10 and does not re-ask covered education from a yearless D24 card", () => {
|
test("answered duty language skips window D10 and does not re-ask covered education from a yearless D24 card", () => {
|
||||||
@@ -2781,7 +2777,18 @@ test("same-year existence probes are skipped; a different-year dasha still ranks
|
|||||||
},
|
},
|
||||||
candidatesSeparated: false,
|
candidatesSeparated: false,
|
||||||
});
|
});
|
||||||
assert.equal(d24Wins.next_followup?.semantic_key, d10.semanticKey);
|
assert.notEqual(d24Wins.next_followup?.semantic_key, d10.semanticKey);
|
||||||
|
assert.notEqual(d24Wins.next_followup?.choice_kind, "varga_style");
|
||||||
|
// 原值: D10 增益更高时压过 D24 成为 next
|
||||||
|
// 新值: D10 性格题 deferred,不得挡在带年月题或交付前面
|
||||||
|
// 原因: BUG-651
|
||||||
|
assert.equal(
|
||||||
|
d24Wins.dropped_probes.some((item) => (
|
||||||
|
item.semantic_key === d10.semanticKey && item.reason === "yearless_deferred"
|
||||||
|
)),
|
||||||
|
true,
|
||||||
|
JSON.stringify(d24Wins.dropped_probes),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("three dated events with one holdout keep collecting instead of discriminating", () => {
|
test("three dated events with one holdout keep collecting instead of discriminating", () => {
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ test("training gate open does not mint yearless existence or quality varga cards
|
|||||||
assert.notEqual(plan.next_followup?.method_id, "d2_finance");
|
assert.notEqual(plan.next_followup?.method_id, "d2_finance");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("training gate open still asks a signed yearless D10 style card", () => {
|
test("training gate open does not ask a signed yearless D10 style card", () => {
|
||||||
const d10 = {
|
const d10 = {
|
||||||
probeId: "contrast:varga.d10.巨蟹座/狮子座",
|
probeId: "contrast:varga.d10.巨蟹座/狮子座",
|
||||||
candidateSetVersion: "04:47-05:15",
|
candidateSetVersion: "04:47-05:15",
|
||||||
@@ -309,10 +309,19 @@ test("training gate open still asks a signed yearless D10 style card", () => {
|
|||||||
probes: [...YEARLESS_PACKET.probes, d10],
|
probes: [...YEARLESS_PACKET.probes, d10],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
assert.equal(plan.next_followup?.intent, "distinguish_candidates");
|
assert.notEqual(plan.next_followup?.intent, "distinguish_candidates");
|
||||||
assert.ok(plan.next_followup?.choice_frame);
|
assert.notEqual(plan.next_followup?.semantic_key, d10.semanticKey);
|
||||||
assert.equal(plan.next_followup?.semantic_key, d10.semanticKey);
|
assert.notEqual(plan.next_followup?.choice_kind, "varga_style");
|
||||||
assert.equal(plan.next_followup?.choice_kind, "varga_style");
|
// 原值: 训练门开后仍问签名 D10 性格卡
|
||||||
|
// 新值: D10 记 yearless_deferred
|
||||||
|
// 原因: BUG-651 性格题不得挡在结果前面
|
||||||
|
assert.equal(
|
||||||
|
plan.dropped_probes.some((item) => (
|
||||||
|
item.semantic_key === d10.semanticKey && item.reason === "yearless_deferred"
|
||||||
|
)),
|
||||||
|
true,
|
||||||
|
JSON.stringify(plan.dropped_probes),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("training gate closed still withholds yearless varga cards", () => {
|
test("training gate closed still withholds yearless varga cards", () => {
|
||||||
|
|||||||
@@ -166,10 +166,18 @@ test("unanchored D10 varga_style cards are dropped; anchored cards mention the l
|
|||||||
contrastPacket: packet,
|
contrastPacket: packet,
|
||||||
candidatesSeparated: false,
|
candidatesSeparated: false,
|
||||||
});
|
});
|
||||||
assert.equal(anchored.next_followup?.semantic_key, D10_STYLE.semanticKey);
|
assert.notEqual(anchored.next_followup?.semantic_key, D10_STYLE.semanticKey);
|
||||||
assert.equal(anchored.next_followup?.choice_kind, "varga_style");
|
assert.notEqual(anchored.next_followup?.choice_kind, "varga_style");
|
||||||
assert.match(anchored.next_followup?.user_prompt_hint ?? "", /2018 年 7 月/);
|
// 原值: 有同年职业账本时把 D10 性格卡当下一问,题干提 2018 年 7 月
|
||||||
assert.doesNotMatch(anchored.next_followup?.choice_frame?.prompt ?? "", /2018/);
|
// 新值: 即使有账本锚点,D10 仍 deferred
|
||||||
|
// 原因: BUG-651 性格题不得挡在结果前面
|
||||||
|
assert.equal(
|
||||||
|
anchored.dropped_probes.some((item) => (
|
||||||
|
item.semantic_key === D10_STYLE.semanticKey && item.reason === "yearless_deferred"
|
||||||
|
)),
|
||||||
|
true,
|
||||||
|
JSON.stringify(anchored.dropped_probes),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("after a D9-style answer the compare request body stays legal", () => {
|
test("after a D9-style answer the compare request body stays legal", () => {
|
||||||
@@ -563,7 +571,12 @@ test("accident replay: fifth card is not the 2023 activation after four answers"
|
|||||||
assert.ok(
|
assert.ok(
|
||||||
decided.probe?.semanticKey === D10_LOW.semanticKey
|
decided.probe?.semanticKey === D10_LOW.semanticKey
|
||||||
|| decided.nextAction === "ready_to_adopt"
|
|| decided.nextAction === "ready_to_adopt"
|
||||||
|| decided.sessionOutcome === "adopt_representative",
|
|| decided.nextAction === "offer_provisional_range"
|
||||||
|
|| decided.nextAction === "complete_with_range"
|
||||||
|
|| decided.nextAction === "ask_fact_collection"
|
||||||
|
|| decided.sessionOutcome === "adopt_representative"
|
||||||
|
|| decided.sessionOutcome === "provisional_range",
|
||||||
|
`${decided.nextAction}/${decided.sessionOutcome}/${decided.probe?.semanticKey}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ function assertChoiceInvariants(input: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test("walkthrough D9 probe question is in the 4-80 window and must survive frame assembly", () => {
|
test("walkthrough D9 probe is deferred instead of blocking delivery", () => {
|
||||||
const packet = d9WalkthroughPacket();
|
const packet = d9WalkthroughPacket();
|
||||||
const probe = packet.probes.find((item) => item.semanticKey.startsWith("varga.d9."));
|
const probe = packet.probes.find((item) => item.semanticKey.startsWith("varga.d9."));
|
||||||
assert.ok(probe);
|
assert.ok(probe);
|
||||||
@@ -184,18 +184,18 @@ test("walkthrough D9 probe question is in the 4-80 window and must survive frame
|
|||||||
candidatesSeparated: false,
|
candidatesSeparated: false,
|
||||||
});
|
});
|
||||||
const followup = plan.next_followup;
|
const followup = plan.next_followup;
|
||||||
assert.equal(followup?.intent, "distinguish_candidates", JSON.stringify({
|
// 原值: 走查 D9 性格卡必须拼出 choice_frame
|
||||||
intent: followup?.intent,
|
// 新值: D9 记 yearless_deferred,不再作为必答区分题
|
||||||
source: followup?.source,
|
// 原因: BUG-651
|
||||||
method_id: followup?.method_id,
|
assert.notEqual(followup?.choice_kind, "varga_style");
|
||||||
dropped: plan.dropped_probes,
|
assert.notEqual(followup?.intent, "distinguish_candidates");
|
||||||
}));
|
assert.equal(
|
||||||
assert.equal(followup?.method_id, "d9_relationship");
|
plan.dropped_probes.some((item) => (
|
||||||
const frame = followup?.choice_frame ?? null;
|
item.semantic_key.startsWith("varga.d9.") && item.reason === "yearless_deferred"
|
||||||
assert.ok(frame, "D9 probe must assemble a choice_frame");
|
)),
|
||||||
assert.equal(frame?.prompt, D9_QUESTION);
|
true,
|
||||||
const copy = frame ? serverOwnedChoiceCopy(frame) : null;
|
JSON.stringify(plan.dropped_probes),
|
||||||
assert.ok(copy?.prompt, "assembled D9 copy must keep the probe question");
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("D9 probe shape with unwired prompt makes the choice invariants red", async () => {
|
test("D9 probe shape with unwired prompt makes the choice invariants red", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user