Review-only snapshot for BUG-1115 through BUG-1117; not merge-ready. New opening and append-turn PostgreSQL permission failures remain blocked. Persisted joint replay has zero completed questions; segment ordering remains off by default. The existing offline replay JSON is retained stale and unchanged after a denied overwrite, including its CRLF line endings. Browser/provider validation and final serial gates remain pending. No deployment, role permission changes, or staging/main push. Co-Authored-By: Claude Code <noreply@anthropic.com>
63 lines
4.6 KiB
TypeScript
63 lines
4.6 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { dossierResponse } from "../src/lib/rectification-agentic/v9/case-dossier-response.ts";
|
|
import { choiceCardFromCaseDossier } from "../src/lib/rectification-agentic/v9/interview-state.ts";
|
|
import { buildSegmentSummary } from "../src/lib/rectification-agentic/core/segment-summary.ts";
|
|
import { parseV9CaseDossier } from "../src/lib/rectification-agentic/v9/tool-service.ts";
|
|
import { dossierFixture, activeFocusFixture, conversationSummaryFixture } from "./rectification-v9-test-support.ts";
|
|
import { AA_RANGE, buildGoldenLatest } from "./rectification-grounding-support.ts";
|
|
|
|
// Real public AA score for identity checks; explicitly fictional focus/unique
|
|
// controls below verify projection only, not engine calibration or SQL.
|
|
test("actual dossier projection preserves read-only gates and hides collect cards only for delivered consistency", async () => {
|
|
const { latest, scored } = await buildGoldenLatest();
|
|
const raw = dossierFixture({ candidateRange: AA_RANGE, latestResult: latest,
|
|
conversationSummary: conversationSummaryFixture({ activeFocus: activeFocusFixture({ intent: "collect_method_evidence", targetKind: "career_change",
|
|
expectedAnswerSchema: { collect: true, prompt: "请说出第一次工作变化发生的年份。" } }) }) });
|
|
const dossier = parseV9CaseDossier(raw)!;
|
|
const identity = { status: "verified", requiresSkillAdoption: false } as never;
|
|
const live = { algorithmVersion: scored.algorithmVersion, policyVersion: scored.policyVersion };
|
|
const writable = dossierResponse(dossier, [], identity, { liveIdentity: live });
|
|
assert.equal(writable.case.result_identity.read_only, false);
|
|
assert.ok(writable.current_question);
|
|
assert.deepEqual(writable.choice_card, choiceCardFromCaseDossier(dossier), "normal projection keeps the production tap/time mutual-exclusion decision");
|
|
const tapRaw = dossierFixture({ candidateRange: AA_RANGE, latestResult: latest,
|
|
conversationSummary: conversationSummaryFixture({ activeFocus: activeFocusFixture({
|
|
intent: "collect_method_evidence", questionId: "collect:targeted:career", targetKind: "targeted:career",
|
|
expectedAnswerSchema: { collect: true, prompt: "工作这条线有没有发生过明显变化?" },
|
|
}) }) });
|
|
const tapDossier = parseV9CaseDossier(tapRaw)!;
|
|
const tap = dossierResponse(tapDossier, [], identity, { liveIdentity: live });
|
|
assert.ok(tap.current_question);
|
|
assert.ok(tap.choice_card, "the actual production projection must retain a valid persisted collect tap card");
|
|
assert.equal(tap.choice_card.scoring, false);
|
|
const readOnlyTap = dossierResponse(tapDossier, [], identity, { liveIdentity: { ...live, algorithmVersion: "synthetic-old-version" } });
|
|
assert.equal(readOnlyTap.current_question, null);
|
|
assert.equal(readOnlyTap.choice_card, null, "read-only removes a proven renderable card, not just an already-null fixture");
|
|
const readOnly = dossierResponse(dossier, [], identity, { liveIdentity: { ...live, algorithmVersion: "synthetic-old-version" } });
|
|
assert.equal(readOnly.case.result_identity.read_only, true);
|
|
assert.equal(readOnly.current_question, null);
|
|
assert.equal(readOnly.choice_card, null);
|
|
assert.equal(readOnly.latest_result?.can_adopt, false);
|
|
assert.equal(readOnly.latest_result?.confirmationAllowed, false);
|
|
|
|
const range = { start_time: "09:00", end_time: "09:20" };
|
|
const minutes = Array.from({ length: 21 }, (_, offset) => ({ offset, date: "2000-06-14", time: `09:${String(offset).padStart(2, "0")}`,
|
|
signs: { D1: 1, D9: 2, D10: 3 } }));
|
|
const summary = buildSegmentSummary({ minutes, candidates: [], targets: ["D1", "D9", "D10"], windowMinutes: 21, scanComplete: true });
|
|
const closedRaw = dossierFixture({ candidateRange: range, status: "closed", latestResult: null,
|
|
conversationSummary: raw.conversation_summary });
|
|
Object.assign(closedRaw.case, { rectification_domain: "general",
|
|
checks: { contract: "segment-opening-v1", domain: "general", range_fingerprint: "a".repeat(64),
|
|
status: "complete", summary, delivered_turn_id: "synthetic-opening-turn" } });
|
|
const closed = parseV9CaseDossier(closedRaw)!;
|
|
const projected = dossierResponse(closed, [], identity, { liveIdentity: live });
|
|
assert.equal(projected.current_question, null);
|
|
assert.equal(projected.choice_card, null);
|
|
assert.equal(projected.latest_result, null);
|
|
assert.equal(projected.step_state.headline, "盘型一致,不用校正");
|
|
assert.equal(projected.next_user_action.id, "start_consultation");
|
|
assert.equal(projected.case.accepted_time, null);
|
|
assert.equal(projected.case.confirmed_time, null);
|
|
});
|