import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { PROBE_EXPLAIN_COPY } from "../src/lib/rectification-agentic/v9/probe-explain.ts"; import { projectRectificationStepState, STEP_STATE_COPY } from "../src/lib/rectification-agentic/v9/step-state.ts"; import { parseRectificationStepState } from "../src/lib/rectification-candidate-result.ts"; test("step state covers collect, discriminate, deliver, and post-adopt", () => { const collect = projectRectificationStepState({ nextAction: "ask_fact_collection", methodsCovered: false, }); assert.equal(collect.stage, "collect"); assert.equal(collect.index, 1); assert.match(collect.headline, /第 1 步/); const discriminate = projectRectificationStepState({ nextAction: "ask_candidate_discriminator", methodsCovered: true, }); assert.equal(discriminate.stage, "discriminate"); assert.equal(discriminate.index, 2); assert.match(discriminate.next, /先这样/); const exhausted = projectRectificationStepState({ nextAction: "complete_with_range", methodsCovered: true, stopReason: "probe_pool_exhausted", }); assert.equal(exhausted.stage, "deliver"); assert.match(exhausted.reason, /已经问完/); const uncertain = projectRectificationStepState({ nextAction: "complete_with_range", stopReason: "user_uncertainty_too_high", }); assert.equal(uncertain.stage, "deliver"); assert.match(uncertain.reason, /说不好/); const adopted = projectRectificationStepState({ accepted: true, nextAction: "ask_holdout_validation", }); assert.equal(adopted.stage, "post_adopt"); assert.equal(adopted.index, 4); }); test("step state parser rejects forbidden certainty words", () => { const parsed = parseRectificationStepState(projectRectificationStepState({ nextAction: "offer_provisional_range", })); assert.ok(parsed); const blob = `${parsed.headline}${parsed.reason}${parsed.next}`; assert.doesNotMatch(blob, /概率|置信度|确定/); }); test("chat renders rectification-step-state above the composer", () => { const chat = readFileSync(new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url), "utf8"); const route = readFileSync(new URL("../src/app/api/rectification/cases/[caseId]/route.ts", import.meta.url), "utf8"); assert.match(chat, /rectification-step-state/); assert.match(chat, /rectification-composer-meta/); assert.match(route, /step_state: stepStateFromCaseDossier/); }); test("explain-layer copy avoids probability words", () => { const blob = [ ...Object.values(PROBE_EXPLAIN_COPY), ...Object.values(STEP_STATE_COPY).flatMap((item) => Object.values(item)), ].join("\n"); assert.doesNotMatch(blob, /概率|置信度|确定/); });