Files
Jyotisha/frontend/tests/rectification-hidden-e2e.test.ts
T
Jesse_Chen a0ce55f066
Independent Staging Quality Gate / validate (push) Successful in 11m17s
Independent Staging Quality Gate / publish (push) Failing after 9m28s
fix(web): drive rectification from candidate contrast and allow range close
Showing a choice card is no longer treated as completion. Distinguish probes
require real candidate groups, holdout stays out of scoring, and ordinary
sessions can finish with a credible range instead of an exact-minute gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-26 17:19:26 +08:00

218 lines
7.8 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { applyChoiceWithoutEvidence } from "../src/lib/rectification-agentic/v9/inference-adapter.ts";
import { applyHoldoutAnswer, buildInferenceState } from "../src/lib/rectification-agentic/core/build-state.ts";
import { unionStillValidRange } from "../src/lib/rectification-agentic/core/credible-range.ts";
import { decideRectification } from "../src/lib/rectification-agentic/core/rectification-decision.ts";
import { posteriorMap } from "../src/lib/rectification-agentic/core/decision-fingerprint.ts";
import { holdoutEventIds } from "../src/lib/rectification-agentic/core/split-holdout.ts";
import { selectDiscriminatorProbe, buildCandidateContrastPacket } from "../src/lib/rectification-agentic/core/candidate-contrast-packet.ts";
import type { ConflictProbe } from "../src/lib/rectification-agentic/core/types.ts";
function probe(input: {
id: string;
domain: string;
year: number;
yesSupports: readonly string[];
noSupports: readonly string[];
}): ConflictProbe {
return {
id: input.id,
semantic_key: `${input.domain}.${input.year}`,
candidate_split_hash: `${input.yesSupports.join(",")}|${input.noSupports.join(",")}`,
domain: input.domain,
year: input.year,
question: `${input.year} 年前后是否发生过相关前事?`,
candidate_ids: [...input.yesSupports, ...input.noSupports],
expected_outcomes: [
{ answer_class: "yes", supports: input.yesSupports, conflicts: [] },
{ answer_class: "no", supports: input.noSupports, conflicts: [] },
{ answer_class: "unsure", supports: [], conflicts: [] },
],
information_gain: 0.4,
source: "dasha_boundary",
};
}
test("credible range unions every still-valid cluster, not only rank=1", () => {
const range = unionStillValidRange([
{
id: "05:00",
time: "05:00",
cluster_range: ["05:00", "05:02"],
prior_score: 12,
posterior_score: 34,
probability: 0.34,
status: "active",
rank: 1,
strong_conflict_count: 0,
},
{
id: "05:20",
time: "05:20",
cluster_range: ["05:20", "05:22"],
prior_score: 12,
posterior_score: 33,
probability: 0.33,
status: "active",
rank: 2,
strong_conflict_count: 0,
},
{
id: "05:40",
time: "05:40",
cluster_range: ["05:40", "05:40"],
prior_score: 4,
posterior_score: 4,
probability: 0.04,
status: "active",
rank: 3,
strong_conflict_count: 0,
},
]);
assert.deepEqual(range, ["05:00", "05:22"]);
});
test("hidden case walks collection through holdout to a range or representative close", () => {
const first = probe({
id: "p-career",
domain: "career",
year: 2015,
yesSupports: ["05:00"],
noSupports: ["05:20"],
});
const collected = buildInferenceState({
range_start: "04:50",
range_end: "05:30",
candidates: [
{ id: "05:00", time: "05:00", relative_support: 10 },
{ id: "05:20", time: "05:20", relative_support: 4 },
],
events: [
{ id: "e1", domain: "education", year: 2016, precision: "month" },
{ id: "e2", domain: "career", year: 2018, precision: "year" },
{ id: "e3", domain: "relationship", year: 2021, precision: "year" },
{ id: "e4", domain: "family", year: 2023, precision: "year" },
],
probes: [first],
});
const holdoutIds = holdoutEventIds(collected.events);
assert.equal(holdoutIds.size, 1);
assert.ok(collected.events.some((item) => item.usage === "training"));
assert.ok(!collected.probes.some((item) => {
const holdout = collected.events.find((row) => row.usage === "holdout");
return holdout !== undefined && item.domain === holdout.domain && item.year === holdout.year;
}));
const packet = buildCandidateContrastPacket({
candidateSetVersion: collected.candidate_set_id,
calculationResultId: "11111111-1111-4111-8111-111111111111",
engineProbes: collected.probes.map((item) => ({
semantic_key: item.semantic_key,
candidate_split_hash: item.candidate_split_hash,
domain: item.domain,
year: item.year,
user_meaning: item.question,
information_gain: item.information_gain,
expected_outcomes: item.expected_outcomes,
candidate_ids: item.candidate_ids,
})),
});
const discriminator = selectDiscriminatorProbe(packet);
assert.ok(discriminator);
assert.ok(discriminator.informationGain > 0);
assert.ok(discriminator.expectedOutcomes.length >= 2);
const mapped = new Set(discriminator.expectedOutcomes.flatMap((row) => [
...row.supportsCandidateIds,
...row.conflictsCandidateIds,
]));
assert.ok(mapped.size >= 2);
const before = posteriorMap(collected.candidates);
const answered = applyChoiceWithoutEvidence(collected, {
choiceKey: "A",
schema: { probe_id: first.id, semantic_key: first.semantic_key },
});
assert.equal(answered.applied, true);
assert.notDeepEqual(posteriorMap(answered.state.candidates), before);
const scores = answered.state.candidates.map((item) => ({
time: item.time,
score: item.posterior_score,
}));
const afterAnswers = decideRectification({
methodCoverageAll: true,
candidateScores: scores,
holdoutValidation: "not_started",
});
assert.equal(afterAnswers.nextAction, "ask_holdout_validation");
assert.equal(afterAnswers.canConfirmExactMinute, false);
const holdoutPassed = applyHoldoutAnswer(answered.state, "yes");
assert.equal(holdoutPassed.holdout_passed, true);
assert.deepEqual(posteriorMap(holdoutPassed.candidates), posteriorMap(answered.state.candidates));
const closed = decideRectification({
methodCoverageAll: true,
candidateScores: holdoutPassed.candidates.map((item) => ({
time: item.time,
score: item.posterior_score,
})),
holdoutValidation: "passed",
inferenceCredibleRange: holdoutPassed.credible_range,
});
assert.ok(closed.nextAction === "ready_to_adopt" || closed.nextAction === "complete_with_range");
assert.equal(closed.canAdopt, true);
assert.equal(closed.canConfirmExactMinute, false);
assert.ok(closed.credibleRange);
assert.notEqual(closed.sessionOutcome, "discriminate_candidates");
});
test("holdout failure returns to candidate discrimination", () => {
const retry = probe({
id: "p-retry",
domain: "career",
year: 2018,
yesSupports: ["05:00"],
noSupports: ["05:20"],
});
const state = buildInferenceState({
range_start: "04:50",
range_end: "05:30",
candidates: [
{ id: "05:00", time: "05:00", relative_support: 40 },
{ id: "05:20", time: "05:20", relative_support: 12 },
],
events: [
{ id: "e1", domain: "education", year: 2016, precision: "month" },
{ id: "e2", domain: "career", year: 2018, precision: "year" },
{ id: "e3", domain: "relationship", year: 2021, precision: "year" },
{ id: "e4", domain: "family", year: 2023, precision: "year" },
],
probes: [retry],
});
const failed = applyHoldoutAnswer(state, "no");
assert.equal(failed.holdout_passed, false);
const retryPacket = buildCandidateContrastPacket({
candidateSetVersion: failed.candidate_set_id,
calculationResultId: "11111111-1111-4111-8111-111111111111",
engineProbes: [{
semantic_key: retry.semantic_key,
candidate_split_hash: retry.candidate_split_hash,
domain: retry.domain,
year: retry.year,
user_meaning: retry.question,
information_gain: retry.information_gain,
expected_outcomes: retry.expected_outcomes,
candidate_ids: retry.candidate_ids,
}],
});
const next = decideRectification({
methodCoverageAll: true,
candidateScores: failed.candidates.map((item) => ({ time: item.time, score: item.posterior_score })),
discriminatorProbe: selectDiscriminatorProbe(retryPacket),
holdoutValidation: "failed",
});
assert.equal(next.nextAction, "ask_candidate_discriminator");
});