fix(account): answer profile writes with the derived birth-time truth
A zero-uncertainty exact declaration is accepted server-side as the active
minute, but the account write only answered {ok:true}. Every save path then
kept the draft it submitted, so the first consultation after initialization
asked for unverified_birth_time against an accepted profile and was rejected
with mode_changed before billing.
The account route now returns the status and active minute it derived, and
every profile save adopts that result instead of its own local guess.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -304,3 +304,30 @@ export function resolveAccountBirthTimeApplicationPatch(
|
||||
rectification_case_id: null,
|
||||
};
|
||||
}
|
||||
|
||||
export type AppliedAccountBirthTime = Readonly<{
|
||||
status: string | null;
|
||||
activeTime: string | null;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* The birth-time truth the account owns after a successful write. Status and
|
||||
* active minute are derived server-side, so a caller that keeps the declaration
|
||||
* it submitted would consult under a mode the server no longer accepts.
|
||||
*/
|
||||
export function resolveAppliedAccountBirthTime(
|
||||
current: AccountBirthTimeState | null,
|
||||
applicationPatch: AccountBirthTimeApplicationPatch,
|
||||
): AppliedAccountBirthTime {
|
||||
const activeTime = normalizeApplicableBirthClock(
|
||||
applicationPatch.active_birth_time !== undefined
|
||||
? applicationPatch.active_birth_time
|
||||
: current?.active_birth_time ?? current?.birth_time,
|
||||
);
|
||||
return Object.freeze({
|
||||
status: applicationPatch.birth_time_status
|
||||
?? current?.birth_time_status
|
||||
?? (activeTime ? "confirmed" : null),
|
||||
activeTime,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,6 +326,31 @@ export function birthTimePersistenceValues(draft: BirthTimeDraft) {
|
||||
};
|
||||
}
|
||||
|
||||
const persistedBirthTimeStatuses = [
|
||||
"reported", "assessing", "rectifying", "candidate", "accepted", "confirmed",
|
||||
] as const satisfies readonly Exclude<BirthTimeStatus, "">[];
|
||||
|
||||
/**
|
||||
* Reconciles a submitted declaration with the birth-time truth the account write
|
||||
* returned. The server decides whether a declaration is already usable as the
|
||||
* active minute, so consultation mode must never be derived from the draft alone.
|
||||
*/
|
||||
export function applyPersistedBirthTime<T extends BirthTimeDraft>(
|
||||
draft: T,
|
||||
applied: unknown,
|
||||
): T {
|
||||
if (applied === null || typeof applied !== "object") return draft;
|
||||
const { status, activeTime } = applied as { status?: unknown; activeTime?: unknown };
|
||||
const persistedStatus = persistedBirthTimeStatuses.find((candidate) => candidate === status);
|
||||
if (!persistedStatus) return draft;
|
||||
const clock = typeof activeTime === "string" ? activeTime.slice(0, 5) : "";
|
||||
return {
|
||||
...draft,
|
||||
time: isBirthClockTime(clock) ? clock : "",
|
||||
birthTimeStatus: persistedStatus,
|
||||
};
|
||||
}
|
||||
|
||||
export function describeBirthTimeDraft(draft: BirthTimeDraft) {
|
||||
const [year, month, day] = draft.date.split("-").map(Number);
|
||||
const date = `${year}年${month}月${day}日`;
|
||||
|
||||
Reference in New Issue
Block a user