Merge pull request #79 from jesse-ux/codex/rectification-profile-completeness
fix(rectification): accept persisted birth dates
This commit is contained in:
@@ -2007,3 +2007,19 @@
|
||||
- 相关记录:BUG-112、BUG-113
|
||||
- 复发自:BUG-113
|
||||
- 修复版本:待本次 staging 修复提交与部署验收
|
||||
|
||||
## BUG-115 | ISO 出生日期被 Agentic 资料门误判并触发 opening 重试循环
|
||||
|
||||
- 状态:resolved(local)
|
||||
- 首次发现:2026-08-03
|
||||
- 最近更新:2026-08-03
|
||||
- 影响面:`POST /api/rectification/agent`、首页 Session 自动恢复、`GET /api/account` 请求频率
|
||||
- 用户现象:账户接口已返回完整出生日期、时间线索和地点,Agent opening 仍返回 `profile_incomplete`;页面随后重复请求 `/api/account` 和 `/api/rectification/agent`。
|
||||
- 触发条件:数据库驱动把出生日期投影为 `YYYY-MM-DDT00:00:00.000Z`,同时当前活动 Session 是 `birth_time_rectification`。
|
||||
- 根因:Agentic profile loader 只接受纯 `YYYY-MM-DD`;失败回调清空 `rectificationSessionId` 却未设置现有的自动恢复暂停状态,resume effect 立即重新挂载聊天并再次发送 opening。
|
||||
- 修复:在共享 profile loader 边界提取并复用现有日历日期校验;服务端资料失败时先设置 `rectificationError` 暂停自动恢复,资料成功保存后再清除暂停状态。
|
||||
- 验证:回归覆盖 ISO 日期规范化、非法日历日期拒绝,以及 profile failure 在清空 Session 前设置自动恢复暂停状态。
|
||||
- 防复发:数据库日期边界不得假设唯一 JavaScript 序列化形态;任何自动挂载请求的失败回调都必须先阻断对应的自动恢复条件。
|
||||
- 相关记录:BUG-016、BUG-114
|
||||
- 复发自:BUG-114
|
||||
- 修复版本:待本次 staging 修复提交与部署验收
|
||||
|
||||
@@ -1816,6 +1816,7 @@ export default function Home() {
|
||||
await persistProfile(profileDraft);
|
||||
setProfile(profileDraft);
|
||||
setProfileDraft(profileDraft);
|
||||
setRectificationError("");
|
||||
if (declarationChanged) {
|
||||
setBirthTimeConsultationConsent(createBirthTimeConsultationConsentState());
|
||||
setAccount((current) => current ? { ...current, rectificationCase: null } : current);
|
||||
@@ -2073,6 +2074,7 @@ export default function Home() {
|
||||
};
|
||||
|
||||
function handleRectificationProfileIncomplete() {
|
||||
setRectificationError("profile_incomplete");
|
||||
setRectificationSessionId(null);
|
||||
setRectificationPendingQuestion(null);
|
||||
const missingStep = missingProfileStep(profile);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { SupabaseClient } from "@supabase/supabase-js";
|
||||
import type { AgenticRectificationContext } from "@/mastra/rectification-tools";
|
||||
import { parseBirthDate } from "../birth-time-intake-model.ts";
|
||||
|
||||
/**
|
||||
* Agentic rectification session support.
|
||||
@@ -133,8 +134,10 @@ export async function loadAgenticRectificationProfile(
|
||||
.single();
|
||||
if (error || !data) throw new AgenticRectificationProfileError("profile_unavailable");
|
||||
|
||||
const birthDate = typeof data.birth_date === "string" ? data.birth_date.trim() : "";
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(birthDate)) {
|
||||
const persistedBirthDate = typeof data.birth_date === "string" ? data.birth_date.trim() : "";
|
||||
const birthDate = persistedBirthDate.slice(0, 10);
|
||||
if (!parseBirthDate(birthDate)
|
||||
|| (persistedBirthDate.length > 10 && persistedBirthDate[10] !== "T")) {
|
||||
throw new AgenticRectificationProfileError("missing_birth_date");
|
||||
}
|
||||
const activeTime = timeValue(data.active_birth_time);
|
||||
|
||||
@@ -41,3 +41,20 @@ test("incomplete profiles stay in the shared onboarding flow", () => {
|
||||
);
|
||||
assert.match(chat, /payload\?\.code === "profile_incomplete"/);
|
||||
});
|
||||
|
||||
test("server profile failures pause automatic rectification resume", () => {
|
||||
const resumeEffect = page.slice(
|
||||
page.indexOf('useEffect(() => {\n if (!hydrated'),
|
||||
page.indexOf('useEffect(() => {\n if (!hydrated || !accountId'),
|
||||
);
|
||||
const incompleteHandler = page.slice(
|
||||
page.indexOf("function handleRectificationProfileIncomplete"),
|
||||
page.indexOf("async function draftSynastryQuestionFromChart"),
|
||||
);
|
||||
assert.match(resumeEffect, /\|\| rectificationError\) return/);
|
||||
assert.match(incompleteHandler, /setRectificationError\("profile_incomplete"\)/);
|
||||
assert.ok(
|
||||
incompleteHandler.indexOf('setRectificationError("profile_incomplete")')
|
||||
< incompleteHandler.indexOf("setRectificationSessionId(null)"),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -61,6 +61,20 @@ test("loadAgenticRectificationProfile derives birth fields, accuracy and baselin
|
||||
assert.equal(profile.baselineActiveTime, "14:31");
|
||||
});
|
||||
|
||||
test("loadAgenticRectificationProfile normalizes a persisted ISO birth date", async () => {
|
||||
const { client } = fakeAccounting(fakeProfileRow({ birth_date: "1997-08-08T00:00:00.000Z" }));
|
||||
const profile = await loadAgenticRectificationProfile(client as never, userId);
|
||||
assert.equal(profile.birth_date, "1997-08-08");
|
||||
});
|
||||
|
||||
test("loadAgenticRectificationProfile rejects an invalid persisted birth date", async () => {
|
||||
const { client } = fakeAccounting(fakeProfileRow({ birth_date: "1997-02-30T00:00:00.000Z" }));
|
||||
await assert.rejects(
|
||||
() => loadAgenticRectificationProfile(client as never, userId),
|
||||
(error) => error instanceof AgenticRectificationProfileError && error.code === "missing_birth_date",
|
||||
);
|
||||
});
|
||||
|
||||
test("loadAgenticRectificationProfile treats hospital source as minute accuracy", async () => {
|
||||
const { client } = fakeAccounting(fakeProfileRow({
|
||||
birth_time_source: "hospital",
|
||||
|
||||
Reference in New Issue
Block a user