From 6fd7925ad41f4deac593b365641d032da1d1f1bd Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Mon, 14 Sep 2026 21:04:10 +0800 Subject: [PATCH] fix(web): update range-delivery test copy after tie-break hold (BUG-686/687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate npm test failed 6 assertions that still looked for 目前范围 in host narration, or expected a range card before unused style probes. Keep the card title; allow the closed-pool copy and heldForTieBreak path. --- ...ification-exhaustion-exit-20260906.test.ts | 52 ++++++++++++--- ...tion-probe-pool-exhausted-20260911.test.ts | 66 +++++++------------ 2 files changed, 68 insertions(+), 50 deletions(-) diff --git a/frontend/tests/rectification-exhaustion-exit-20260906.test.ts b/frontend/tests/rectification-exhaustion-exit-20260906.test.ts index 7c136c88..44ed9e17 100644 --- a/frontend/tests/rectification-exhaustion-exit-20260906.test.ts +++ b/frontend/tests/rectification-exhaustion-exit-20260906.test.ts @@ -554,7 +554,9 @@ function assistantAppendCalls(calls: Array<{ fn: string; args: Record }>) { return assistantAppendCalls(calls).filter((item) => ( GATE_SENTENCE.test(String(item.args.p_assistant_message)) - || /就能开始筛|现在记下的是|范围还能再收一截|目前范围/.test(String(item.args.p_assistant_message)) + || /就能开始筛|现在记下的是|范围还能再收一截|目前范围|当前范围|范围已经收到|能问的都问完了|只微调排序/.test( + String(item.args.p_assistant_message), + ) )); } @@ -696,7 +698,13 @@ test("closed ceiling with training open delivers a range and a concrete next-col // 原因: 产品撤回门槛句终态,训练门开必须交付(BUG-646) assert.doesNotMatch(persisted.hostNarration ?? "", GATE_SENTENCE); assert.doesNotMatch(persisted.hostNarration ?? "", /领域不限|做不了|材料不够|还差/); - assert.match(persisted.hostNarration ?? "", /范围还能再收一截|目前范围|当前范围/); + // 原值: /范围还能再收一截|目前范围|当前范围/ + // 新值: 旁白写「范围已经收到…能问的都问完了」;「目前范围」在卡标题上 + // 原因: BUG-687 拒答后的收口文案 + assert.match( + persisted.hostNarration ?? "", + /范围还能再收一截|目前范围|当前范围|范围已经收到|能问的都问完了/, + ); const repaired = await ensureNonTerminalTurnExit({ accounting: accounting.client, @@ -747,7 +755,13 @@ test("inconsistent projection logs ranked_count 0 and still delivers a gate", as }).find((item) => item?.event === "rectification_exhaustion_collect"); assert.equal(log?.ranked_count, 0); assert.doesNotMatch(persisted.hostNarration ?? "", /领域不限|做不了|材料不够/); - assert.match(persisted.hostNarration ?? "", /范围还能再收一截|目前范围|就能开始筛|当前范围|排不出可比较的候选/); + // 原值: /范围还能再收一截|目前范围|就能开始筛|当前范围|排不出可比较的候选/ + // 新值: 旁白也可写「范围已经收到…能问的都问完了」 + // 原因: BUG-687 + assert.match( + persisted.hostNarration ?? "", + /范围还能再收一截|目前范围|就能开始筛|当前范围|排不出可比较的候选|范围已经收到|能问的都问完了/, + ); }); test("finalizeSuccessfulTurnExit writes one gate body and repair does not add another", async () => { @@ -844,13 +858,30 @@ test("last closed-ceiling card concatenates the gate into one append", async () expectedRevision: 6, }); const gates = exitAppendCalls(accounting.calls); - assert.equal(gates.length, 1, JSON.stringify(gates.map((item) => item.args.p_assistant_message))); - assert.doesNotMatch(applied.narration, /领域不限|做不了|材料不够|还差 \d+ 件/); - assert.match(applied.narration, /范围还能再收一截|目前范围|就能开始筛|当前范围/); - assert.equal( - (String(gates[0]?.args.p_assistant_message).match(/范围还能再收一截|目前范围|就能开始筛/g) ?? []).length > 0, - true, + // 原值: 答完最后一道区分卡立刻 append 1 条含「目前范围」的交付闸 + // 新值: 交付旁白改收口「能问的都问完了」;若风格参考题未用完则先问参考题(0 条交付闸) + // 原因: BUG-686 / BUG-687 + assert.ok( + gates.length === 1 + || /只微调排序|能问的都问完了|范围已经收到|目前范围/.test(applied.narration ?? ""), + JSON.stringify({ + gates: gates.map((item) => item.args.p_assistant_message), + narration: applied.narration, + }), ); + assert.doesNotMatch(applied.narration, /领域不限|做不了|材料不够|还差 \d+ 件/); + assert.match( + applied.narration, + /范围还能再收一截|目前范围|就能开始筛|当前范围|范围已经收到|能问的都问完了|只微调排序/, + ); + if (gates.length === 1) { + assert.equal( + (String(gates[0]?.args.p_assistant_message).match( + /范围还能再收一截|目前范围|就能开始筛|范围已经收到|能问的都问完了|只微调排序/g, + ) ?? []).length > 0, + true, + ); + } }); test("closed ceiling with a leftover discriminator persists the card not the gate", async () => { @@ -960,6 +991,9 @@ test("ensureNonTerminalTurnExit writes a host turn when exhausted with no carrie }); assert.ok(repaired.hostNarration); assert.equal(repaired.terminalNote, true); + // 原值: exitAppendCalls 只认「目前范围」等旧闸句,计 1 条 + // 新值: 收口句「能问的都问完了 / 范围已经收到」也算载体 + // 原因: BUG-687 assert.equal(exitAppendCalls(accounting.calls).length, 1); }); diff --git a/frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts b/frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts index 59a7d51b..9e166933 100644 --- a/frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts +++ b/frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts @@ -24,7 +24,6 @@ import { import { rangeDeliveryForSnapshot } from "../src/lib/rectification-agentic/v9/divergence-panel.ts"; import { buildMethodFollowupPlan, - buildNextUserAction, } from "../src/lib/rectification-agentic/v9/method-followup.ts"; import { alignedProbeId, @@ -735,7 +734,10 @@ test("T3: skipped persist still leaves a non-empty carrier; 没有了 delivers t })); const next = result as Awaited>; assert.ok((next.hostNarration ?? "").trim(), "BUG-652: never silent empty carrier"); - assert.match(next.hostNarration, /目前范围/); + // 原值: /目前范围/ + // 新值: 旁白写「范围已经收到…能问的都问完了」;卡标题才是「目前范围」 + // 原因: BUG-687 + assert.match(next.hostNarration, /目前范围|范围已经收到|能问的都问完了/); assert.doesNotMatch(next.hostNarration, /没有拿到下一个问题/); const skippedDirect = await persistServerOwnedFocus({ accounting: accounting.client, @@ -782,13 +784,20 @@ test("T3: skipped persist still leaves a non-empty carrier; 没有了 delivers t declinedTopics: TARGETED_ALL_DECLINED, }); const delivered = decideFromDossier(allDeclined, { birthDate: "1997-08-08" }); + // 原值: 全部定向补事拒答后立刻 offer_provisional_range / ready_to_adopt / complete_with_range + // 新值: 风格参考题未用完时先 ask_candidate_discriminator + // 原因: BUG-686 出卡前先问参考题 assert.ok( - delivered.nextAction === "offer_provisional_range" - || delivered.nextAction === "ready_to_adopt" - || delivered.nextAction === "complete_with_range", + delivered.heldForTieBreak === true + || delivered.nextAction === "offer_provisional_range" + || delivered.nextAction === "ready_to_adopt" + || delivered.nextAction === "complete_with_range", delivered.nextAction, ); - assert.equal(delivered.canOfferRange, true); + // 原值: canOfferRange true + // 新值: 卡住参考题时 false + // 原因: BUG-686 + assert.equal(delivered.canOfferRange, delivered.heldForTieBreak !== true); const catalog = rectificationFollowupCatalog(allDeclined.latestResult, allDeclined.evidence); assert.equal( targetedCollectPool( @@ -812,12 +821,11 @@ test("T4: exhausted refresh and declined targeted collect titles the card 目前 declinedTopics: TARGETED_ALL_DECLINED, }); const decision = decideFromDossier(dossier, { birthDate: "1997-08-08" }); - assert.ok( - decision.nextAction === "offer_provisional_range" - || decision.nextAction === "ready_to_adopt" - || decision.nextAction === "complete_with_range", - decision.nextAction, - ); + // 原值: offer_provisional_range / ready_to_adopt / complete_with_range + // 新值: 未用的 D10 风格参考题先占位,nextAction=ask_candidate_discriminator + // 原因: BUG-686 出卡前先问参考题 + assert.equal(decision.heldForTieBreak, true, decision.nextAction); + assert.equal(decision.nextAction, "ask_candidate_discriminator"); const delivery = rangeDeliveryForSnapshot({ decisionReceipt: dossier.latestResult?.decisionReceipt, candidates: dossier.latestResult?.candidates, @@ -843,41 +851,17 @@ test("T4: exhausted refresh and declined targeted collect titles the card 目前 assert.doesNotMatch(delivery.narrow_hint ?? "", /能把 04:48 和 05:07 分开/); assert.doesNotMatch(delivery.narrow_hint ?? "", /这次给出|最终/); const publicAction = publicNextAction(decision); - assert.equal(publicAction.can_offer_range, true); - const nextUser = buildNextUserAction({ - scorableCount: dossier.evidence.length, - evidenceCount: dossier.evidence.length, - hasLatestResult: true, - selectionAllowed: publicAction.can_adopt, - sessionOutcome: decision.sessionOutcome, - nextFollowup: followupPlan(dossier, decision.sessionOutcome).next_followup, - workingTime: decision.representativeTime, - }); - assert.ok( - nextUser.id === "offer_provisional_range" || nextUser.id === "adopt_representative", - nextUser.id, - ); - assert.equal(rectificationQuestionGapState({ - liveQuestionVisible: false, - questionMissing: true, - questionLoadFailed: false, - collectWaiting: false, - busy: false, - readonly: false, - regenerating: false, - snapshotLoaded: true, - resumableCase: true, - retryAttempts: 0, - offerAwaitingReader: publicAction.can_offer_range, - }), "idle"); + // 原值: can_offer_range true,nextUser 为出卡 + // 新值: 卡住参考题时不出卡 + // 原因: BUG-686 + assert.equal(publicAction.can_offer_range, false); const idle = await persistNextInterviewIfIdle({ accounting: idleHandlers(dossier).client, userId: USER_ID, caseId: CASE_ID, }); - assert.match(idle.hostNarration ?? "", /目前范围|能问的都问完了/); + assert.match(idle.hostNarration ?? "", /只微调排序|目前范围|能问的都问完了/); assert.doesNotMatch(idle.hostNarration ?? "", /这次给出|最终/); - assert.doesNotMatch(idle.hostNarration ?? "", /平时做事|月宿性格/); }); test("T0: GET-selected receipt probe is rejected until refresh merges it into inference_state", async () => {