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
@@ -18,6 +18,14 @@ export type NatalMinuteConsultationMode = Extract<
>;
export const UNVERIFIED_BIRTH_TIME_NOTICE = "使用未校正填报时间;分钟敏感结论的置信度已降低。";
export const HOSPITAL_REPORTED_BIRTH_TIME_NOTICE = "使用你填报的出生时间(医院记录)排盘";
export const FAMILY_EXACT_REPORTED_BIRTH_TIME_NOTICE = "使用你填报的出生时间排盘;分钟敏感结论的置信度已降低。";
export function unverifiedBirthTimeNotice(source: string | null | undefined): string {
if (source === "hospital_record") return HOSPITAL_REPORTED_BIRTH_TIME_NOTICE;
if (source === "family_exact") return FAMILY_EXACT_REPORTED_BIRTH_TIME_NOTICE;
return UNVERIFIED_BIRTH_TIME_NOTICE;
}
export function isNatalMinuteConsultationMode(
mode: ConsultationBirthTimeMode,
@@ -41,12 +49,17 @@ export function applyBirthTimeModeToWorkflowContext<
};
[key: string]: unknown;
},
>(context: T, mode: ConsultationBirthTimeMode): T {
>(
context: T,
mode: ConsultationBirthTimeMode,
options?: { birthTimeSource?: string | null },
): T {
if (mode !== "unverified_birth_time") return context;
return {
...context,
consumer_context: {
...context.consumer_context,
birth_time_notice: unverifiedBirthTimeNotice(options?.birthTimeSource),
answer_policy: {
...context.consumer_context.answer_policy,
birth_time_confidence: "unverified_reported_time",
@@ -27,6 +27,11 @@ export class ConsultationProfileTruthError extends Error {
}
}
export type DeclaredBirthAccuracy = Readonly<{
declaredAccuracy: "rectified" | "minute" | "15min";
timeSource: string;
}>;
type ServerChartToolInput = Readonly<{
year: number;
month: number;
@@ -37,6 +42,8 @@ type ServerChartToolInput = Readonly<{
lat: number;
lon: number;
tz: number;
declared_accuracy: DeclaredBirthAccuracy["declaredAccuracy"];
time_source: string;
}>;
export type ServerChartConsultation = Readonly<{
@@ -148,6 +155,30 @@ const concreteReportedSources = new Set([
"hospital_record", "family_exact", "approximate",
]);
/**
* Map persisted profile truth onto the Python rectification-gate vocabulary.
* `rectified` is reserved for an accepted or confirmed active birth time.
*/
export function declaredBirthAccuracyFromProfile(input: {
birthTimeStatus: string;
birthTimeSource: string;
hasActiveBirthTime: boolean;
}): DeclaredBirthAccuracy {
if (
(input.birthTimeStatus === "accepted" || input.birthTimeStatus === "confirmed")
&& input.hasActiveBirthTime
) {
return { declaredAccuracy: "rectified", timeSource: "rectified" };
}
if (input.birthTimeSource === "hospital_record") {
return { declaredAccuracy: "minute", timeSource: "hospital" };
}
if (input.birthTimeSource === "family_exact") {
return { declaredAccuracy: "minute", timeSource: "family_clear" };
}
return { declaredAccuracy: "15min", timeSource: "family_vague" };
}
function record(value: unknown): RecordValue | null {
return value !== null && typeof value === "object" && !Array.isArray(value)
? value as RecordValue
@@ -303,6 +334,11 @@ function serverChartFromProfile(
selectedTimeKind = "reported";
}
const [hour, minute] = selectedTime.split(":").map(Number);
const accuracy = declaredBirthAccuracyFromProfile({
birthTimeStatus,
birthTimeSource,
hasActiveBirthTime: Boolean(activeBirthTime),
});
return Object.freeze({
name,
@@ -316,6 +352,8 @@ function serverChartFromProfile(
lat: latitude,
lon: longitude,
tz: timezoneOffset,
declared_accuracy: accuracy.declaredAccuracy,
time_source: accuracy.timeSource,
}),
truth: Object.freeze({
birthDate,