import assert from "node:assert/strict"; import test from "node:test"; import { parseRectificationScoring } from "../src/lib/birth-time-journey-adapters.ts"; import { projectJourneyTurn } from "../src/lib/birth-time-journey-turn.ts"; import type { CandidateResult } from "../src/lib/birth-time-evidence.ts"; const snapshot = { state: "rectifying", assistantIntent: "continue_rectification_questions", input: "rectification_questions", route: "rectification", confidence: "low", canApply: false, activeTime: null, reportedRange: { label: "10:00—11:00", startTime: "10:00", endTime: "11:00" }, } as const; const lowResult: CandidateResult = { resultId: "1d8ee348-61a3-433d-8907-ff6d281b9992", confidence: "low", canApply: false, winningSegment: null, eventCount: 3, domainCount: 3, topScore: 8, secondScore: 7, marginPercent: 12.5, reasons: [], evidence: [], algorithmVersion: "birth-time-event-scoring-v1", }; const questionnaire = { samples: [ { d4Sign: "Aries", d9Sign: "Leo", d10Sign: "Virgo", d24Sign: "Gemini", d30Sign: "Pisces" }, { d4Sign: "Aries", d9Sign: "Leo", d10Sign: "Libra", d24Sign: "Gemini", d30Sign: "Pisces" }, ] } as const; const lifeEvents = [ { id: "5cb071d6-6d99-46be-85dc-a9bf59ef6ac5", domain: "education", date: "2011", precision: "year" }, { id: "0790866c-ad5e-4a45-b2b4-a5c73f6be6ea", domain: "relocation", date: "2019", precision: "year" }, { id: "0ef52e51-ab5f-453b-81e5-adb44a929224", domain: "health_pressure", date: "2021", precision: "year" }, ] as const; test("scoring adapter prefers the server-selected next question", () => { const scoring = parseRectificationScoring({ answered_count: 1, candidate_cluster_rankings: [], next_round: 2, next_round_questions: [{ id: "fallback", domain: "career", prompt: "fallback" }], next_round_selection: { selected_questions: [{ id: "selected", domain: "relationship", prompt: "selected" }], }, }); assert.equal(scoring.nextRoundQuestions[0]?.id, "selected"); assert.equal(scoring.nextRoundQuestions[0]?.domain, "relationship"); }); test("adaptive projection prefers a valid server-selected question", () => { const turn = projectJourneyTurn({ turnVersion: 1, snapshot, questionnaire, persistedProgress: { adaptiveRound: 0, askedDomains: [] }, candidateResult: lowResult, serverSelectedQuestion: { id: "relationship_followup", domain: "relationship" }, lifeEvents, }); assert.equal(turn.nextAction.kind, "ask_adaptive_evidence"); if (turn.nextAction.kind === "ask_adaptive_evidence") { assert.equal(turn.nextAction.question.questionId, "relationship_followup"); assert.equal(turn.nextAction.question.domain, "relationship"); } }); test("invalid server domains fall back locally and baseline stays local", () => { const adaptive = projectJourneyTurn({ turnVersion: 1, snapshot, questionnaire, persistedProgress: { adaptiveRound: 0, askedDomains: [] }, candidateResult: lowResult, serverSelectedQuestion: { id: "invalid", domain: "unsupported" }, lifeEvents, }); assert.equal(adaptive.nextAction.kind, "ask_adaptive_evidence"); if (adaptive.nextAction.kind === "ask_adaptive_evidence") { assert.equal(adaptive.nextAction.question.domain, "career"); } const baseline = projectJourneyTurn({ turnVersion: 0, snapshot: { ...snapshot, confidence: null }, questionnaire, persistedProgress: { adaptiveRound: 0, askedDomains: [] }, candidateResult: null, serverSelectedQuestion: { id: "relationship_followup", domain: "relationship" }, lifeEvents: lifeEvents.slice(0, 1), }); assert.equal(baseline.nextAction.kind, "ask_baseline_evidence"); if (baseline.nextAction.kind === "ask_baseline_evidence") { assert.equal(baseline.nextAction.question.domain, "career"); } });