From 075c62e579305ea4f16737623b325f8221fdb408 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Sat, 15 Aug 2026 17:50:51 +0800 Subject: [PATCH] fix(rectification): reset fresh cases to reported time --- docs/BUG_HISTORY.md | 30 +++ .../api/rectification/cases/[caseId]/route.ts | 5 + frontend/src/components/birth-time-intake.tsx | 138 +--------- .../components/rectification-agentic-chat.tsx | 20 ++ frontend/src/lib/account-profile-patch.ts | 4 +- frontend/src/lib/birth-time-intake-model.ts | 56 ++++- .../src/lib/rectification-activity-receipt.ts | 13 +- .../lib/rectification-agentic/v9/agent-run.ts | 31 ++- .../rectification-agentic/v9/case-service.ts | 65 +---- .../rectification-agentic/v9/tool-service.ts | 21 ++ ...ectification_focus_receipt_consistency.sql | 235 ++++++++++++++++++ frontend/tests/account-api.test.ts | 7 +- frontend/tests/birth-time-intake.test.ts | 49 +++- .../rectification-activity-receipt.test.ts | 26 +- ...tion-v10-focus-receipt-consistency.test.ts | 161 ++++++++++++ .../rectification-v9-case-service.test.ts | 135 ++++++---- .../tests/rectification-v9-stream.test.ts | 138 ++++++++++ 17 files changed, 878 insertions(+), 256 deletions(-) create mode 100644 frontend/supabase/migrations/20260815010000_rectification_focus_receipt_consistency.sql create mode 100644 frontend/tests/rectification-v10-focus-receipt-consistency.test.ts diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index ac468509..7170100d 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -3273,3 +3273,33 @@ - 防复发:任何 revision state 快路径都必须验证可读性,并保留容器镜像 revision 的只读回退,不得仅以路径存在作为可消费条件。 - 相关记录:BUG-083、BUG-192 - 修复版本:本次 staging 集成提交(精确 SHA 以远端 staging 与部署结果为准) + +## BUG-194 | 生时校正焦点已写入却被前端误报为未完成 + +- 状态:resolved(本地候选,待 forward migration、staging 发布与登录态业务验收) +- 首次发现:2026-08-15 +- 最近更新:2026-08-15 +- 影响面:V10 生时校正 opening/message 回合的 conversation focus 持久化、运行完成状态、刷新后的 Activity receipt。 +- 用户现象:Agent 已正常提出首个校正问题,但界面同时显示“设置对话焦点未完成,当前进度已保留”;刷新后失败步骤又可能从执行凭证中消失。 +- 触发条件:`set_agentic_rectification_conversation_focus` 成功插入记录后返回精简字段,而 TypeScript 工具层按完整 focus row 解析;或 set-focus 真失败后,runner 仍允许已有回答文本进入 completed 路径。 +- 根因:已部署 RPC 的创建返回值只有 `focus_id/status/idempotent`,与 `parseConversationFocus` 要求的 `id/case_id/question_id/intent/...` 合同不一致,导致成功写入后抛出 `invalid_focus`。同时 turn completion 没有把 set-focus 的最终失败状态作为阻断条件,旧 receipt 也只聚合 completed tools,造成实时与刷新后的失败状态不一致。 +- 修复:新增 forward-only migration,使创建和幂等分支统一返回完整 `focus` row 与 `idempotent`;set-focus 最终失败时 turn fail closed 为 retryable、不得发送完成回答或结算;turn receipt 按选定 attempt 聚合每个工具最新 terminal 状态并公开安全的 `tool_activities`,客户端使用同一 reducer 恢复 failed tool 与 methods。 +- 验证:聚焦回归覆盖 RPC 返回合同、失败后不得 completed、同 attempt 重试成功、receipt attempt 隔离、failed tool 持久化及刷新前后 Activity 一致性;与初始化、Case 和 migration 回归合并运行 203 passed、0 failed。远端 migration 尚未应用,仍需 staging 登录态验证最终 NDJSON、持久化 turn/receipt 与免费 opening 计费不变量。 +- 防复发:数据库 RPC 返回结构必须与 TypeScript parser 共用合同测试;提出用户可见主问题前必须成功持久化对应 focus;完成凭证不得只记录成功工具或从 Agent 文本反推执行状态。 +- 相关记录:BUG-176、BUG-181、BUG-185、BUG-186 +- 修复版本:本次功能分支提交(精确 SHA 以提交、远程分支与 staging 发布结果为准) + +## BUG-195 | 新建生时校正错误继承已采用时间和用户误差范围 + +- 状态:resolved(本地候选,待 staging 发布与登录态业务验收) +- 首次发现:2026-08-15 +- 最近更新:2026-08-15 +- 影响面:初始化出生时间采集、`homepage/new` 生时校正 Case 基线与候选搜索范围、历史 Session 恢复。 +- 用户现象:用户再次新建校正时,系统可能以上一次采用的分钟而不是最初填报时间为中心,并继续继承旧的前后误差;初始化页面还要求用户选择误差分钟或大致时段。 +- 触发条件:Profile 同时存在 `reported_birth_time`、历史 `active_birth_time` 和 uncertainty,或只有 period/unknown 声明时创建 fresh Case。 +- 根因:fresh Case 的范围推导优先使用 `active_birth_time`,再直接读取 Profile uncertainty;初始化模型把用户声明误差和引擎搜索窗口混为同一字段,并允许用 period 或全天范围代替具体初始时间。 +- 修复:初始化 UI 只采集一个具体 `reported_birth_time`,不再提供误差分钟、大致时段、范围线索或跳过入口;新填报的准确时间保存为 `reported + 0/0`,不自动宣称引擎 confirmed。`homepage/new` 只查询和使用 `reported_birth_time`,忽略历史 active minute、uncertainty 与 period;没有合法 reported time 时在调用创建 RPC 前以 `profile_incomplete` fail closed。Case 扫描所需的可移动窗口改为独立的服务器执行策略,目前以填报时间为中心使用前后 15 分钟,不再伪装成用户声明;`intent=session` 继续恢复历史 Case 自身冻结的 baseline/range。 +- 验证:回归覆盖新初始化只展示单一时间输入、`0/0` 持久化但不确认、旧 uncertainty 不影响 fresh range、旧 active minute 不进入 baseline、period/unknown/无具体时间 legacy profile 不得新建、失败前不调用 RPC,以及 session 恢复不读取当前 Profile;与 Focus、receipt 和 migration 回归合并运行 203 passed、0 failed。 +- 防复发:`reported_birth_time` 是 fresh Case 唯一用户时间基线;`active_birth_time` 只表示已采用的当前排盘时间,不能反向改写新校正起点;用户声明字段、服务器搜索策略与最终 confirmed truth 必须保持分层。 +- 相关记录:BUG-127、BUG-177、BUG-187 +- 修复版本:本次功能分支提交(精确 SHA 以提交、远程分支与 staging 发布结果为准) diff --git a/frontend/src/app/api/rectification/cases/[caseId]/route.ts b/frontend/src/app/api/rectification/cases/[caseId]/route.ts index d9c5c21f..615649f7 100644 --- a/frontend/src/app/api/rectification/cases/[caseId]/route.ts +++ b/frontend/src/app/api/rectification/cases/[caseId]/route.ts @@ -123,6 +123,11 @@ function turnReceipt( engine_version: receipt.engineVersion, status: receiptStatusFromTurn(receipt.status), phases: receipt.phases.map((phase) => phase.phase), + tool_activities: receipt.toolActivities.map((activity) => ({ + tool: activity.tool, + status: activity.status, + methods: activity.methods, + })), tools: receipt.tools, methods: receipt.methods, started_at: receipt.startedAt, diff --git a/frontend/src/components/birth-time-intake.tsx b/frontend/src/components/birth-time-intake.tsx index 0932cb1d..5a4cdd46 100644 --- a/frontend/src/components/birth-time-intake.tsx +++ b/frontend/src/components/birth-time-intake.tsx @@ -6,11 +6,10 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { birthTimeConsultationOptionsCopy } from "@/lib/birth-time-consultation-consent"; import { birthTimeDisplayState, - birthTimePeriodOptions, + birthTimeSourceDefaults, birthTimeSourceOptions, type BirthTimeDraft, type BirthTimeDraftPatch, - type BirthTimeSource, } from "@/lib/birth-time-intake-model"; type BirthTimeIntakeProps = { @@ -66,47 +65,11 @@ function BirthClockSelect({ value, onChange }: BirthClockSelectProps) { ); } -const sourceDefaults = { - hospital_record: { - birthTimePeriod: "", - birthTimeClue: "", - uncertaintyBeforeMinutes: 2, - uncertaintyAfterMinutes: 2, - }, - family_exact: { - birthTimePeriod: "", - birthTimeClue: "", - uncertaintyBeforeMinutes: 10, - uncertaintyAfterMinutes: 10, - }, - approximate: { - birthTimePeriod: "", - birthTimeClue: "", - uncertaintyBeforeMinutes: 30, - uncertaintyAfterMinutes: 30, - }, - period_only: { - reportedTime: "", - birthTimeClue: "", - uncertaintyBeforeMinutes: null, - uncertaintyAfterMinutes: null, - }, - unknown: { - reportedTime: "", - birthTimePeriod: "", - uncertaintyBeforeMinutes: null, - uncertaintyAfterMinutes: null, - }, -} as const satisfies Record, BirthTimeDraftPatch>; - export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps) { const groupId = useId(); const source = value.birthTimeSource; const isConfirmed = value.birthTimeStatus === "confirmed"; const displayState = birthTimeDisplayState(value); - const knowledgeMode = source === "period_only" || source === "unknown" - ? "uncertain" - : source ? "exact" : ""; const usesClockTime = source === "hospital_record" || source === "family_exact" || source === "approximate" @@ -151,20 +114,16 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps) )} {!isConfirmed &&
- 你对出生时间了解多少? -

不用猜具体分钟,先选一个最符合你的情况的选项。

+ 填写出生时间 +

请填写你初始化提供的具体出生时间。

{birthTimeSourceOptions.map((option) => (
- )} - - {source === "period_only" && ( -
- -