123 lines
4.5 KiB
TypeScript
123 lines
4.5 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import {
|
|
createInitialJourneyTurn,
|
|
deriveNextAction,
|
|
projectJourneyTurn,
|
|
} from "../src/lib/birth-time-journey-turn.ts";
|
|
import type { CandidateResult } from "../src/lib/birth-time-evidence.ts";
|
|
import type { QuestionSpec } from "../src/lib/birth-time-question-planner.ts";
|
|
|
|
function question(domain: QuestionSpec["domain"]): QuestionSpec {
|
|
return {
|
|
questionId: `baseline_${domain}_1`,
|
|
phase: "baseline",
|
|
domain,
|
|
requestedPrecision: ["year", "month"],
|
|
allowUnknown: true,
|
|
purposeCode: `candidate_difference_${domain}`,
|
|
plannerVersion: "candidate-difference-v1",
|
|
};
|
|
}
|
|
|
|
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: ["Candidate scores remain close."],
|
|
evidence: [],
|
|
algorithmVersion: "birth-time-event-scoring-v1",
|
|
};
|
|
|
|
test("a fresh rectification turn asks exactly one baseline evidence question", () => {
|
|
const turn = createInitialJourneyTurn(question("career"));
|
|
|
|
assert.equal(turn.nextAction.kind, "ask_baseline_evidence");
|
|
assert.equal(turn.progress.confirmedEvidenceCount, 0);
|
|
assert.equal(turn.progress.maxAdaptiveRounds, 3);
|
|
assert.equal(turn.permissions.canConfirmCandidate, false);
|
|
});
|
|
|
|
test("the third low adaptive result becomes terminal", () => {
|
|
const next = deriveNextAction({
|
|
progress: {
|
|
phase: "adaptive",
|
|
baselineDomainCount: 3,
|
|
confirmedEvidenceCount: 6,
|
|
adaptiveRound: 3,
|
|
maxAdaptiveRounds: 3,
|
|
},
|
|
candidateResult: lowResult,
|
|
nextQuestion: question("health_pressure"),
|
|
confirmedTime: null,
|
|
});
|
|
|
|
assert.equal(next.kind, "present_low_result");
|
|
});
|
|
|
|
test("projection skips confirmed evidence domains when planning the next baseline question", () => {
|
|
const careerEvents = [
|
|
{ id: "5cb071d6-6d99-46be-85dc-a9bf59ef6ac5", domain: "career", date: "2011", precision: "year" },
|
|
{ id: "0790866c-ad5e-4a45-b2b4-a5c73f6be6ea", domain: "career", date: "2019", precision: "year" },
|
|
{ id: "0ef52e51-ab5f-453b-81e5-adb44a929224", domain: "career", date: "2021", precision: "year" },
|
|
] as const;
|
|
const turn = projectJourneyTurn({
|
|
turnVersion: 0,
|
|
snapshot: {
|
|
state: "rectifying", assistantIntent: "continue_rectification_questions", input: "rectification_questions",
|
|
route: "rectification", confidence: null, canApply: false, activeTime: null,
|
|
reportedRange: { label: "14:00—15:00", startTime: "14:00", endTime: "15:00" },
|
|
},
|
|
questionnaire: { samples: [
|
|
{ d4Sign: "Aries", d9Sign: "Aries", d10Sign: "Aries", d24Sign: "Aries", d30Sign: "Aries" },
|
|
{ d4Sign: "Aries", d9Sign: "Aries", d10Sign: "Taurus", d24Sign: "Aries", d30Sign: "Aries" },
|
|
{ d4Sign: "Aries", d9Sign: "Aries", d10Sign: "Gemini", d24Sign: "Aries", d30Sign: "Aries" },
|
|
] },
|
|
persistedProgress: { adaptiveRound: 0, askedDomains: [] },
|
|
candidateResult: null,
|
|
lifeEvents: careerEvents,
|
|
});
|
|
|
|
assert.equal(turn.nextAction.kind, "ask_baseline_evidence");
|
|
assert.equal(turn.nextAction.question.domain, "education");
|
|
});
|
|
|
|
test("a low result becomes terminal when adaptive question planning is exhausted early", () => {
|
|
const next = deriveNextAction({
|
|
progress: {
|
|
phase: "adaptive", baselineDomainCount: 3, confirmedEvidenceCount: 6, adaptiveRound: 1, maxAdaptiveRounds: 3,
|
|
},
|
|
candidateResult: lowResult,
|
|
nextQuestion: null,
|
|
});
|
|
|
|
assert.deepEqual(next, { kind: "present_low_result", resultId: lowResult.resultId });
|
|
});
|
|
|
|
test("projection pauses completed legacy evidence without fabricating a draft", () => {
|
|
const turn = projectJourneyTurn({
|
|
turnVersion: 0,
|
|
snapshot: {
|
|
state: "rectifying", assistantIntent: "continue_rectification_questions", input: "rectification_questions",
|
|
route: "rectification", confidence: null, canApply: false, activeTime: null,
|
|
reportedRange: { label: "14:00—15:00", startTime: "14:00", endTime: "15:00" },
|
|
},
|
|
questionnaire: { samples: [] },
|
|
candidateResult: null,
|
|
lifeEvents: [
|
|
{ id: "5cb071d6-6d99-46be-85dc-a9bf59ef6ac5", domain: "education", date: "2011", precision: "year" },
|
|
{ id: "0790866c-ad5e-4a45-b2b4-a5c73f6be6ea", domain: "career", date: "2019", precision: "year" },
|
|
{ id: "0ef52e51-ab5f-453b-81e5-adb44a929224", domain: "relationship", date: "2021", precision: "year" },
|
|
],
|
|
});
|
|
|
|
assert.equal(turn.nextAction.kind, "paused");
|
|
assert.equal(turn.evidenceDraft, null);
|
|
});
|