Merge PR #49: pass birth-time accuracy truth into consultation engine

Reviewed: tsc + consult 295 + rectification 747 + python suites green;
guard functions and window/general instructions verified unchanged.
Conflict: BUG entry renumbered 467→468 (467 taken by report-extraction).
Review fix: test_consultation_birth_accuracy.py added to CORE_PYTEST_TARGETS
(pytest-style file outside the rectification glob would never hit the auto gate).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
This commit is contained in:
Jesse_Chen
2026-09-01 20:48:27 +00:00
co-authored by Claude Fable 5
15 changed files with 534 additions and 14 deletions
@@ -38,7 +38,7 @@ import {
const serverChart = {
name: "测试",
toolInput: { year: 1990, month: 1, day: 2, hour: 3, minute: 4, city: "台北", lat: 25.03, lon: 121.56, tz: 8 },
toolInput: { year: 1990, month: 1, day: 2, hour: 3, minute: 4, city: "台北", lat: 25.03, lon: 121.56, tz: 8, declared_accuracy: "15min" as const, time_source: "family_vague" },
truth: {
birthDate: "1990-01-02", reportedBirthTime: "03:04", activeBirthTime: null,
selectedTimeKind: "reported" as const, birthTimeSource: "reported", birthTimeStatus: "reported",
@@ -0,0 +1,287 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { prepareConsultationRoute } from "../src/lib/consultation-route-service.ts";
import {
createBirthTimeModeOutputGuard,
shouldRunBirthChartWorkflow,
shouldRunDeclaredWindowWorkflow,
UNVERIFIED_BIRTH_TIME_NOTICE,
applyBirthTimeModeToWorkflowContext,
unverifiedBirthTimeNotice,
} from "../src/lib/consultation-birth-time-mode.ts";
import {
consultationInputSchema,
runConsultationWorkflow,
toAgentConsultationContext,
} from "../src/mastra/consultation-workflow.ts";
const profile = Object.freeze({
name: "岳辰",
birth_date: "1997-08-08",
reported_birth_time: "05:30:00",
active_birth_time: "05:18:00",
birth_time_source: "approximate",
birth_time_status: "candidate",
country_code: "CN",
province_code: "130000",
city_code: "130400",
district_code: "130406",
latitude: 36.420487,
longitude: 114.209936,
timezone_offset: 8,
});
const RANGE_INTERACTION = /按时间范围|candidate windows|not_auto_rectified/;
function agentContext(rectification: Record<string, unknown>) {
return toAgentConsultationContext({
success: true,
question: "事业如何",
chart: { modules: {} },
routing: { primary_theme: "career" },
consumer_context: {
route: "career",
core_status: "ready",
available_layers: ["D1", "D9", "D10"],
missing_route_layers: [],
hard_blockers: [],
answer_policy: {
can_answer_direction: true,
can_answer_precise_timing: true,
},
},
rectification,
});
}
async function chartRoute(
mode: "verified_chart" | "unverified_birth_time",
overrides: Record<string, unknown>,
) {
return prepareConsultationRoute({
userId: "user-1",
mode,
loadProfile: async () => ({ ...profile, ...overrides }),
reserve: async () => "reserved",
});
}
test("verified_chart users send rectified accuracy and do not consume a not_auto_rectified boundary", async () => {
const prepared = await chartRoute("verified_chart", {
birth_time_status: "accepted",
birth_time_source: "approximate",
});
const toolInput = prepared.serverChart?.toolInput;
assert.equal(toolInput?.declared_accuracy, "rectified");
const parsed = consultationInputSchema.parse({
...toolInput,
question: "事业如何",
theme: "career",
entryMode: "direct_chart",
});
assert.equal(parsed.declared_accuracy, "rectified");
let requestBody = "";
const originalFetch = globalThis.fetch;
globalThis.fetch = async (_input, init) => {
requestBody = typeof init?.body === "string" ? init.body : "";
return new Response(JSON.stringify({
success: true,
chart: {},
routing: {},
consumer_context: {
route: "career",
core_status: "ready",
available_layers: [],
missing_route_layers: [],
hard_blockers: [],
answer_policy: { can_answer_direction: true, can_answer_precise_timing: true },
},
}), { status: 200 });
};
try {
await runConsultationWorkflow(parsed);
} finally {
globalThis.fetch = originalFetch;
}
assert.equal(JSON.parse(requestBody).declared_accuracy, "rectified");
const context = agentContext({
effective_accuracy: "rectified",
summary: { headline: "出生时间风险较低,可进入完整解盘" },
enabled_vargas: { D1: "enabled", D9: "enabled", D10: "enabled" },
lagna_boundary: { is_sensitive: false },
});
assert.notEqual(context.rectification.boundary, "not_auto_rectified");
assert.doesNotMatch(JSON.stringify(context), RANGE_INTERACTION);
});
test("hospital_record users send minute/hospital and never receive rectified", async () => {
const prepared = await chartRoute("unverified_birth_time", {
birth_time_source: "hospital_record",
birth_time_status: "reported",
active_birth_time: "05:18:00",
});
const toolInput = prepared.serverChart?.toolInput;
assert.equal(toolInput?.declared_accuracy, "minute");
assert.equal(toolInput?.time_source, "hospital");
assert.notEqual(toolInput?.declared_accuracy, "rectified");
const mapper = await import("../src/lib/consultation-route-service.ts");
assert.equal(typeof mapper.declaredBirthAccuracyFromProfile, "function");
assert.deepEqual(mapper.declaredBirthAccuracyFromProfile({
birthTimeStatus: "reported",
birthTimeSource: "hospital_record",
hasActiveBirthTime: true,
}), { declaredAccuracy: "minute", timeSource: "hospital" });
});
test("family_exact and approximate keep the documented downgrade without range-interaction copy", async () => {
const family = await chartRoute("unverified_birth_time", {
birth_time_source: "family_exact",
birth_time_status: "reported",
active_birth_time: null,
});
assert.equal(family.serverChart?.toolInput.declared_accuracy, "minute");
assert.equal(family.serverChart?.toolInput.time_source, "family_clear");
assert.notEqual(family.serverChart?.toolInput.declared_accuracy, "rectified");
const approximate = await chartRoute("unverified_birth_time", {
birth_time_source: "approximate",
birth_time_status: "reported",
active_birth_time: null,
});
assert.equal(approximate.serverChart?.toolInput.declared_accuracy, "15min");
assert.notEqual(approximate.serverChart?.toolInput.declared_accuracy, "rectified");
const familyContext = agentContext({
effective_accuracy: "5min",
summary: { headline: "按填报时间排盘,分钟敏感结论已标注精度" },
enabled_vargas: { D1: "enabled", D9: "enabled", D10: "enabled" },
lagna_boundary: { is_sensitive: false },
});
assert.notEqual(familyContext.rectification.boundary, "not_auto_rectified");
assert.doesNotMatch(JSON.stringify(familyContext), RANGE_INTERACTION);
const approximateContext = agentContext({
effective_accuracy: "15min",
summary: { headline: "按填报时间排盘,分钟敏感结论已标注精度" },
enabled_vargas: { D1: "enabled", D9: "enabled", D10: "enabled_with_warning" },
lagna_boundary: { is_sensitive: false },
});
assert.notEqual(approximateContext.rectification.boundary, "not_auto_rectified");
assert.doesNotMatch(JSON.stringify(approximateContext), RANGE_INTERACTION);
});
test("rectified accuracy is reserved for accepted or confirmed profiles with an active birth time", async () => {
const mapper = await import("../src/lib/consultation-route-service.ts");
const map = mapper.declaredBirthAccuracyFromProfile;
assert.equal(typeof map, "function");
assert.deepEqual(map({
birthTimeStatus: "accepted",
birthTimeSource: "approximate",
hasActiveBirthTime: true,
}), { declaredAccuracy: "rectified", timeSource: "rectified" });
assert.deepEqual(map({
birthTimeStatus: "confirmed",
birthTimeSource: "hospital_record",
hasActiveBirthTime: true,
}), { declaredAccuracy: "rectified", timeSource: "rectified" });
assert.notEqual(map({
birthTimeStatus: "accepted",
birthTimeSource: "hospital_record",
hasActiveBirthTime: false,
}).declaredAccuracy, "rectified");
assert.notEqual(map({
birthTimeStatus: "candidate",
birthTimeSource: "hospital_record",
hasActiveBirthTime: true,
}).declaredAccuracy, "rectified");
assert.notEqual(map({
birthTimeStatus: "reported",
birthTimeSource: "family_exact",
hasActiveBirthTime: true,
}).declaredAccuracy, "rectified");
});
test("declared_birth_window and general_no_birth_time never enter the natal accuracy mapper", async () => {
const windowed = await prepareConsultationRoute({
userId: "user-1",
mode: "declared_birth_window",
loadProfile: async () => ({
...profile,
reported_birth_time: null,
active_birth_time: null,
birth_time_source: "period_only",
birth_time_period: "evening",
birth_time_status: "reported",
}),
reserve: async () => "reserved",
});
assert.equal(windowed.consultationMode, "declared_birth_window");
assert.equal(windowed.serverChart, null);
assert.equal("declared_accuracy" in (windowed.declaredWindow?.toolInput ?? {}), false);
assert.equal("time_source" in (windowed.declaredWindow?.toolInput ?? {}), false);
assert.equal(shouldRunBirthChartWorkflow("declared_birth_window"), false);
assert.equal(shouldRunDeclaredWindowWorkflow("declared_birth_window"), true);
assert.equal(shouldRunBirthChartWorkflow("general_no_birth_time"), false);
assert.equal(shouldRunDeclaredWindowWorkflow("general_no_birth_time"), false);
});
test("unverified notices grade by source without changing the output guards", () => {
assert.equal(unverifiedBirthTimeNotice("hospital_record"), "使用你填报的出生时间(医院记录)排盘");
assert.equal(unverifiedBirthTimeNotice("family_exact"), "使用你填报的出生时间排盘;分钟敏感结论的置信度已降低。");
assert.equal(unverifiedBirthTimeNotice("approximate"), UNVERIFIED_BIRTH_TIME_NOTICE);
assert.equal(unverifiedBirthTimeNotice("unknown"), UNVERIFIED_BIRTH_TIME_NOTICE);
const guarded = applyBirthTimeModeToWorkflowContext({
success: true,
consumer_context: {
core_status: "ready",
answer_policy: { can_answer_direction: true, can_answer_precise_timing: true },
},
}, "unverified_birth_time", { birthTimeSource: "hospital_record" });
assert.equal(
(guarded.consumer_context as { birth_time_notice?: string }).birth_time_notice,
"使用你填报的出生时间(医院记录)排盘",
);
assert.equal(guarded.consumer_context.answer_policy.can_answer_precise_timing, true);
});
test("output guards and window/general instruction seams stay byte-stable", () => {
const modeSource = readFileSync(new URL("../src/lib/consultation-birth-time-mode.ts", import.meta.url), "utf8");
const routeSource = readFileSync(new URL("../src/app/api/consult/route.ts", import.meta.url), "utf8");
assert.match(modeSource, /if \(mode === "general_no_birth_time"\) return guardGeneralNoBirthTimeOutput\(text\)/);
assert.match(
modeSource,
/if \(mode === "declared_birth_window" \|\| !canAnswerPreciseTiming\) \{[\s\S]*return guardPreciseTimingOutput\(text\);/,
);
assert.equal(UNVERIFIED_BIRTH_TIME_NOTICE.includes("未校正填报时间"), true);
const general = createBirthTimeModeOutputGuard("general_no_birth_time", false)(
"D9 在印度占星中通常用于观察婚姻与法则层面的成熟。\n你的上升是巨蟹座。",
);
assert.match(general, /D9 在印度占星中通常用于观察婚姻与法则层面的成熟/);
assert.doesNotMatch(general, /你的上升是巨蟹座/);
const windowed = createBirthTimeModeOutputGuard("declared_birth_window", false)(
"Rahu 大运为 2013年11月21日 至 2031年11月22日。",
);
assert.match(windowed, /具体时间已省略/);
assert.match(routeSource, /function generalNoMinuteInstruction\(hasPublicDaily: boolean\) \{/);
assert.match(routeSource, /function declaredWindowInstruction\(\) \{/);
assert.match(
routeSource,
/当前没有具体出生分钟。不得计算或推断个人星盘,也不得补 00:00、时段中点或任何候选分钟。/,
);
assert.match(
routeSource,
/当前是声明出生窗口咨询,没有单一出生分钟。如需个人结构结论,必须调用 run-jyotish-window-consultation。/,
);
});
+1 -1
View File
@@ -51,7 +51,7 @@ test("keeps strength, Ashtakavarga, and timing evidence available to the answer
assert.match(workflowSource, /answer_policy: consumerContext\.answer_policy/);
assert.match(agentSource, /evidence_contract\.answer_policy/);
assert.match(agentSource, /can_answer_precise_timing/);
assert.match(workflowSource, /boundary: "not_auto_rectified"/);
assert.match(workflowSource, /function rectificationBoundaryFromGate/);
assert.match(agentSource, /rectification\.boundary=not_auto_rectified/);
assert.match(workflowSource, /external_engine_evidence:/);
assert.match(workflowSource, /runtime_truth: record\(data\.runtime_truth\)/);
@@ -58,6 +58,8 @@ test("route service loads complete server truth before billing and uses only rep
lat: 36.420487,
lon: 114.209936,
tz: 8,
declared_accuracy: "15min",
time_source: "family_vague",
});
assert.equal(prepared.serverChart?.name, "岳辰");
assert.equal(prepared.serverChart?.truth.birthTimeSource, "approximate");
@@ -156,6 +158,8 @@ test("global normalized places use their exact coordinates and historical offset
lat: 25.033,
lon: 121.5654,
tz: 9,
declared_accuracy: "15min",
time_source: "family_vague",
});
assert.equal(prepared.serverChart?.truth.placeId, "1668341");
assert.equal(prepared.serverChart?.truth.timezoneId, "Asia/Taipei");