import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { parseRectificationChoiceCard } from "../src/lib/rectification-agentic/v9/choice-card.ts"; import { isRenderableChoiceOpenQuestion, openQuestionFromPersistedFocus, persistServerOwnedFocus, stableFollowupQuestionId, } from "../src/lib/rectification-agentic/v9/server-focus.ts"; import { projectTurnDecision } from "../src/lib/rectification-agentic/v9/turn-decision.ts"; import { spokenCollectFallbackFollowup } from "../src/lib/rectification-agentic/v9/method-followup.ts"; import type { MethodFollowup } from "../src/lib/rectification-agentic/v9/method-followup.ts"; import { RECTIFICATION_SKILL_VERSION } from "../src/lib/rectification-agentic/v9/case-status.ts"; import { CASE_ID, EVIDENCE_ID, FOCUS_ID, TURN_ID, USER_ID, activeFocusFixture, candidateSnapshotFixture, conversationSummaryFixture, dossierFixture, fakeAccounting, } from "./rectification-v9-test-support.ts"; import { parseV9CaseDossier as parseDossier } from "../src/lib/rectification-agentic/v9/tool-service.ts"; import { USER_COLLECT_QUESTION } from "../src/lib/rectification-agentic/user-copy.ts"; const RELATIONSHIP_PROMPT = USER_COLLECT_QUESTION.relationship; const COLLECT_GET_QUESTION = { question_id: "active_focus:active_focus", kind: "collect_spoken", intent: "collect_method_evidence", domain: "relationship", prompt: RELATIONSHIP_PROMPT, }; function collectFollowup(overrides: Partial = {}): MethodFollowup { return { method_id: "active_focus", intent: "collect_method_evidence", ask_theme: "active_focus", domain: "relationship", kind_hint: null, user_prompt_hint: "collect", must_not_label: false, choice_frame: null, source: "active_focus", ...overrides, }; } function collectPersistResult(overrides: { questionId?: string; prompt?: string; schema?: Record; domain?: string | null; } = {}) { const questionId = overrides.questionId ?? "collect:relationship:collect_method_evidence"; const prompt = overrides.prompt ?? RELATIONSHIP_PROMPT; return { status: "created" as const, questionId, prompt, focus: { id: FOCUS_ID, caseId: CASE_ID, questionId, intent: "collect_method_evidence", targetEvidenceId: null, targetDomain: overrides.domain ?? "relationship", targetKind: null, expectedAnswerSchema: overrides.schema ?? { prompt, collect: true, }, status: "active" as const, askedAt: "2026-08-30T00:00:00.000Z", resolvedAt: null, }, }; } test("skill version stays 10.0.14 for the spoken-collect visibility fix", () => { assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.14"); }); test("cases current_question drives the unified question slot", () => { const chat = readFileSync(new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url), "utf8"); assert.match(chat, /current_question\?: unknown/); assert.match(chat, /setCurrentQuestion\(nextQuestion\)/); assert.match(chat, /className="rectification-question-slot"/); assert.match(chat, /isLatestMessage/); assert.match(chat, /currentQuestion\?\.kind === "choice"/); assert.match(chat, / { const chat = readFileSync(new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url), "utf8"); const styles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); assert.match(chat, /currentQuestion\?\.kind === "collect_spoken"/); assert.match(chat, /const collectSpokenPrompt =/); assert.match(chat, /collectSpokenPrompt\n\s+\? "请回答上面的问题…"/); assert.match(chat, /rectification-question-slot__prompt/); // Former lock: `aria-describedby={collectSpokenPrompt` on the inline