Files
Jyotisha/frontend/tests/rectification-server-focus.test.ts
T
Jesse_Chen fe87a9ecdb fix(web): isolate rectification answers from tool-step planning text
Mastra intermediate text-delta was published as answer.delta, then set-focus domain errors reset the attempt and replayed evidence. Publish only the terminal no-tool step, persist the next probe on the server, and ground batch quotes in the source turn.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 21:26:57 +08:00

112 lines
3.7 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { persistServerOwnedFocus, shouldSkipDiscriminatorFollowup, stableFollowupQuestionId } from "../src/lib/rectification-agentic/v9/server-focus.ts";
import { buildChoiceFrame, serverOwnedChoiceCopy } from "../src/lib/rectification-agentic/v9/choice-card.ts";
import type { MethodFollowup } from "../src/lib/rectification-agentic/v9/method-followup.ts";
import { fakeAccounting, CASE_ID, FOCUS_ID, USER_ID, activeFocusFixture } from "./rectification-v9-test-support.ts";
function discriminatorFollowup(overrides: Partial<MethodFollowup> = {}): MethodFollowup {
const frame = buildChoiceFrame({
method_id: "dasha_events",
ask_theme: "dated_event",
domain: "education",
user_prompt_hint: "ask",
}, {
probes: [{
year: 2016,
year_label: "2016 年前后",
domain: "education",
event_family: "高考或重要考试发挥明显失常",
source: "dasha_activation",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "2016 年前后高考或重要考试发挥明显失常",
role: "distinguish",
information_gain: 0.4,
semantic_key: "education:2016",
}],
});
return {
method_id: "dasha_events",
intent: "distinguish_candidates",
ask_theme: "dated_event",
domain: "education",
kind_hint: null,
user_prompt_hint: "ask",
must_not_label: false,
choice_frame: frame,
source: "event_probe",
information_gain: 0.4,
semantic_key: "education:2016",
probe_year: 2016,
...overrides,
};
}
test("does not ask an already-answered discriminator probe again", async () => {
const followup = discriminatorFollowup();
const active = activeFocusFixture({
expectedAnswerSchema: { probe_id: "education:2016", choice: serverOwnedChoiceCopy(followup.choice_frame!) },
});
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: () => {
throw new Error("should not create a second focus");
},
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: {
...active,
id: FOCUS_ID,
caseId: CASE_ID,
questionId: stableFollowupQuestionId(followup),
intent: "distinguish_candidates",
targetEvidenceId: null,
targetDomain: "education",
targetKind: null,
expectedAnswerSchema: { probe_id: "education:2016" },
status: "active",
askedAt: "2026-08-24T00:00:00.000Z",
resolvedAt: null,
},
decisionReceipt: {
inference_state: {
answered_probes: [{ probe_id: "education:2016", semantic_key: "education:2016", answer_class: "yes" }],
probes: [],
},
},
followup,
});
assert.equal(result.status, "probe_already_answered");
assert.equal(accounting.calls.length, 0);
});
test("zero information gain does not open a discriminator", () => {
assert.equal(
shouldSkipDiscriminatorFollowup(discriminatorFollowup({ information_gain: 0 })),
"zero_information_gain",
);
});
test("duplicate focus conflict does not throw", async () => {
const followup = discriminatorFollowup({ source: "precision_stage", information_gain: 0.2 });
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: () => {
throw new Error("agentic_rectification_focus_idempotency_conflict");
},
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup,
});
assert.equal(result.status, "duplicate_focus");
});