Files
Jyotisha/frontend/tests/rectification-credible-range-projection.test.ts
T
Jesse_Chen 20df8c6022
Independent Staging Quality Gate / validate (push) Successful in 10m34s
Independent Staging Quality Gate / publish (push) Successful in 13m34s
fix(rectification): let engine-ready cases adopt a representative minute
deliveryCapability no longer ties adoption to minute separation or holdout;
those stay on the exact-minute confirmation gate so users can save a range.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 16:52:01 +08:00

250 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import test from "node:test";
import { buildInferenceState, type EngineEventInput } from "../src/lib/rectification-agentic/core/build-state.ts";
import { unionStillValidRange } from "../src/lib/rectification-agentic/core/credible-range.ts";
import { decideFromDossier } from "../src/lib/rectification-agentic/v9/decision-from-dossier.ts";
import {
authoritativeCandidateProjection,
buildCaseInferenceState,
} from "../src/lib/rectification-agentic/v9/inference-adapter.ts";
import { evidenceLedgerFingerprint } from "../src/lib/rectification-agentic/v9/tool-service.ts";
const HOLDOUT_EVENTS: readonly EngineEventInput[] = [
{ id: "e-edu", domain: "education", year: 2016, precision: "year" },
{ id: "e-career", domain: "career", year: 2018, precision: "year" },
{ id: "e-rel", domain: "relationship", year: 2021, precision: "year" },
{ id: "e-fam", domain: "family", year: 2023, precision: "year" },
];
const COVERAGE_EVIDENCE = [
{
id: "e-edu",
status: "confirmed",
domain: "education",
datePrecision: "year",
occurredFrom: "2016-01-01",
occurredTo: null,
eventKind: "education_milestone",
},
{
id: "e-career",
status: "confirmed",
domain: "career",
datePrecision: "year",
occurredFrom: "2018-01-01",
occurredTo: null,
eventKind: "career_entry",
},
{
id: "e-rel",
status: "confirmed",
domain: "relationship",
datePrecision: "year",
occurredFrom: "2021-01-01",
occurredTo: null,
eventKind: "relationship_start",
},
{
id: "e-fam",
status: "confirmed",
domain: "family",
datePrecision: "year",
occurredFrom: "2023-01-01",
occurredTo: null,
eventKind: "family_event",
},
{
id: "e-occ",
status: "confirmed",
domain: "occupation",
datePrecision: "unknown",
occurredFrom: null,
occurredTo: null,
eventKind: "occupation_note",
},
] as const;
function producedSnapshot(
rows: readonly Readonly<{ time: string; relativeSupport: number }>[],
events: readonly EngineEventInput[] = [],
) {
const times = rows.map((item) => item.time).sort();
const state = buildInferenceState({
range_start: times[0]!,
range_end: times[times.length - 1]!,
candidates: rows.map((item) => ({
id: item.time,
time: item.time,
relative_support: item.relativeSupport,
})),
events,
probes: [],
});
const persisted = state.candidates.map((item) => ({
candidateId: item.id,
time: item.time,
rank: item.rank,
relativeSupport: Math.round(item.posterior_score),
}));
return {
state,
latest: {
resultId: "55555555-5555-4555-8555-555555555555",
candidates: persisted,
representativeTime: state.representative_time,
evidenceLedgerFingerprint: evidenceLedgerFingerprint(COVERAGE_EVIDENCE as never),
decisionReceipt: {
acceptance_allowed: true,
selection_allowed: true,
propose_allowed: true,
confirmation_allowed: false,
inference_state: state,
},
},
};
}
function dossierFor(latest: ReturnType<typeof producedSnapshot>["latest"]) {
return {
evidence: COVERAGE_EVIDENCE,
conversationSummary: { activeFocus: null, declinedSkippedTopics: [] },
latestResult: latest,
case: { acceptedTime: null },
};
}
test("producer 58/42 lead keeps both candidates and can close without opening the minute gate", () => {
const { state, latest } = producedSnapshot([
{ time: "05:02", relativeSupport: 58 },
{ time: "04:55", relativeSupport: 42 },
], HOLDOUT_EVENTS);
assert.deepEqual(state.credible_range, unionStillValidRange(state.candidates));
assert.notDeepEqual(state.credible_range, ["04:55", "05:02"]);
const projection = authoritativeCandidateProjection(latest);
assert.equal(projection.consistent, true);
assert.equal(projection.candidates.length, 2);
assert.deepEqual(projection.credibleRange, state.credible_range);
const decision = decideFromDossier(dossierFor(latest));
assert.equal(decision.separation.sufficient, true);
// 原断言 nextAction=ask_holdout_validation → 新断言 ready_to_adopt。
// 为什么:分离充分仍不够开确认门;holdout 只挡唯一分钟,provisional 采用应直接出牌。
assert.equal(decision.nextAction, "ready_to_adopt");
assert.equal(decision.canAdopt, true);
assert.equal(decision.canConfirmExactMinute, false);
assert.notEqual(decision.sessionOutcome, "exact_minute_confirmed");
});
test("producer 58/42 with no holdout remains a review-only range", () => {
const { latest } = producedSnapshot([
{ time: "05:02", relativeSupport: 58 },
{ time: "04:55", relativeSupport: 42 },
]);
const decision = decideFromDossier(dossierFor(latest));
assert.equal(authoritativeCandidateProjection(latest).consistent, true);
assert.equal(decision.separation.sufficient, true);
// 原断言 canAdopt=falseholdout unavailable 不放行)→ 新断言 canAdopt=true。
// 为什么:无 holdout 只挡唯一分钟确认,不挡代表性采用。
assert.equal(decision.nextAction, "offer_provisional_range");
assert.equal(decision.canAdopt, true);
assert.equal(decision.canConfirmExactMinute, false);
assert.notEqual(decision.sessionOutcome, "exact_minute_confirmed");
});
test("producer 50/30/20 lead keeps all three candidates and still-valid range", () => {
const { state, latest } = producedSnapshot([
{ time: "05:00", relativeSupport: 50 },
{ time: "05:20", relativeSupport: 30 },
{ time: "05:40", relativeSupport: 20 },
]);
const projection = authoritativeCandidateProjection(latest);
assert.equal(projection.consistent, true);
assert.equal(projection.candidates.length, 3);
assert.deepEqual(projection.credibleRange, unionStillValidRange(state.candidates));
assert.deepEqual(projection.credibleRange, state.credible_range);
});
test("producer sole candidate stays consistent", () => {
const { state, latest } = producedSnapshot([
{ time: "05:02", relativeSupport: 100 },
]);
const projection = authoritativeCandidateProjection(latest);
assert.equal(projection.consistent, true);
assert.equal(projection.candidates.length, 1);
assert.deepEqual(projection.credibleRange, unionStillValidRange(state.candidates));
});
test("missing inference_state still fail-closes to an empty projection", () => {
const projection = authoritativeCandidateProjection({
candidates: [
{ candidateId: "05:02", time: "05:02", relativeSupport: 58 },
{ candidateId: "04:55", time: "04:55", relativeSupport: 42 },
],
decisionReceipt: {},
});
assert.equal(projection.consistent, false);
assert.deepEqual(projection.candidates, []);
assert.deepEqual(projection.scores, []);
assert.equal(projection.representativeTime, null);
assert.equal(projection.credibleRange, null);
});
test("a corrupted receipt range outside the window still fail-closes", () => {
const { state, latest } = producedSnapshot([
{ time: "05:02", relativeSupport: 58 },
{ time: "04:55", relativeSupport: 42 },
]);
const broken = {
...latest,
decisionReceipt: {
inference_state: { ...state, credible_range: ["04:00", "04:10"] },
},
};
const projection = authoritativeCandidateProjection(broken);
assert.equal(projection.consistent, false);
assert.deepEqual(projection.candidates, []);
assert.equal(projection.credibleRange, null);
});
test("quarter-precision evidence stays dated in the inference ledger", () => {
const state = buildCaseInferenceState({
range: { start_time: "05:00", end_time: "05:10" },
candidates: [
{ candidateId: "05:00", time: "05:00", relativeSupport: 50 },
{ candidateId: "05:10", time: "05:10", relativeSupport: 50 },
],
evidence: [
{ id: "q-career", domain: "career", occurredFrom: "2018-04-01", datePrecision: "quarter" },
{ id: "y-edu", domain: "education", occurredFrom: "2016-01-01", datePrecision: "year" },
],
probes: [],
});
const quarter = state.events.find((item) => item.id === "q-career");
assert.equal(quarter?.precision, "year");
assert.notEqual(quarter?.usage, "unused");
});
test("buildCaseInferenceState receipt is the producer of the projected range", () => {
const state = buildCaseInferenceState({
range: { start_time: "04:55", end_time: "05:02" },
candidates: [
{ candidateId: "05:02", time: "05:02", relativeSupport: 58 },
{ candidateId: "04:55", time: "04:55", relativeSupport: 42 },
],
evidence: [],
probes: [],
});
const projection = authoritativeCandidateProjection({
candidates: state.candidates.map((item) => ({
candidateId: item.id,
time: item.time,
relativeSupport: Math.round(item.posterior_score),
})),
decisionReceipt: { inference_state: state },
});
assert.equal(projection.consistent, true);
assert.deepEqual(projection.credibleRange, unionStillValidRange(state.candidates));
});