import assert from "node:assert/strict"; import test from "node:test"; import { applyProbeOutcome, outcomeForAnswer } from "../src/lib/rectification-agentic/core/apply-probe-outcome.ts"; import { buildInferenceState } from "../src/lib/rectification-agentic/core/build-state.ts"; import { distinguishContractErrors } from "../src/lib/rectification-agentic/core/distinguish-contract.ts"; import { probeFromEngine } from "../src/lib/rectification-agentic/core/probes-from-engine.ts"; import { parseDiscriminatingEventProbes } from "../src/lib/rectification-agentic/v9/refinement-packet.ts"; test("CI forbids distinguish probes with empty mapping or non-positive gain", () => { assert.deepEqual(distinguishContractErrors({ role: "distinguish", information_gain: 0, candidate_ids: ["05:00", "05:20"], expected_outcomes: [ { answer_class: "yes", supports: ["05:00"], conflicts: ["05:20"] }, { answer_class: "no", supports: ["05:20"], conflicts: ["05:00"] }, ], }), ["distinguish_non_positive_information_gain"]); assert.deepEqual(distinguishContractErrors({ role: "distinguish", information_gain: 0.4, candidate_ids: [], expected_outcomes: [ { answer_class: "yes", supports: [], conflicts: [] }, { answer_class: "no", supports: [], conflicts: [] }, ], }), ["distinguish_empty_candidate_ids"]); assert.deepEqual(distinguishContractErrors({ role: "distinguish", information_gain: 0.4, candidate_ids: ["05:00", "05:20"], expected_outcomes: [], }), ["distinguish_empty_expected_outcomes"]); }); test("receipt parser drops invalid distinguish probes and known_event_quality", () => { const parsed = parseDiscriminatingEventProbes([ { year: 2016, year_label: "2016 年前后", domain: "education", event_family: "学业变化", source: "known_event_quality", tracks: ["vimshottari", "narayana"], tracks_agree: true, unique_minute_claim: false, user_meaning: "clarification only", role: "distinguish", information_gain: 0, }, { year: 2018, year_label: "2018 年前后", domain: "career", event_family: "职责变化", source: "dasha_activation", tracks: ["vimshottari", "narayana"], tracks_agree: true, unique_minute_claim: false, user_meaning: "engine locked year and family", role: "distinguish", information_gain: 0.4, candidate_ids: ["05:00", "05:20"], expected_outcomes: [ { answer_class: "yes", supports: ["05:00"], conflicts: ["05:20"] }, { answer_class: "no", supports: ["05:20"], conflicts: ["05:00"] }, ], }, ]); assert.equal(parsed.length, 1); assert.equal(parsed[0]?.domain, "career"); assert.equal(parsed[0]?.source, "dasha_activation"); assert.ok((parsed[0]?.information_gain ?? 0) > 0); }); test("receipt parser keeps engine month on dasha boundary probes", () => { const parsed = parseDiscriminatingEventProbes([{ year: 2018, month: 3, year_label: "2018 年 3 月前后", domain: "career", event_family: "职责变化", source: "dasha_boundary", tracks: ["vimshottari", "narayana"], tracks_agree: true, unique_minute_claim: false, user_meaning: "时间范围锁定 2018 年 3 月前后", role: "distinguish", information_gain: 0.4, semantic_key: "career.2018.03.dasha_boundary", candidate_ids: ["05:00", "05:20"], expected_outcomes: [ { answer_class: "yes", supports: ["05:00"], conflicts: ["05:20"] }, { answer_class: "no", supports: ["05:20"], conflicts: ["05:00"] }, ], }]); assert.equal(parsed[0]?.month, 3); assert.equal(parsed[0]?.year_label, "2018 年 3 月前后"); assert.equal(parsed[0]?.source, "dasha_boundary"); }); test("randomized hidden mutated answers change posterior only when mapped", () => { const probe = probeFromEngine({ year: 2018, year_label: "2018 年前后", domain: "career", event_family: "职责变化", source: "dasha_activation", tracks: ["vimshottari", "narayana"], tracks_agree: true, unique_minute_claim: false, user_meaning: "engine locked year and family", role: "distinguish", information_gain: 0.4, semantic_key: "career.2018", candidate_split_hash: "set:career:2018", candidate_ids: ["05:00", "05:20"], expected_outcomes: [ { answer_class: "yes", supports: ["05:00"], conflicts: ["05:20"] }, { answer_class: "no", supports: ["05:20"], conflicts: ["05:00"] }, { answer_class: "unsure", supports: [], conflicts: [] }, ], }); assert.ok(probe); const scores = { "05:00": 10, "05:20": 10 }; const yes = applyProbeOutcome(scores, probe, "yes"); assert.equal(yes.kind, "informative"); assert.notDeepEqual(yes.scores, scores); assert.ok(yes.deltas["05:00"] !== 0); const mutated = applyProbeOutcome(scores, probe, "unsure"); assert.equal(mutated.kind, "low_information"); assert.deepEqual(mutated.scores, scores); const state = buildInferenceState({ range_start: "05:00", range_end: "05:20", candidates: [ { id: "05:00", time: "05:00", relative_support: 10 }, { id: "05:20", time: "05:20", relative_support: 10 }, ], events: [ { id: "e1", domain: "education", year: 2016, precision: "month" }, { id: "e2", domain: "career", year: 2018, precision: "year" }, { id: "e3", domain: "family", year: 2020, precision: "year" }, ], probes: [probe], answered_probes: [{ probe_id: probe.id, semantic_key: probe.semantic_key, candidate_split_hash: probe.candidate_split_hash, answer_class: "unsure", classified_from: "choice", }], }); assert.equal(state.last_inference_round?.kind, "low_information"); assert.deepEqual(state.last_inference_round?.scores_before, state.last_inference_round?.scores_after); assert.equal( state.rounds.filter((item) => item.kind === "informative").length, 0, ); }); test("existence weak_yes shares yes mapping at half weight and never eliminates on one answer", () => { const probe = probeFromEngine({ year: 2018, year_label: "2018 年前后", domain: "career", event_family: "职责变化", source: "dasha_activation", tracks: ["vimshottari", "narayana"], tracks_agree: true, unique_minute_claim: false, user_meaning: "时间范围锁定 2018 年前后", role: "distinguish", information_gain: 0.4, semantic_key: "career.2018", candidate_split_hash: "set:career:2018", candidate_ids: ["05:00", "05:20"], expected_outcomes: [ { answer_class: "yes", supports: ["05:00"], conflicts: ["05:20"] }, { answer_class: "weak_yes", supports: ["05:00"], conflicts: ["05:20"] }, { answer_class: "no", supports: ["05:20"], conflicts: ["05:00"] }, { answer_class: "unsure", supports: [], conflicts: [] }, ], }); assert.ok(probe); assert.deepEqual(outcomeForAnswer(probe, "yes")?.supports, outcomeForAnswer(probe, "weak_yes")?.supports); assert.deepEqual(outcomeForAnswer(probe, "yes")?.conflicts, outcomeForAnswer(probe, "weak_yes")?.conflicts); const scores = { "05:00": 10, "05:20": 10 }; const yes = applyProbeOutcome(scores, probe, "yes"); const weak = applyProbeOutcome(scores, probe, "weak_yes"); assert.equal(yes.deltas["05:00"], 2); assert.equal(yes.deltas["05:20"], -2); assert.equal(weak.deltas["05:00"], 1); assert.equal(weak.deltas["05:20"], -1); assert.deepEqual(weak.eliminated_ids, []); assert.equal(weak.kind, "informative"); });