Files
Jyotisha/frontend/tests/rectification-server-focus.test.ts
T
Jesse_Chen 35e5781e66
Independent Staging Quality Gate / validate (push) Successful in 9m50s
Independent Staging Quality Gate / publish (push) Successful in 1m53s
fix(rectification): gate collect-phase adopt cards and keep post-adopt verify answerable
Public can_adopt follows session_outcome; reverse_verify reuses the persisted question id; offer cards settle on the owning message with a status-bar handoff.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 09:29:34 +08:00

745 lines
26 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 } from "../src/lib/rectification-agentic/core/build-state.ts";
import {
COLLECT_FOCUS_RETRY_SUFFIX,
openQuestionFromPersistedFocus,
persistableFocusDomain,
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 {
buildMethodFollowupPlan,
spokenCollectFallbackFollowup,
spokenFollowupForUser,
type MethodFollowup,
} from "../src/lib/rectification-agentic/v9/method-followup.ts";
import type { EventProbeStyleOption } from "../src/lib/rectification-agentic/v9/refinement-packet.ts";
import { fakeAccounting, CASE_ID, FOCUS_ID, USER_ID, activeFocusFixture } from "./rectification-v9-test-support.ts";
const DYNAMIC_STYLE_OPTIONS = [
{ label: "明确发生且时间吻合", answer_class: "yes" },
{ label: "发生过但程度较弱", answer_class: "weak_yes" },
{ label: "明确没有发生", answer_class: "no" },
{ label: "这段记不清楚", answer_class: "unsure" },
] as const;
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",
style_options: DYNAMIC_STYLE_OPTIONS,
}],
});
assert.ok(frame);
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,
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"] },
],
...overrides,
};
}
function invalidStyleFollowup(
styleOptions: readonly EventProbeStyleOption[] | undefined,
eventFamily = "升学结果或学习环境出现明显变化",
choiceKind: "existence" | "varga_style" | "event_quality" = "existence",
): MethodFollowup {
const valid = discriminatorFollowup();
return {
...valid,
choice_kind: choiceKind,
choice_frame: buildChoiceFrame({
method_id: "dasha_events",
ask_theme: "dated_event",
domain: "education",
user_prompt_hint: "ask",
choice_kind: choiceKind,
}, {
probes: [{
year: 2016,
year_label: "2016 年前后",
domain: "education",
event_family: eventFamily,
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",
choice_kind: choiceKind,
style_options: styleOptions,
}],
}),
};
}
async function assertUnrenderableChoiceFallsBackToSpoken(followup: MethodFollowup) {
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: null,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-31T00:00:00.000Z",
resolved_at: null,
idempotent: false,
}),
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup,
});
assert.ok(result.status === "created" || result.status === "already_open");
assert.equal(result.focus?.intent, "collect_method_evidence");
const open = openQuestionFromPersistedFocus(result);
assert.equal(open?.kind, "collect_spoken");
assert.ok(open?.prompt?.trim());
}
function persistedFocus(questionId = "probe:education:2016") {
const copy = serverOwnedChoiceCopy(discriminatorFollowup().choice_frame!);
assert.ok(copy);
return {
id: FOCUS_ID,
caseId: CASE_ID,
questionId,
intent: "distinguish_candidates",
targetEvidenceId: null,
targetDomain: "education",
targetKind: null,
expectedAnswerSchema: {
choice: copy,
semantic_key: "education:2016",
candidate_split_hash: "education:2016",
},
status: "active" as const,
askedAt: "2026-08-27T00:00:00.000Z",
resolvedAt: null,
};
}
test("only a successfully persisted focus can expose its discriminator question", () => {
const focus = persistedFocus();
for (const status of ["created", "already_open"] as const) {
assert.deepEqual(openQuestionFromPersistedFocus({
status,
focus,
questionId: focus.questionId,
prompt: "2016 年前后 · 升学结果或学习环境出现明显变化",
}), {
question_id: focus.questionId,
prompt: "2016 年前后 · 升学结果或学习环境出现明显变化",
status,
kind: "choice",
});
}
});
test("an unpersisted, mismatched, or incomplete focus cannot expose a discriminator question", () => {
const focus = persistedFocus();
assert.equal(openQuestionFromPersistedFocus({
status: "duplicate_focus",
focus,
questionId: focus.questionId,
prompt: "不能展示",
}), null);
assert.equal(openQuestionFromPersistedFocus({
status: "created",
focus: { ...focus, questionId: "another-question" },
questionId: focus.questionId,
prompt: "不能展示",
}), null);
const incomplete = openQuestionFromPersistedFocus({
status: "created",
focus: { ...focus, expectedAnswerSchema: { choice: { prompt: "不完整" } } },
questionId: focus.questionId,
prompt: "不能展示",
});
assert.equal(incomplete?.unrenderable, true);
assert.equal(incomplete?.prompt, null);
assert.equal(incomplete?.reason, "invalid_choice_schema");
});
test("complete dynamic event options persist a server-owned focus", async () => {
const followup = discriminatorFollowup({ source: "precision_stage", information_gain: 0.2 });
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: null,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-27T00:00:00.000Z",
resolved_at: null,
idempotent: false,
}),
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup,
});
assert.equal(result.status, "created");
assert.equal(accounting.calls.length, 1);
assert.deepEqual(result.focus?.expectedAnswerSchema.choice, {
prompt: "2016 年前后,有没有升学结果或学习环境出现明显变化?",
option_a: DYNAMIC_STYLE_OPTIONS[0].label,
option_b: DYNAMIC_STYLE_OPTIONS[1].label,
option_c: DYNAMIC_STYLE_OPTIONS[2].label,
option_d: DYNAMIC_STYLE_OPTIONS[3].label,
options: [
{ key: "A", label: DYNAMIC_STYLE_OPTIONS[0].label, answer_class: "yes" },
{ key: "B", label: DYNAMIC_STYLE_OPTIONS[1].label, answer_class: "weak_yes" },
{ key: "C", label: DYNAMIC_STYLE_OPTIONS[2].label, answer_class: "no" },
{ key: "D", label: DYNAMIC_STYLE_OPTIONS[3].label, answer_class: "unsure" },
],
});
});
test("non-renderable varga styles and empty event family fall back to spoken collect", async () => {
await assertUnrenderableChoiceFallsBackToSpoken(invalidStyleFollowup(
[{ label: "巨蟹相处主动热情", answer_class: "yes" }],
"升学结果或学习环境出现明显变化",
"varga_style",
));
await assertUnrenderableChoiceFallsBackToSpoken(invalidStyleFollowup(DYNAMIC_STYLE_OPTIONS, " "));
});
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("recorded event quality cannot open a zero-information scoring card", () => {
assert.equal(
shouldSkipDiscriminatorFollowup(discriminatorFollowup({
choice_kind: "event_quality",
information_gain: 0,
})),
"zero_information_gain",
);
});
test("an unmatched scoring identity is skipped instead of being synthesized", async () => {
const followup = discriminatorFollowup({
semantic_key: "education.synthetic",
candidate_split_hash: "education:synthetic",
});
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: () => {
throw new Error("must not persist an unmatched scoring focus");
},
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: {
inference_state: {
algorithm_version: "birth-time-event-scoring-v1",
candidates: [
{ id: "05:00", birth_time: "05:00", posterior: 0.5 },
{ id: "05:20", birth_time: "05:20", posterior: 0.5 },
],
answered_probes: [],
probes: [{
id: "p-live",
semantic_key: "education.live",
candidate_split_hash: "education:live",
domain: "education",
year: 2016,
question: "live",
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"] },
],
information_gain: 0.2,
source: "dasha_activation",
}],
},
},
followup,
});
assert.equal(result.status, "invalid_choice_schema");
assert.equal(accounting.calls.length, 0);
});
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");
});
test("contrast probe is not replaced by an already-answered education quality probe", async () => {
const followup = discriminatorFollowup({
domain: "education",
ask_theme: "education_style",
information_gain: 0.16,
semantic_key: "varga.d24.05:00/05:06|05:07",
candidate_split_hash: "varga.d24.05:00/05:06|05:07",
probe_year: undefined,
});
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: null,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-25T00:00:00.000Z",
resolved_at: null,
idempotent: false,
}),
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: {
inference_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: [],
probes: [{
id: "probe:education.2016",
semantic_key: "education.2016",
candidate_split_hash: "education:2016",
domain: "education",
year: 2016,
question: "发挥失常",
candidate_ids: [],
expected_outcomes: [],
information_gain: 0,
source: "known_event_quality",
}, {
id: "contrast:varga.d24.05:00/05:06|05:07",
semantic_key: "varga.d24.05:00/05:06|05:07",
candidate_split_hash: "varga.d24.05:00/05:06|05:07",
domain: "education",
year: 0,
question: "学业盘候选差异",
candidate_ids: ["05:00", "05:06", "05:07"],
expected_outcomes: [
{ answer_class: "yes", supports: ["05:00"], conflicts: ["05:06", "05:07"] },
{ answer_class: "no", supports: ["05:06", "05:07"], conflicts: ["05:00"] },
],
information_gain: 0.16,
source: "varga_contrast",
}],
}),
},
followup,
});
assert.equal(result.status, "created");
assert.ok(result.focus);
const schema = result.focus.expectedAnswerSchema;
assert.equal(schema.semantic_key, "varga.d24.05:00/05:06|05:07");
assert.notEqual(schema.probe_id, "probe:education.2016");
assert.match(String(schema.probe_id), /varga\.d24/);
});
test("spoken collect followup persists a collect focus without a choice card", async () => {
const followup: MethodFollowup = {
method_id: "relatives",
intent: "collect_method_evidence",
ask_theme: "family_event",
domain: "family",
kind_hint: "family_event",
user_prompt_hint: "collect",
must_not_label: false,
choice_frame: null,
source: "method_coverage",
probe_year: 2021,
year_label: "2021 年前后",
semantic_key: "family.2021",
};
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: null,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-27T00:00:00.000Z",
resolved_at: null,
idempotent: false,
}),
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup,
});
assert.equal(result.status, "created");
assert.equal(result.focus?.intent, "collect_method_evidence");
assert.equal(result.focus?.expectedAnswerSchema.collect, true);
assert.equal(result.focus?.expectedAnswerSchema.choice, undefined);
assert.match(String(result.focus?.expectedAnswerSchema.prompt ?? ""), /2021/);
const openCollect = openQuestionFromPersistedFocus(result);
assert.equal(openCollect?.kind, "collect_spoken");
assert.match(openCollect?.prompt ?? "", /2021/);
assert.notEqual(openCollect?.unrenderable, true);
});
test("zero-evidence opening persists the server-owned first question", async () => {
const followup = buildMethodFollowupPlan({ evidence: [] }).next_followup;
if (!followup) throw new Error("zero-evidence opening must produce a follow-up");
// 旧:开场 domain 落成 unknown,题干是 GENERIC_COLLECT_QUESTION。
// 新:开场 domain 取轮转第一个领域 education,题干走该领域采集句。
// 保留语义:仍是 collect_spoken,仍由服务端 persistAgent 不自拟开场题。
assert.equal(followup.domain, "education");
assert.doesNotMatch(stableFollowupQuestionId(followup), /unknown/);
const expected = spokenFollowupForUser(followup);
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: null,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-31T00:00:00.000Z",
resolved_at: null,
idempotent: false,
}),
});
const persisted = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup,
});
const open = openQuestionFromPersistedFocus(persisted);
assert.deepEqual({
narration: spokenFollowupForUser(followup),
persistedPrompt: persisted.focus?.expectedAnswerSchema.prompt,
openKind: open?.kind,
openPrompt: open?.prompt,
}, {
narration: expected,
persistedPrompt: expected,
openKind: "collect_spoken",
openPrompt: expected,
});
});
test("degraded spoken collect does not keep discriminator identity or block a later card", async () => {
const discriminator = discriminatorFollowup({
year_label: "2016 年前后",
probe_id: "probe:education:2016",
});
const spokenFollowup = spokenCollectFallbackFollowup(discriminator);
const spoken = spokenFollowupForUser(spokenFollowup);
assert.equal("semantic_key" in spokenFollowup, false);
assert.equal("candidate_split_hash" in spokenFollowup, false);
assert.equal("probe_year" in spokenFollowup, false);
assert.equal("year_label" in spokenFollowup, false);
assert.equal("probe_id" in spokenFollowup, false);
assert.equal(stableFollowupQuestionId(spokenFollowup).includes("education:2016"), false);
assert.doesNotMatch(spoken ?? "", /2016/);
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
id: args.p_intent === "collect_method_evidence"
? FOCUS_ID
: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: null,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-27T00:00:00.000Z",
resolved_at: null,
idempotent: false,
}),
});
const collected = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup: {
...spokenFollowup,
user_prompt_hint: spoken ?? spokenFollowup.user_prompt_hint,
},
});
assert.equal(collected.status, "created");
assert.equal(collected.questionId?.includes("education:2016"), false);
assert.equal(collected.focus?.expectedAnswerSchema.semantic_key, undefined);
assert.doesNotMatch(String(collected.focus?.expectedAnswerSchema.prompt ?? ""), /2016/);
assert.equal(collected.focus?.expectedAnswerSchema.prompt, spoken);
const openCollect = openQuestionFromPersistedFocus(collected);
assert.equal(openCollect?.kind, "collect_spoken");
assert.equal(openCollect?.prompt, spoken);
assert.notEqual(openCollect?.unrenderable, true);
const card = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: collected.focus ?? null,
decisionReceipt: null,
followup: discriminator,
});
assert.equal(card.status, "created");
assert.ok(card.focus?.expectedAnswerSchema.choice);
const open = openQuestionFromPersistedFocus(card);
assert.ok(open);
assert.notEqual(open?.unrenderable, true);
});
function collectFollowup(overrides: Partial<MethodFollowup> = {}): MethodFollowup {
return {
method_id: "dasha_events",
intent: "collect_method_evidence",
ask_theme: "dated_event",
domain: "relationship",
kind_hint: null,
user_prompt_hint: "collect",
must_not_label: false,
choice_frame: null,
source: "method_coverage",
...overrides,
};
}
function focusRowFromArgs(args: Record<string, unknown>) {
return {
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: args.p_target_evidence_id,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-31T00:00:00.000Z",
resolved_at: null,
idempotent: false,
};
}
test("collect persist maps occupation to other and health_pressure to health", async () => {
assert.equal(persistableFocusDomain("occupation"), "other");
assert.equal(persistableFocusDomain("health_pressure"), "health");
assert.equal(persistableFocusDomain("education"), "education");
assert.equal(persistableFocusDomain("horary"), "horary");
assert.equal(persistableFocusDomain(null), null);
assert.equal(persistableFocusDomain("unknown"), null);
assert.equal(stableFollowupQuestionId(collectFollowup({ domain: "unknown" })), "collect:education:collect_method_evidence");
assert.equal(stableFollowupQuestionId(collectFollowup({ domain: null })), "collect:education:collect_method_evidence");
const occupation = collectFollowup({
method_id: "occupation",
ask_theme: "occupation",
domain: "occupation",
kind_hint: "occupation_note",
});
assert.equal(stableFollowupQuestionId(occupation), "collect:occupation:collect_method_evidence");
const health = collectFollowup({
method_id: "d30_health",
ask_theme: "health_pressure",
domain: "health_pressure",
});
const generic = collectFollowup({ domain: "other" });
assert.equal(stableFollowupQuestionId(generic), "collect:other:collect_method_evidence");
assert.doesNotMatch(stableFollowupQuestionId(generic), /unknown/);
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => focusRowFromArgs(args),
});
const occupationPersisted = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup: occupation,
});
const healthPersisted = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup: health,
});
const occupationWrite = accounting.calls.find((item) => (
item.fn === "set_agentic_rectification_conversation_focus"
&& String(item.args.p_question_id).startsWith("collect:occupation:")
));
const healthWrite = accounting.calls.find((item) => (
item.fn === "set_agentic_rectification_conversation_focus"
&& String(item.args.p_question_id).startsWith("collect:health_pressure:")
));
assert.equal(occupationPersisted.status, "created");
assert.equal(healthPersisted.status, "created");
assert.equal(occupationWrite?.args.p_target_domain, "other");
assert.equal(healthWrite?.args.p_target_domain, "health");
});
test("collect focus unique conflict retries with a :next question id", async () => {
const followup = collectFollowup();
let writes = 0;
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => {
writes += 1;
if (writes === 1) throw new Error("agentic_rectification_focus_idempotency_conflict");
return focusRowFromArgs(args);
},
});
const persisted = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup,
});
assert.equal(persisted.status, "created");
assert.equal(writes, 2);
assert.equal(
persisted.questionId,
`collect:relationship:collect_method_evidence:${COLLECT_FOCUS_RETRY_SUFFIX}`,
);
const retryWrite = accounting.calls.at(-1);
assert.equal(
retryWrite?.args.p_question_id,
`collect:relationship:collect_method_evidence:${COLLECT_FOCUS_RETRY_SUFFIX}`,
);
});