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[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); });