e41eaace8d
Co-authored-by: Cursor <cursoragent@cursor.com>
172 lines
6.7 KiB
TypeScript
172 lines
6.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { applyProbeOutcome } from "../src/lib/rectification-agentic/core/apply-probe-outcome.ts";
|
|
import { buildInferenceState } from "../src/lib/rectification-agentic/core/build-state.ts";
|
|
import {
|
|
buildCandidateContrastPacket,
|
|
conflictProbesFromContrast,
|
|
} from "../src/lib/rectification-agentic/core/candidate-contrast-packet.ts";
|
|
import { asInferenceState } from "../src/lib/rectification-agentic/core/compose-receipt.ts";
|
|
import { decisionStateFingerprint } from "../src/lib/rectification-agentic/core/decision-fingerprint.ts";
|
|
import type { AnswerClass, ConflictProbe } from "../src/lib/rectification-agentic/core/types.ts";
|
|
|
|
function twoGroupStylePacket() {
|
|
return buildCandidateContrastPacket({
|
|
candidateSetVersion: "05:00-05:04",
|
|
candidateTimes: ["05:00", "05:04"],
|
|
transitions: [
|
|
{ layer: "d9", at: "05:04", from_sign: "巨蟹座", to_sign: "狮子座" },
|
|
],
|
|
});
|
|
}
|
|
|
|
function threeGroupStylePacket() {
|
|
return buildCandidateContrastPacket({
|
|
candidateSetVersion: "05:00-05:04",
|
|
candidateTimes: ["05:00", "05:03", "05:04"],
|
|
transitions: [
|
|
{ layer: "d10", at: "05:03", from_sign: "巨蟹座", to_sign: "狮子座" },
|
|
{ layer: "d10", at: "05:04", from_sign: "狮子座", to_sign: "处女座" },
|
|
],
|
|
});
|
|
}
|
|
|
|
function styleProbeFrom(packet: ReturnType<typeof buildCandidateContrastPacket>): ConflictProbe {
|
|
const probe = conflictProbesFromContrast(packet).find((item) => item.source === "varga_contrast");
|
|
assert.ok(probe);
|
|
return probe;
|
|
}
|
|
|
|
function absSupportDelta(probe: ConflictProbe, answer: AnswerClass, time: string): number {
|
|
const scores = Object.fromEntries(probe.candidate_ids.map((id) => [id, 10]));
|
|
const applied = applyProbeOutcome(scores, probe, answer);
|
|
return Math.abs(applied.deltas[time] ?? 0);
|
|
}
|
|
|
|
test("two-group varga_style A and B move their groups by the same absolute delta", () => {
|
|
const probe = styleProbeFrom(twoGroupStylePacket());
|
|
assert.equal(probe.choice_kind, "varga_style");
|
|
const yesGroup = probe.expected_outcomes.find((row) => row.answer_class === "yes")?.supports[0];
|
|
const weakGroup = probe.expected_outcomes.find((row) => row.answer_class === "weak_yes")?.supports[0];
|
|
assert.ok(yesGroup);
|
|
assert.ok(weakGroup);
|
|
assert.notEqual(yesGroup, weakGroup);
|
|
const yesDelta = absSupportDelta(probe, "yes", yesGroup);
|
|
const weakDelta = absSupportDelta(probe, "weak_yes", weakGroup);
|
|
assert.equal(yesDelta, 2);
|
|
assert.equal(weakDelta, 2);
|
|
assert.equal(yesDelta, weakDelta);
|
|
const unsure = applyProbeOutcome(
|
|
Object.fromEntries(probe.candidate_ids.map((id) => [id, 10])),
|
|
probe,
|
|
"unsure",
|
|
);
|
|
assert.ok(Object.values(unsure.deltas).every((value) => value === 0));
|
|
});
|
|
|
|
test("three-group varga_style A/B/C each carry full peer weight", () => {
|
|
const probe = styleProbeFrom(threeGroupStylePacket());
|
|
assert.equal(probe.choice_kind, "varga_style");
|
|
const scored = (["yes", "weak_yes", "no"] as const).map((answer) => {
|
|
const support = probe.expected_outcomes.find((row) => row.answer_class === answer)?.supports[0];
|
|
assert.ok(support, answer);
|
|
return absSupportDelta(probe, answer, support);
|
|
});
|
|
assert.deepEqual(scored, [2, 2, 2]);
|
|
});
|
|
|
|
test("old ConflictProbe receipts without choice_kind keep half-weight weak_yes", () => {
|
|
const produced = styleProbeFrom(twoGroupStylePacket());
|
|
const legacy: ConflictProbe = {
|
|
id: produced.id,
|
|
semantic_key: produced.semantic_key,
|
|
candidate_split_hash: produced.candidate_split_hash,
|
|
domain: produced.domain,
|
|
year: produced.year,
|
|
question: produced.question,
|
|
candidate_ids: produced.candidate_ids,
|
|
expected_outcomes: produced.expected_outcomes,
|
|
information_gain: produced.information_gain,
|
|
source: produced.source,
|
|
};
|
|
assert.equal(legacy.choice_kind, undefined);
|
|
|
|
const state = buildInferenceState({
|
|
range_start: "05:00",
|
|
range_end: "05:04",
|
|
candidates: [
|
|
{ id: "05:00", time: "05:00", relative_support: 34 },
|
|
{ id: "05:04", time: "05:04", relative_support: 33 },
|
|
],
|
|
events: [{ id: "e-rel", domain: "relationship", year: 2024, precision: "year" }],
|
|
probes: [legacy],
|
|
});
|
|
const loaded = asInferenceState(JSON.parse(JSON.stringify(state)));
|
|
assert.ok(loaded);
|
|
const loadedProbe = loaded.probes.find((item) => item.id === produced.id);
|
|
assert.ok(loadedProbe);
|
|
assert.equal(loadedProbe.choice_kind, undefined);
|
|
|
|
const weakGroup = loadedProbe.expected_outcomes.find((row) => row.answer_class === "weak_yes")?.supports[0];
|
|
const yesGroup = loadedProbe.expected_outcomes.find((row) => row.answer_class === "yes")?.supports[0];
|
|
assert.ok(weakGroup);
|
|
assert.ok(yesGroup);
|
|
assert.equal(absSupportDelta(loadedProbe, "weak_yes", weakGroup), 1);
|
|
assert.equal(absSupportDelta(loadedProbe, "yes", yesGroup), 2);
|
|
assert.equal(loaded.candidate_set_id, state.candidate_set_id);
|
|
assert.equal(loaded.revision, state.revision);
|
|
});
|
|
|
|
test("replaying a varga_style probe keeps candidate_set_id and a monotonic revision", () => {
|
|
const probe = styleProbeFrom(twoGroupStylePacket());
|
|
const before = buildInferenceState({
|
|
range_start: "05:00",
|
|
range_end: "05:04",
|
|
candidates: [
|
|
{ id: "05:00", time: "05:00", relative_support: 34 },
|
|
{ id: "05:04", time: "05:04", relative_support: 33 },
|
|
],
|
|
events: [{ id: "e-rel", domain: "relationship", year: 2024, precision: "year" }],
|
|
probes: [probe],
|
|
});
|
|
const after = buildInferenceState({
|
|
range_start: before.range_start,
|
|
range_end: before.range_end,
|
|
candidates: before.candidates.map((item) => ({
|
|
id: item.id,
|
|
time: item.time,
|
|
relative_support: item.prior_score,
|
|
})),
|
|
events: before.events,
|
|
probes: before.probes,
|
|
previous: before,
|
|
answered_probes: [{
|
|
probe_id: probe.id,
|
|
semantic_key: probe.semantic_key,
|
|
candidate_split_hash: probe.candidate_split_hash,
|
|
answer_class: "weak_yes",
|
|
classified_from: "choice",
|
|
}],
|
|
});
|
|
assert.equal(after.candidate_set_id, before.candidate_set_id);
|
|
assert.ok(after.revision >= before.revision);
|
|
const beforeFp = decisionStateFingerprint({
|
|
caseId: "case-style-weight",
|
|
evidenceLedgerFingerprint: "fp-a",
|
|
candidateSetId: before.candidate_set_id,
|
|
inferenceRevision: before.revision,
|
|
answeredProbeIds: before.answered_probes.map((item) => item.probe_id),
|
|
scoringPolicyVersion: "policy-v2",
|
|
});
|
|
const afterFp = decisionStateFingerprint({
|
|
caseId: "case-style-weight",
|
|
evidenceLedgerFingerprint: "fp-a",
|
|
candidateSetId: after.candidate_set_id,
|
|
inferenceRevision: after.revision,
|
|
answeredProbeIds: after.answered_probes.map((item) => item.probe_id),
|
|
scoringPolicyVersion: "policy-v2",
|
|
});
|
|
assert.notEqual(afterFp, beforeFp);
|
|
});
|