fix(rectification): anchor candidate windows to civil dates across midnight
Independent Staging Quality Gate / validate (push) Successful in 13m27s
Independent Staging Quality Gate / publish (push) Failing after 1h0m1s

Carry explicit local date intervals instead of inferring the day from clock
order. Cluster width, delivery, adoption, and reports keep the actual civil
date; adopted date is stored separately from the reported birth_date.

Algorithm identity is scoring-9 / spec-v5. Scoring weights, confirmation
thresholds, and Skill version are unchanged. Isolated Linux final-3 gates
passed; four pre-existing Python failures remain. This is not a production
release.
This commit is contained in:
jesse-ux
2026-09-21 02:55:00 +08:00
parent 3be740f84d
commit b85c4a686a
115 changed files with 106484 additions and 315 deletions
+15 -9
View File
@@ -49,14 +49,14 @@ export async function GET() {
const admin = createAdminSupabaseClient();
let { data: profile, error: profileError } = await supabase
.from("profiles")
.select("credits,active_birth_time,birth_time_status,birth_date,reported_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,ayanamsa,name,birth_time,rectification_case_id")
.select("credits,active_birth_time,birth_time_status,birth_date,active_birth_date,active_birth_timezone_offset,active_birth_provenance,reported_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,ayanamsa,name,birth_time,rectification_case_id")
.eq("id", userId)
.single();
if (profileError && isMissingProfileColumn(profileError)) {
const withoutDeclaredWindow = await supabase
.from("profiles")
.select("credits,active_birth_time,birth_time_status,birth_date,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,name,birth_time,rectification_case_id")
.select("credits,active_birth_time,birth_time_status,birth_date,active_birth_date,active_birth_timezone_offset,active_birth_provenance,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,name,birth_time,rectification_case_id")
.eq("id", userId)
.single();
if (!withoutDeclaredWindow.error && withoutDeclaredWindow.data) {
@@ -70,7 +70,7 @@ export async function GET() {
} else if (withoutDeclaredWindow.error && isMissingProfileColumn(withoutDeclaredWindow.error)) {
const fallback = await supabase
.from("profiles")
.select("credits,active_birth_time,birth_time_status,birth_date,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,name,birth_time,rectification_case_id")
.select("credits,active_birth_time,birth_time_status,birth_date,active_birth_date,active_birth_timezone_offset,active_birth_provenance,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,name,birth_time,rectification_case_id")
.eq("id", userId)
.single();
profile = fallback.data ? {
@@ -191,13 +191,13 @@ export async function PATCH(request: Request) {
const admin = createAdminSupabaseClient();
let { data: currentProfile, error: currentProfileError } = await admin
.from("profiles")
.select("birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source")
.select("birth_date,active_birth_date,active_birth_timezone_offset,active_birth_provenance,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source")
.eq("id", userId)
.maybeSingle();
if (currentProfileError && isMissingProfileColumn(currentProfileError)) {
const withoutDeclaredWindow = await admin
.from("profiles")
.select("birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source")
.select("birth_date,active_birth_date,active_birth_timezone_offset,active_birth_provenance,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source")
.eq("id", userId)
.maybeSingle();
if (!withoutDeclaredWindow.error && withoutDeclaredWindow.data) {
@@ -210,7 +210,7 @@ export async function PATCH(request: Request) {
} else if (withoutDeclaredWindow.error && isMissingProfileColumn(withoutDeclaredWindow.error)) {
const fallback = await admin
.from("profiles")
.select("birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code")
.select("birth_date,active_birth_date,active_birth_timezone_offset,active_birth_provenance,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code")
.eq("id", userId)
.maybeSingle();
currentProfile = fallback.data ? {
@@ -292,14 +292,14 @@ export async function PATCH(request: Request) {
return admin
.from("profiles")
.upsert(values, { onConflict: "id" })
.select("id")
.select("id,birth_time_status,active_birth_time,active_birth_date,active_birth_timezone_offset")
.maybeSingle();
}
let query = admin.from("profiles").update(values).eq("id", userId);
if (invalidatesUnconfirmedApplication) {
query = applyAccountProfileConcurrencyGuards(query, currentProfile);
}
return query.select("id").maybeSingle();
return query.select("id,birth_time_status,active_birth_time,active_birth_date,active_birth_timezone_offset").maybeSingle();
}
let { data, error } = await writeProfile(withCoordinates);
@@ -345,7 +345,13 @@ export async function PATCH(request: Request) {
return NextResponse.json({
ok: true,
birthTime: resolveAppliedAccountBirthTime(currentProfile, applicationPatch),
birthTime: {
...resolveAppliedAccountBirthTime(currentProfile, applicationPatch),
status: data.birth_time_status,
activeTime: data.active_birth_time,
activeDate: data.active_birth_date ?? null,
activeTimezoneOffset: data.active_birth_timezone_offset ?? null,
},
});
} catch (error) {
if (isSupabaseConfigurationError(error)) {
+1 -1
View File
@@ -396,7 +396,7 @@ export async function POST(request: Request) {
async loadProfile(profileUserId) {
const { data, error } = await supabase
.from("profiles")
.select("name,birth_date,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_status,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,ayanamsa")
.select("name,birth_date,active_birth_date,active_birth_timezone_offset,active_birth_provenance,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_status,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,ayanamsa")
.eq("id", profileUserId)
.single();
if (error || !data) throw new ConsultationProfileTruthError("profile_unavailable");
@@ -6,6 +6,7 @@ import { withRectificationRequestCache } from "@/lib/rectification-agentic/v9/re
import { isProductEnabled } from "@/lib/product-access";
import { createServerSupabaseClient } from "@/lib/supabase/server";
import { RectificationToolServiceError } from "@/lib/rectification-agentic/v9/tool-service";
import { parseSavedCivilFields } from "@/lib/rectification-agentic/v9/saved-civil-fields";
export const runtime = "nodejs";
@@ -159,6 +160,7 @@ export async function POST(request: Request, context: RouteContext) {
return NextResponse.json({ error: "暂时无法采用该候选时间", code: "candidate_accept_rejected" }, { status: 409 });
}
const result = row as Record<string, unknown>;
const civil = parseSavedCivilFields(result);
try {
const { persistNextInterviewIfIdle } = await import(
"@/lib/rectification-agentic/v9/answer-choice"
@@ -174,6 +176,12 @@ export async function POST(request: Request, context: RouteContext) {
return NextResponse.json({
ok: true,
saved_time: result.saved_time,
...(civil.savedDate ? {
saved_date: civil.savedDate,
saved_timezone_offset: civil.savedTimezoneOffset,
day_offset: civil.dayOffset,
date_changed: civil.dateChanged,
} : {}),
status: "accepted",
result_id: result.result_id,
case_status: result.case_status,
+3 -2
View File
@@ -1,4 +1,5 @@
import { NextResponse } from "next/server";
import { resolveMissingBirthTimezoneOffset } from "@/lib/birth-profile-timezone";
import { runConsultationWorkflow } from "@/mastra";
import { createPersonalReportAgent } from "@/mastra/personal-report";
import { loadLanguageModelCatalog } from "@/lib/model-catalog";
@@ -170,10 +171,10 @@ export async function POST(request: Request) {
if (userId) {
const result = await supabase
.from("profiles")
.select("name,birth_date,reported_birth_time,active_birth_time,birth_time_source,birth_time_status,rectification_case_id,declared_window_start,declared_window_end,uncertainty_before_minutes,uncertainty_after_minutes,latitude,longitude,timezone_offset,birth_place_label,ayanamsa")
.select("name,birth_date,active_birth_date,active_birth_timezone_offset,active_birth_provenance,reported_birth_time,active_birth_time,birth_time_source,birth_time_status,rectification_case_id,declared_window_start,declared_window_end,uncertainty_before_minutes,uncertainty_after_minutes,latitude,longitude,timezone_offset,birth_place_label,ayanamsa")
.eq("id", userId)
.maybeSingle();
profile = result.data ?? null;
profile = result.data ? await resolveMissingBirthTimezoneOffset(result.data, { useActiveDate: true }) : null;
profileError = result.error;
}