宽度超过 10 分钟或头名并列时不再出交付卡,改为按大运边界逐条问、 用类型芯片和年/月选择器录入。跳过的线换问法再问一次;答「这类事 都没有过」的不再问。用户说「没有了」仍立刻给目前范围。Skill 10.0.27。 BUG-740~743
132 lines
4.3 KiB
TypeScript
132 lines
4.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { RECTIFICATION_POLICY } from "../src/lib/rectification-policy.ts";
|
|
import {
|
|
decideRectification,
|
|
mayDeliverOnPrecision,
|
|
publicCanAdopt,
|
|
sessionOutcomeAllowsDelivery,
|
|
} from "../src/lib/rectification-agentic/core/rectification-decision.ts";
|
|
import { computePrecisionGateMet } from "../src/lib/rectification-agentic/core/precision-gate.ts";
|
|
import { RANGE_DELIVERY_TIE_PERCENT } from "../src/lib/rectification-agentic/user-copy.ts";
|
|
import { OPEN_ENGINE_CAPABILITY_CEILING } from "./rectification-v9-test-support.ts";
|
|
|
|
const TIED_FIVE = [
|
|
{ time: "04:51", score: 13 },
|
|
{ time: "04:53", score: 13 },
|
|
{ time: "04:56", score: 13 },
|
|
{ time: "04:58", score: 13 },
|
|
{ time: "05:11", score: 13 },
|
|
] as const;
|
|
|
|
function input(overrides: Partial<Parameters<typeof decideRectification>[0]> = {}) {
|
|
return {
|
|
engineCeiling: OPEN_ENGINE_CAPABILITY_CEILING,
|
|
methodCoverageAll: true,
|
|
trainingGateOpen: true,
|
|
candidateScores: TIED_FIVE.map((item) => ({ time: item.time, score: item.score })),
|
|
holdoutValidation: "unavailable" as const,
|
|
datedEventCount: 5,
|
|
datedDomainCount: 3,
|
|
discriminatorProbe: null,
|
|
targetedCollectExhausted: true,
|
|
refreshExhausted: true,
|
|
inferenceCredibleRange: ["04:51", "05:11"] as const,
|
|
openingCandidateRange: ["04:45", "05:15"] as const,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test("policy defines deliveryMaxWidthMinutes once", () => {
|
|
assert.equal(RECTIFICATION_POLICY.deliveryMaxWidthMinutes, 10);
|
|
assert.equal(RANGE_DELIVERY_TIE_PERCENT, 3);
|
|
});
|
|
|
|
test("precision gate requires width, display gap, and no exact tie", () => {
|
|
assert.equal(computePrecisionGateMet({
|
|
range: ["04:51", "05:11"],
|
|
topTwoPercents: [26, 26],
|
|
tiedForFirst: true,
|
|
}), false);
|
|
assert.equal(computePrecisionGateMet({
|
|
range: ["04:51", "04:59"],
|
|
topTwoPercents: [40, 35],
|
|
tiedForFirst: false,
|
|
}), true);
|
|
assert.equal(computePrecisionGateMet({
|
|
range: ["04:51", "04:59"],
|
|
topTwoPercents: [26, 26],
|
|
tiedForFirst: true,
|
|
}), false);
|
|
});
|
|
|
|
test("20 minute tied range keeps collecting with a guided question", () => {
|
|
const decision = decideRectification(input({
|
|
precisionGateMet: false,
|
|
guidedCollectExhausted: false,
|
|
}));
|
|
assert.equal(decision.nextAction, "ask_fact_collection");
|
|
assert.equal(decision.canOfferRange, false);
|
|
assert.equal(sessionOutcomeAllowsDelivery(decision.sessionOutcome), false);
|
|
assert.equal(publicCanAdopt(decision), false);
|
|
});
|
|
|
|
test("8 minute range with a 5 point lead delivers", () => {
|
|
const decision = decideRectification(input({
|
|
candidateScores: [
|
|
{ time: "04:51", score: 20 },
|
|
{ time: "04:53", score: 15 },
|
|
{ time: "04:56", score: 10 },
|
|
],
|
|
inferenceCredibleRange: ["04:51", "04:59"],
|
|
precisionGateMet: true,
|
|
guidedCollectExhausted: false,
|
|
targetedCollectExhausted: true,
|
|
refreshExhausted: true,
|
|
}));
|
|
assert.equal(decision.canOfferRange, true);
|
|
assert.equal(sessionOutcomeAllowsDelivery(decision.sessionOutcome), true);
|
|
});
|
|
|
|
test("8 minute exact tie keeps collecting", () => {
|
|
const decision = decideRectification(input({
|
|
candidateScores: [
|
|
{ time: "04:51", score: 13 },
|
|
{ time: "04:53", score: 13 },
|
|
],
|
|
inferenceCredibleRange: ["04:51", "04:59"],
|
|
precisionGateMet: false,
|
|
guidedCollectExhausted: false,
|
|
}));
|
|
assert.equal(decision.nextAction, "ask_fact_collection");
|
|
assert.equal(decision.canOfferRange, false);
|
|
});
|
|
|
|
test("gate unmet but guided collect exhausted still delivers", () => {
|
|
const decision = decideRectification(input({
|
|
precisionGateMet: false,
|
|
guidedCollectExhausted: true,
|
|
}));
|
|
assert.equal(decision.canOfferRange, true);
|
|
assert.equal(sessionOutcomeAllowsDelivery(decision.sessionOutcome), true);
|
|
});
|
|
|
|
test("userStopped delivers even when the precision gate is unmet", () => {
|
|
const decision = decideRectification(input({
|
|
userStopped: true,
|
|
precisionGateMet: false,
|
|
guidedCollectExhausted: false,
|
|
}));
|
|
assert.equal(decision.canOfferRange, true);
|
|
assert.equal(sessionOutcomeAllowsDelivery(decision.sessionOutcome), true);
|
|
});
|
|
|
|
test("omitted precision flags keep the helper path delivering", () => {
|
|
assert.equal(mayDeliverOnPrecision({
|
|
methodCoverageAll: true,
|
|
candidateScores: [],
|
|
engineCeiling: OPEN_ENGINE_CAPABILITY_CEILING,
|
|
}), true);
|
|
});
|