feat: complete minute birth-time rectification flow

This commit is contained in:
Jesse_Chen
2026-07-25 01:15:48 +08:00
parent f90aba217f
commit 3ca30ed7de
93 changed files with 8347 additions and 1646 deletions
+548
View File
@@ -347,6 +347,7 @@
- 修复版本:待提交
## BUG-020 | 生时校正把语言问答包装成高阻力卡片表单
## BUG-018 | 生时校正把语言问答包装成高阻力卡片表单
- 状态:resolved
- 首次发现:2026-07-22
@@ -363,6 +364,7 @@
- 修复版本:待提交(本地可测)
## BUG-021 | 自然语言真实事件在分钟评分入口被静默丢弃
## BUG-019 | 自然语言真实事件在分钟评分入口被静默丢弃
- 状态:resolved
- 首次发现:2026-07-22
@@ -379,6 +381,11 @@
- 修复版本:待提交(本地可测)
## BUG-022 | 生时校正入口等待首轮请求完成后才切换会话
- 相关记录:BUG-008、BUG-009、BUG-015、BUG-016、BUG-018
- 复发自:BUG-018
- 修复版本:待提交(本地可测)
## BUG-020 | 生时校正入口等待首轮请求完成后才切换会话
- 状态:resolved
- 首次发现:2026-07-22
@@ -395,6 +402,11 @@
- 修复版本:待提交(本地可测)
## BUG-023 | 初始化地址保存后的加载提示仍指向生时评估
- 相关记录:BUG-016、BUG-018、BUG-019
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-021 | 初始化地址保存后的加载提示仍指向生时评估
- 状态:resolved
- 首次发现:2026-07-22
@@ -411,6 +423,11 @@
- 修复版本:待提交(本地可测)
## BUG-024 | 侧栏会话标题与更多操作被拆成两块
- 相关记录:BUG-020
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-022 | 侧栏会话标题与更多操作被拆成两块
- 状态:resolved
- 首次发现:2026-07-22
@@ -427,6 +444,7 @@
- 修复版本:待提交(本地可测)
## BUG-025 | 健康压力追问被数据库误判为校正操作冲突
## BUG-023 | 健康压力追问被数据库误判为校正操作冲突
- 状态:resolved
- 首次发现:2026-07-22
@@ -475,6 +493,11 @@
- 修复版本:待提交(production gate
## BUG-028 | 生时校正收到具体经历后仍重复泛问
- 相关记录:BUG-007、BUG-018、BUG-019
- 复发自:BUG-007
- 修复版本:待提交(测试 Supabase smoke 通过)
## BUG-024 | 生时校正收到具体经历后仍重复泛问
- 状态:resolved
- 首次发现:2026-07-22
@@ -491,6 +514,7 @@
- 修复版本:待提交(本地可测)
## BUG-029 | 生时校正仍有未回答区分领域时提前结束
## BUG-025 | 生时校正仍有未回答区分领域时提前结束
- 状态:resolved
- 首次发现:2026-07-22
@@ -507,6 +531,11 @@
- 修复版本:待提交(本地可测)
## BUG-030 | 职位和管理职责变化未识别为事业证据
- 相关记录:BUG-019、BUG-024
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-026 | 职位和管理职责变化未识别为事业证据
- 状态:resolved
- 首次发现:2026-07-22
@@ -523,6 +552,11 @@
- 修复版本:待提交(本地可测)
## BUG-031 | 生时纠正未收敛仍永久扣费且事件事实未进入评分契约
- 相关记录:BUG-024、BUG-025
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-027 | 生时纠正未收敛仍永久扣费且事件事实未进入评分契约
- 状态:resolved
- 首次发现:2026-07-22
@@ -539,6 +573,11 @@
- 修复版本:待提交(本地可测)
## BUG-032 | 生时校正覆盖历史回答且完成后无法返回原问题
- 相关记录:BUG-019、BUG-024、BUG-025
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-028 | 生时校正覆盖历史回答且完成后无法返回原问题
- 状态:resolved
- 首次发现:2026-07-23
@@ -555,6 +594,11 @@
- 修复版本:待提交(本地可测)
## BUG-033 | 生时校正缺少连续事件语义导致模板式跳问
- 相关记录:BUG-018、BUG-019、BUG-024
- 复发自:BUG-018、BUG-024
- 修复版本:待提交(本地可测)
## BUG-029 | 生时校正缺少连续事件语义导致模板式跳问
- 状态:resolved
- 首次发现:2026-07-23
@@ -571,6 +615,11 @@
- 修复版本:待提交(本地可测)
## BUG-034 | 生时校正 Agent 降级只记录 200 导致模板回退不可诊断
- 相关记录:BUG-024、BUG-025、BUG-028
- 复发自:BUG-024、BUG-028
- 修复版本:待提交(本地可测)
## BUG-030 | 生时校正 Agent 降级只记录 200 导致模板回退不可诊断
- 状态:resolved
- 首次发现:2026-07-23
@@ -587,6 +636,10 @@
- 修复版本:待提交(本地可测)
## BUG-035 | 生时校正发送错误内容后无法撤回修改
- 相关记录:BUG-028、BUG-029
- 修复版本:待提交(本地可测)
## BUG-031 | 生时校正发送错误内容后无法撤回修改
- 状态:resolved
- 首次发现:2026-07-23
@@ -603,6 +656,10 @@
- 修复版本:待提交(本地可测)
## BUG-036 | Agent 化生时校正上线前被旧模板组件断言阻断
- 相关记录:BUG-018、BUG-028
- 修复版本:待提交(本地可测)
## BUG-032 | 连续事件补充回答丢失状态并重新进入日期模板
- 状态:resolved
- 首次发现:2026-07-23
@@ -619,6 +676,18 @@
- 修复版本:待提交
## BUG-037 | 生时校正刷新后把真实对话重建成“已记录”模板
- 影响面:生时校正多轮问答、事件证据账本、Agent 自然追问
- 用户现象:用户先提供“2017年5月参加工作”,Agent 继续确认“正式工作还是实习/兼职”;用户回答“正式工作”后,系统却把这句话当成新事件,重新追问“具体是什么年月/只记得年份也可以”,既重复问题又丢失上一轮的年月。
- 触发条件:Agent 的追问属于已有事件的日期补充或细节补充,但公开 turn 只保存问题文本,没有保存追问目标事件和问题类型。
- 根因:编排器仅根据当前消息是否含日期决定分支;上一轮没有持久化 `event_date``event_detail``new_event` 状态和目标 `evidenceId`,导致无日期的补充回答落入固定非评分模板;后续虽让 narrative Agent 输出了目标状态,进度装饰层仍无条件重建 evidence request 并覆盖为 `new_event`;细节合并还会被事件分隔符错误拆成多个证据。
- 修复:为 evidence request 增加向后兼容的 follow-up 状态机,记录问题类型和目标事件 ID;narrative Agent 被要求在追问已有事实时输出对应状态;进度装饰只维护确定性的下一领域,不再覆盖 Agent 可见问题对应的 follow-up 目标;服务端按状态将日期或细节 append-only 合并到目标事件并保留 `correctsEvidenceIds`,只有 `new_event` 才创建独立事件;细节合并改为单事件解析后覆盖组合摘要,避免标点导致错误拆分。
- 验证:叙事、编排与存储聚焦测试 81/81 通过;真实两轮编排回归覆盖 Agent 先追问已有事件细节并持久化目标,然后“2017-05 参加工作”后回答“正式工作”仍保留 `2017-05`、形成“参加工作;正式工作”并不再出现日期模板;旧 turn 缺少 follow-up 时仍按兼容路径处理。目标文件 ESLint、相关 TypeScript 检查和 `git diff --check` 通过。
- 防复发:每个中间轮必须断言追问类型、目标 evidence ID 和下一轮的事件合并结果;模型回退时默认显式 `new_event`,不得把已有事件细节当成新事实;跨轮测试必须断言用户可见回复不重复已回答的日期问题。
- 相关记录:BUG-024、BUG-029、BUG-030
- 复发自:BUG-029
- 修复版本:待提交(本地可测)
## BUG-033 | 首次进入生时校正长期停留在建立记录
- 状态:resolved
- 首次发现:2026-07-23
@@ -635,6 +704,16 @@
- 修复版本:待提交
## BUG-038 | 老案例摘要被误标成原始对话且第七条事件后无法继续
- 影响面:首次创建生时校正 case、首条 Agent 引导消息、进入校正页面的等待时间
- 用户现象:点击生时校正后已经进入统一加载动画,但长时间停留在“正在建立校正记录…”。
- 根因:建档事务在写入首轮和完成扣点前同步等待 Mastra Agent 的结构化输出;模型 SDK 自带重试,叙事校验层也允许重试一次,慢请求和双重重试会把整段时间全部暴露给用户。实测本地星盘扫描约 0.08 秒,不是主要瓶颈。
- 修复:首轮叙事的两次校验尝试共享 10 秒中止信号,并关闭 Mastra SDK 内部重试;10 秒内生成成功仍使用 Agent 自然回答,超时或两次校验失败则使用现有的安全、可评分 fallback 完成建档。中间轮和最终总结不受该首轮时限影响。
- 验证:新增回归断言,确保首轮两次叙事尝试共享同一个 `AbortSignal`;目标 narrative 测试、ESLint、TypeScript 与生产构建通过后记录最终结果。
- 防复发:首轮建档不得无限等待模型,也不得同时开启 SDK 重试和业务校验重试;耗时预算必须覆盖整个首轮生成,而不是每次尝试单独重新计时。
- 相关记录:BUG-030
- 修复版本:待提交(本地可测)
## BUG-034 | 生时校正入口旧快照重复 start 导致 409
- 状态:resolved
- 首次发现:2026-07-23
@@ -651,6 +730,18 @@
- 修复版本:0850619eaf5002736542463394720b0ab1949ce9
## BUG-039 | 生时校正 Agent 回答等待结束后一次性出现
- 影响面:生时校正首页入口、未完成 case 恢复、重复 start 防护
- 用户现象:用户点击进入生时校正时,接口发送新的 `start`,返回 `409 action_conflict`,页面无法进入校正会话。
- 触发条件:前端账户快照没有包含已有未完成 case,或账户快照在出生声明变更后隐藏了旧 case,但数据库中仍存在该用户的未完成 V3 case。
- 根因:前端只根据内存账户状态选择 `start/resume`;数据库会阻止同一用户创建第二个未完成 V3 case,旧快照因此被拒绝。
- 修复:首次 `start` 收到 409 后刷新账户状态;如果刷新得到可恢复 case,自动使用最新 `caseId``turnVersion` 执行 `resume`,不重复扣点;如果刷新后仍没有声明匹配的 case,则保留原错误,避免未经用户确认自动放弃旧校正记录。
- 验证:`frontend/tests/consultation-entrypoint.test.ts` 23/23 通过,覆盖 409、刷新账户、使用最新 case 版本恢复;目标文件 ESLint 与 `git diff --check` 通过。完整套件仍有既有 CSS 断言和数据库权限测试失败,与本修复无关。
- 防复发:生时校正入口必须把 `start` 视为可恢复的幂等操作;收到 case 冲突时先刷新 durable 状态,再决定恢复或提示用户;不得直接再次扣费、创建第二个 case,或在声明不一致时静默删除旧 case。
- 相关记录:BUG-030、BUG-033
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-035 | 服务层重复 start 未在扣费前复用同一声明的未完成 case
- 状态:resolved
- 首次发现:2026-07-23
@@ -667,6 +758,18 @@
- 修复版本:本次流式修复提交
## BUG-040 | Agent 活动状态与回答被错误渲染为互斥状态
- 影响面:生时校正 start 编排、重复点击/多标签进入、首轮等待时间与扣费预留
- 用户现象:前端收到新的 `start` 后仍等待模型计算,最终返回 `409 action_conflict`;账户刷新未必能返回可恢复 case,导致用户无法进入会话。
- 触发条件:同一用户使用不同 `actionId` 重复进入生时校正,或两个入口并发发起 start。
- 根因:编排器只按当前 `actionId` 查询 case;数据库在 reserve/create 阶段才执行“同一用户只能有一个未完成 V3 case”的约束,应用因此先做了不必要的计算和扣费预留。
- 修复:在 reserve 前增加账户级未完成 case 查询;同一 `declaredBirthInput` 直接返回已有公开 turn,不重复计算或扣费;声明不一致继续稳定返回冲突。并发竞态在释放本次预留后重新读取账户,复用同声明的胜出 case。
- 验证:`frontend/tests/conversational-rectification-orchestrator.test.ts` 32/32 通过,覆盖同声明重复 start 复用、声明不一致冲突和既有预留重试;后续需补充线上部署后的真实 smoke。
- 防复发:start 幂等性必须同时在入口、编排器和 durable RPC 三层成立;任何新增 actionId 的 start 都必须先检查账户级未完成 case,不能把数据库冲突当成正常流程。
- 相关记录:BUG-030、BUG-033、BUG-034
- 复发自:BUG-034
- 修复版本:待提交(本地可测)
## BUG-036 | 旧数据库校验器把首条新事件回答误报为 409
- 状态:resolved
- 首次发现:2026-07-23
@@ -683,6 +786,16 @@
- 修复版本:`4a9b1dc`
## BUG-041 | 生时校正生成回答时消息列表不自动跟随到底部
- 影响面:生时校正首条及后续普通新事件回答
- 用户现象:case 与请求版本同为 0,提交首条经历仍返回 `409 action_conflict`
- 根因:应用已为证据请求加入可选 `followUp` 状态,但当前数据库校验器仍只接受旧字段;显式的 `new_event` 与旧默认语义相同,却在保存边界被拒绝。
- 修复:保存任何 turn 时都移除语义冗余的 `followUp: new_event`,兼容旧校验器;`event_date``event_detail` 仍等待对应数据库迁移后持久化。
- 验证:直接 RPC 探针确认旧格式为 `true`、带 `followUp``false`;新增 store 回归测试覆盖首轮与后续 turn 的兼容投影。
- 防复发:新增可选持久化字段时,默认语义必须能向旧数据库降级;数据库迁移未应用前不得把兼容字段直接写入 durable RPC。
- 相关记录:BUG-032、BUG-034、BUG-035
- 修复版本:待提交(本地可测)
## BUG-037 | 非评分回答绕过 Agent 并显示固定澄清模板
- 状态:resolved
- 首次发现:2026-07-23
@@ -729,3 +842,438 @@
- 相关记录:BUG-020、BUG-034、BUG-042
- 复发自:BUG-020
- 修复版本:待提交
- 影响面:生时校正缺少年月、未来事件、换方向和非评分更正后的自然问答
- 用户现象:用户用自然语言补充“化学专业”“后来换了工作”等内容后,页面直接显示“你提到……具体内容我已经记下了。它大致是什么年月?”,与前后 Agent 语气断裂,也可能忽略刚才已经建立的事件上下文。
- 根因:编排器发现本轮暂时没有可评分事件后,直接进入同步 `nonScoringTurn()`;该函数完全没有调用 narrative Agent,而是用固定字符串生成可见回复。Mastra 和模型没有获得生成这轮回答的机会。
- 修复:非评分分支先用当前候选技术包、完整事件账本、最新用户原话和未决证据调用现有 narrative Agent;状态机只在后台固定本轮追问属于 `event_date``event_detail` 还是 `new_event`,不再替代 Agent 的可见措辞。模型或技术包调用失败时仍保留确定性安全文案,避免正常澄清变成 5xx。
- 验证:编排回归 32/32 通过;相关叙事、编排和存储测试 81/81 通过。新增断言确认无年月事件会得到 Agent 针对该事件生成的单一自然问题,并持久化 `event_date + evidenceId`,不再出现旧固定模板;未来事件、换方向和已有评分事件的行为保持兼容。
- 防复发:任何能继续对话的业务分支都必须优先经过 narrative Agent;状态机可以决定证据目标和可评分性,但不得直接占用正常用户回复。确定性模板只能作为模型或技术计算失败时的技术兜底。
- 相关记录:BUG-029、BUG-030、BUG-032
- 复发自:BUG-029
- 修复版本:待提交(本地可测)
## BUG-038 | 全球地点字段未迁移导致账户余额接口整体 500
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`GET /api/account`、首页账户初始化、余额和生时校正入口
- 用户现象:已登录用户访问本地首页时,账户接口返回 `500 {"error":"暂时无法读取账户余额"}`,余额与账户状态均无法加载。
- 根因:账户 GET 为全球出生地点新增了 `birth_place_*``timezone_*` 字段查询,但当前 Supabase 尚未应用对应迁移,PostgREST 返回 `42703 column profiles.birth_place_label does not exist`。PATCH 已有旧表回退,GET 缺少同等兼容路径,并把资料字段错误误报成余额错误。
- 修复:GET 检测缺列或 schema cache 错误后,回退到迁移前的 profile 字段集合;全球地点字段在响应中返回空值,余额、出生时间状态和未完成校正 case 继续正常读取。迁移应用后自动使用完整查询。
- 验证:服务角色直接查询确认修复前错误为 `42703``frontend/tests/account-api.test.ts` 增加旧表回退回归,另运行 TypeScript、目标 ESLint 与本地登录接口 smoke。
- 防复发:向账户初始化查询增加非关键资料字段时,应用发布必须兼容迁移前后的数据库形状;不能让可选地点元数据阻断余额与核心账户状态。
- 相关记录:BUG-034、BUG-035
- 修复版本:待提交(本地可测)
## BUG-039 | 全球地点迁移漏掉服务角色列权限导致账户保存 500
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`PATCH /api/account`、初始化出生地点保存、全球地点资料更新
- 用户现象:读取账户已经恢复,但提交出生资料返回 `500 {"error":"暂时无法核对现有出生资料"}`
- 根因:账户 PATCH 的并发保护和缺失 profile 恢复路径通过服务角色读取、插入及更新 `profiles`;全球地点迁移只授予了 `authenticated` 更新权限,遗漏服务角色对六个新字段的列级 `SELECT / INSERT / UPDATE`PostgREST 返回 `42501 permission denied for table profiles`
- 修复:在全球地点迁移中为服务角色补齐六个新字段的最小列级读取、插入和更新权限,不恢复表级宽权限。
- 验证:迁移权限静态回归、生产事务 dry-run、正式授权、字段权限查询和真实登录 PATCH smoke。
- 防复发:扩展服务端账户 upsert 字段时,必须同时审计 authenticated 自助保存和 service_role 并发读取/upsert 两条权限链。
- 相关记录:BUG-034、BUG-038
- 修复版本:待提交(本地可测)
## BUG-040 | 选择全球地点后重复搜索且候选列表被卡片裁切
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:初始化出生地点搜索、全球地点选择与资料保存
- 用户现象:选择“中国 · 河北省 · 邯郸市 · 峰峰矿区”后,完整标签又触发一次搜索并返回泛化的 Geoapify 结果;候选列表还会被出生地点卡片截断,后续选项看不全。
- 根因:位置服务在没有完整出生时刻时合法返回 `timezoneOffset: null`,但页面只把数字 offset 视为已选择地点,因此父组件继续向 combobox 传入空值,选中标签被当成新查询;同时 onboarding 卡片使用 `overflow: hidden` 裁切了绝对定位的候选列表。
- 修复:地点完整性改为接受“合法坐标 + IANA timezoneId”,数字 offset 可暂时为空;选中地点时使在途搜索序列失效并清空候选;出生时间过渡卡片允许候选列表溢出显示。
- 验证:地点选择、出生资料完整性和全球地点目标测试覆盖 nullable offset、缺失 timezoneId、选择竞态保护与候选列表可见性。
- 防复发:前端地点完整性必须与位置 API 的 nullable offset 合同一致;选择类异步组件必须在 commit selection 时废弃旧请求,浮层祖先不得无意裁切。
- 相关记录:BUG-038、BUG-039
- 修复版本:待提交(本地可测)
## BUG-041 | 全球地点资料已保存但初始化接口仍判定未完成
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`POST /api/onboarding`、全球出生地点用户进入首页
- 用户现象:用户已填写称呼、出生日期、时间线索和旧金山地点,保存成功后进入首页仍返回 `出生资料尚未完成`
- 根因:onboarding 服务端完整性判断仍把中国 `province_code + city_code` 写死为必填,没有识别已经持久化的全球地点标签、经纬度和 IANA 时区。
- 修复:服务端资料投影补齐全球地点字段;完整性判断接受“有效全球地点”或“旧中国行政区地点”,并复用出生资料共享校验。
- 验证:新增旧金山 `period_only + timezoneOffset null + America/Los_Angeles` 回归用例,确认可生成首页初始问题。
- 防复发:读取全球地点的服务端流程不得继续以中国行政区代码作为唯一地点完成条件。
- 相关记录:BUG-038、BUG-040
- 修复版本:待提交(本地可测)
## BUG-042 | 全球地点缺少数字时区偏移导致生时校正入口 409
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`POST /api/birth-time-conversation`、仅填写时间段的全球出生地点用户
- 用户现象:旧金山资料已完成并能进入首页,但点击生时校正返回 `profile_incomplete`
- 根因:地点搜索在没有具体出生分钟时只保存 IANA 时区,数字历史 offset 合法为空;生时校正入口起初没有在计算前补算。补算 offset 后,Geoapify 的七位小数坐标又超过校正持久化合同的六位小数边界,仍被统一映射成 `profile_incomplete`
- 修复:生时校正读取资料后复用现有本地时区服务,按出生日期和已申报时间或时间段参考时刻解析历史 offset;进入持久化合同前把经纬度规范为六位小数;旧 case 导入走同一路径。
- 验证:旧金山 `1955-02-24 + evening + America/Los_Angeles` 回归确认请求历史时区服务、得到 `-8`、规范化 Geoapify 坐标后进入校正。
- 防复发:资料保存可以暂缺 offset,但任何进入分钟计算的路径必须先通过 IANA 历史时区解析;外部地理编码坐标必须在进入耐久 JSON 合同前规范化,不能把 offset null 或坐标精度问题误报为资料缺失。
- 相关记录:BUG-040、BUG-041
- 修复版本:待提交(本地可测)
## BUG-043 | 全球出生地点在兄弟接口中被误判、冲突或静默退化
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`GET /api/account``POST /api/consult``POST /api/birth-time-journey`、今日星语、合盘与遗留生时评估接口
- 用户现象:全球出生地点已保存且可进入首页,但已有生时校正记录无法恢复并反复触发 `action_conflict 409`;正式咨询可能在扣点前返回资料不可用;部分旧功能仍要求中国行政区代码,合法全球资料会静默退回模板或 503。
- 根因:全球地点迁移只修复了 onboarding 与 conversational rectification 主入口,多个兄弟读取路径仍各自维护旧合同:强制 `timezone_offset` 为数字、直接比较 Geoapify 七位小数坐标,或把中国省市区中心当作唯一地点真值。账户接口因此看不到数据库中实际存在的未完成 case,前端误判为需要重新创建。
- 修复:抽取共享历史时区解析器,兼容数据库 snake_case 与浏览器 camelCase 资料,并按实际申报/确认时间调用本地 IANA 历史时区服务;账户恢复使用已存 case 的不可变 offset 作为仅用于匹配的 fallback,并把坐标统一到六位耐久精度;咨询在扣点前使用最终 active time 补算 offsetjourney 入口和 case loader 在严格解析前补算;今日星语、合盘和遗留评估优先使用真实全球经纬度与时区,中国行政区仅作兼容 fallback。
- 验证:账户 nullable offset + 七位坐标可恢复旧金山已有 case,时区 ID 或地点 ID 不一致仍拒绝恢复;verified consultation 使用最终 active time 解析 `-8` 且解析失败不调用扣点;journey、今日星语、合盘和遗留评估的全球地点回归通过。两组聚焦套件共 84/84 通过,目标文件 ESLint、TypeScript `--noEmit``git diff --check` 通过。
- 防复发:出生地点完成性与计算就绪性必须分层;资料层可保存 IANA timezoneId 且 offset 暂空,但所有星盘计算入口必须统一补算历史 offset。不得在新接口复制中国专用地点解析,也不得用未经规范化的外部坐标直接比较持久化声明。
- 相关记录:BUG-040、BUG-041、BUG-042
- 修复版本:待提交(本地可测)
## BUG-044 | 数据库出生地点声明校验落后导致创建校正记录误报 409
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`POST /api/birth-time-conversation`、Geoapify 等全球地点用户首次创建生时校正记录
- 用户现象:账户资料完整且不存在未完成 case,点击生时纠正仍返回 `409 action_conflict`;重试同一个已释放的 action 后可能转为计费失败。
- 根因:TypeScript 持久化声明已经支持 `placeId``placeType``provider``timezoneId``timezoneSource`,但数据库函数 `conversational_rectification_valid_declared_birth_input` 仍只允许旧中国地点字段。合法全球地点声明在 `create_conversational_rectification_case` 内被判无效,并被 RPC 统一映射为 `conversational_action_conflict`
- 修复:新增向前迁移同步数据库地点声明白名单,接受全球地点身份、IANA 时区来源及坐标字段;地点身份允许 `city``cityCode``placeId` 任一存在,同时保留旧中国地点兼容和数值边界校验。
- 验证:数据库迁移单独执行成功;使用新的 actionId 对本地接口真实 smoke 返回 `200 active`,创建可恢复 case,首轮 narrative 非空;相同 actionId 重放仍返回同一 case,账户积分只从 100 扣至 99;无待交接内容时 handoff 按合同返回 `204`。相关持久化与全球地点测试 28/28 通过。
- 防复发:任何扩展 `declaredBirthInput.birthplace` 的应用字段都必须同步更新数据库 JSON 校验函数并增加迁移文本回归断言;不得把声明校验失败笼统诊断为旧 case 冲突。
- 相关记录:BUG-034、BUG-035、BUG-042、BUG-043
- 修复版本:待提交(本地可测)
## BUG-045 | 数据库追问合同落后导致第一条回答误报 409
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`POST /api/birth-time-conversation`、已创建校正 case 的第一条及后续自然语言回答
- 用户现象:校正记录能够创建和恢复,但提交第一条人生事件后返回 `409 action_conflict`case 的 `turnVersion` 保持不变,积分没有再次扣除。
- 根因:应用的 evidence request 已支持带目标事件的 `followUp: { kind, evidenceId }`,但共享 Supabase 尚未执行仓库已有迁移 `20260723010000_align_conversational_follow_up_request.sql`。旧版 `conversational_rectification_valid_evidence_request` 拒绝该合法字段,保存 RPC 将校验失败统一映射成 `conversational_action_conflict`
- 修复:在 Supabase 项目 `vtvnfqmonbfuxmqkqdlc` 单独执行已有向前迁移,使数据库 evidence request 校验与当前 TypeScript 持久化合同一致;未执行其他迁移,也未重新部署应用。
- 验证:对原 case 使用新 actionId 重试第一条回答返回 HTTP 200,`turnVersion` 从 0 递增到 1narrative 和目标 `event_detail` follow-up 正常返回;用同一 actionId 重放返回相同 turn,账户积分保持 99。服务端日志两次均为 `actionKind=answer``resultCategory=success``billingState=unchanged`;持久化与迁移聚焦测试 27/27 通过。
- 防复发:应用扩展 durable JSON 合同时,数据库迁移必须进入环境迁移账本并在发布验收中执行第一条真实写入 smoke;不能只用迁移文件存在或静态测试通过代替远端数据库合同验证。
- 相关记录:BUG-034、BUG-035、BUG-044
- 修复版本:数据库向前迁移已应用;应用代码待提交(本地可测)
## BUG-046 | 生时校正刷新丢失首条引导并堆叠历史用户气泡
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正会话首次进入、连续问答及刷新后的历史恢复
- 用户现象:首次进入时 Agent 的引导消息刷新后消失;已录入的人生事件被集中恢复成多条连续的右侧用户气泡,最后只剩一条 Agent 回复,破坏真实的一问一答顺序。
- 根因:新会话没有把完整问答 transcript 持久化到 session;恢复接口只返回最新 turn,前端只能把最新 turn 中累计的 `evidenceRecap` 误当成逐轮聊天历史。数据库实际保存了每轮 `birth_time_rectification_turns.narrative`,用户原文也可通过 `birth_time_rectification_event_evidence.source_turn_id``raw_text` 恢复,但旧恢复链路没有读取这些数据。
- 修复:新会话从首条 Agent narrative 开始持久化完整消息序列,每次回答后保存真实的 `Agent → 用户 → Agent` transcriptresume 接口按 turn version 读取 narrative,并用 evidence 的 source turn 关联用户原文,返回耐久的交替历史;页面优先用数据库历史修复旧 session。无法可靠恢复用户原文的残缺旧 turn 不再伪造模板回复,相邻 Agent 状态只保留最新一条;最末级 legacy fallback 也只恢复最近一组可靠消息,不再展开全部累计 evidence。
- 验证:client、controller、route 聚焦测试 52/52 通过;component 非 Chromium 测试 8/8 通过;TypeScript `--noEmit`、目标文件 ESLint 与 `git diff --check` 通过。按用户要求未运行 Chrome/Playwright。
- 防复发:聊天历史必须以逐轮 durable transcript 或可验证的 turn/evidence 关联为真源;累计业务证据只能用于评分与摘要,不能被前端推断成消息列表,也不能为缺失历史生成看似真实的 Agent 文案。
- 相关记录:BUG-024、BUG-029、BUG-032、BUG-045
- 修复版本:待提交(本地可测)
## BUG-047 | 相对日期缺少上下文解析并触发模板式重复追问
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正连续追问、自然语言事件提取与澄清合并
- 用户现象:Agent 已知用户在 `1972年12月正式退学`,继续追问彻底离校时间后,用户回答“来年1月份我彻底离开的学校”,系统仍重复询问具体年份和月份。
- 根因:事件提取器把“来年、次年”等相对日期视为无日期文本,但不读取正在讨论的事件年份;澄清合并又只接受不含描述的纯日期回答;非评分分支随后用固定澄清模板覆盖模型已经生成的上下文回答。
- 修复:对 `event_date``event_detail` 追问使用目标事件或最近明确事件作为年份锚点,将“来年、次年、第二年、翌年”和“同年、当年、那年”的月份上下文化后再提取,同时保留用户原始文本;允许带自然描述的日期补充完成原事件,但仅在纯日期、同领域或低信息细节时合并,避免把“搬家”等明确新事件吞入上一条澄清;普通澄清优先显示 narrative Agent 的自然回答,确定性模板仅保留为模型失败兜底。
- 验证:事件提取与编排聚焦测试 59/59 通过,覆盖“2022年12月正式退学 → 来年1月彻底离校”解析为 `2023-01`、带描述日期补充、相对日期无上下文时不臆造年份,以及不同领域新事件不误合并。目标 TypeScript、ESLint 与补丁检查通过;按用户要求未运行 Chrome/Playwright。
- 防复发:事件状态机只负责持久化目标、证据质量与评分边界,不得替代 Agent 的可见对话;相对日期必须在明确的当前事件上下文内解析,缺少可靠锚点时保持待澄清;澄清合并必须同时判断日期、摘要和领域连续性。
- 相关记录:BUG-029、BUG-032、BUG-037
- 修复版本:待提交(本地可测)
## BUG-048 | 生时校正消息更新后没有自动滚动到最新内容
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正聊天区、用户发送、Agent 思考态及回答更新
- 用户现象:用户发送经历后,Agent 开始思考并生成回答,但消息列表停留在原位置,需要手动向下滚动才能看到最新内容。
- 根因:普通 session 在消息列表末尾设置了滚动锚点和 `scrollIntoView` effect;生时校正使用独立的 `rectification-message-list` 滚动容器,却没有对应的末尾锚点和消息更新监听。
- 修复:在生时校正消息列表末尾增加滚动锚点,并在消息数量、最新回答文本、思考状态、提交状态、turn version 和错误状态变化时滚动到末尾;生成中即时跟随,完成后平滑滚动,同时尊重 `prefers-reduced-motion`
- 验证:组件目标回归测试通过;TypeScript `--noEmit`、目标 ESLint 与 `git diff --check` 通过。按用户要求未运行 Chrome/Playwright。
- 防复发:新增独立聊天滚动容器时必须同时提供末尾锚点,并覆盖乐观用户消息、生成状态和回答文本更新三类触发源。
- 相关记录:BUG-046
- 修复版本:待提交(本地可测)
## BUG-049 | 生时校正 Agent 提问悬空截断且回答缺少消息操作
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正自然语言回答、Agent 消息操作、当前轮次重跑
- 用户现象:Agent 已经理解并复述了用户的教育经历,但回答最后停在“我需要确认一个关键信息——”,没有显示真正的问题;每条 Agent 回答下方也缺少赞、踩、复制和重跑操作。
- 根因:Mastra 返回的结构化 JSON 可以通过校验,但其中 `narrative` 自身可能以破折号、冒号或“关键信息”等悬空引导语结束;旧逻辑只要在正文中看见“确认”等疑问词,就误判为已经包含可见问题,因此没有把同一结构化输出中的完整 `evidenceRequest.prompt` 补到正文末尾。
- 修复:增加悬空提问结尾识别;中间轮次若正文没有完整问题或停在悬空引导语,自动追加模型结构化输出中的具体问题。Agent 回答下方增加赞、踩、复制和重跑图标;赞踩互斥并保留在当前页面,复制写入剪贴板,只有最新回答可以重跑。
- 重跑边界:新增独立、幂等的 `regenerate` 命令,使用上一轮已经持久化的用户原文和事件台账重新生成当前 Agent narrative;不重新提取事件、不追加用户消息、不重复评分、不改变候选范围、不扣点,并在当前消息列表和刷新后的耐久历史中替换原 Agent 回答。
- 验证:TypeScript `--noEmit` 通过;叙事、Controller、client、route、orchestrator 聚焦测试 119/119 通过,覆盖悬空提问补全、无 payload 重跑、消息原位替换、证据与候选保持不变;组件静态/SSR 检查 9 项通过;目标 ESLint 与 `git diff --check` 通过。测试过滤器未按预期排除文件内的真实 Chromium 用例,该用例误启动后以 `SIGTRAP` 退出,未作为本次 UI 验收依据,且不再重试。
- 防复发:结构化回答校验必须区分“出现疑问相关词”与“存在完整可回答的问题”;任何重新生成操作都必须与 evidence mutation、评分和计费分离,并通过相同 action receipt 保持幂等。
- 相关记录:BUG-029、BUG-046、BUG-047、BUG-048
- 修复版本:待提交(本地可测)
## BUG-050 | 生时校正重跑期间重复显示旧回答和新思考气泡
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正 Agent 回答重跑交互
- 用户现象:点击重跑后旧回答仍保留,列表底部额外出现一个思考消息,生成完成后旧回答才被替换。
- 根因:重跑只复用了通用 `pending` 状态;消息列表继续渲染旧回答,同时通用 pending 分支又在列表末尾追加思考气泡。
- 修复:组件记录当前重跑消息,立即在原消息位置显示思考态并隐藏操作栏;重跑期间不渲染通用末尾思考气泡,成功后原位显示新回答,失败后恢复旧回答。
- 验证:组件静态回归、目标 ESLint、TypeScript 与补丁检查通过;按既定限制未运行 Chrome/Playwright。
- 防复发:原位更新类操作必须把进行中状态绑定到目标消息,不得同时复用追加新消息的通用 pending UI。
- 相关记录:BUG-048、BUG-049
- 修复版本:待提交(本地可测)
## BUG-051 | 既有事件的原因补充被误判为无日期新事件
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正自然语言事件补充、事件台账合并、Agent 可见回答
- 用户现象:Agent 已围绕一条带年月的事件追问原因,用户回答原因后,系统仍固定回复“还差时间定位”,再次索要年份和月份。
- 触发条件:模型可见问题是在追问既有事件的原因或具体表现,但结构化 `followUp` 偶尔错标为 `new_event`;用户随后用不带日期的自然语言回答。
- 根因:澄清合并逻辑完全信任结构化 `followUp`,遇到 `new_event` 立即退出;原因回答因此被提取成新的无日期事件,并触发确定性的日期澄清模板覆盖正常对话。
- 修复:当上一轮可见问题明确包含原因、表现或“哪些方面”等细节追问语义时,即使 metadata 错标为 `new_event`,也把回答合并回最近一条带日期的有效事件;同时移除“这件事很有用,但还差时间定位”的固定话术,保留不假定上下文的简短兜底。
- 验证:orchestrator 回归覆盖“1972年12月退学 → 追问压力原因但 metadata 错标 → 经济负担导致无法继续”,断言沿用原日期、合并为同一有效事件且不再出现固定索时模板;目标 ESLint、TypeScript 与补丁检查。
- 防复发:事件追问的可见语义必须能够兜底结构化 metadata 的偶发错标;已有日期事件的原因、性质和具体表现回答不得强制再次提供日期。
- 相关记录:BUG-029、BUG-047
- 复发自:BUG-047
- 修复版本:待提交(本地可测)
## BUG-052 | 确定性流程模板覆盖生时校正 Agent 的自然回答
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正首轮引导、中间事件追问、方向切换、未来事件、暂停、放弃与确认消息
- 用户现象:模型已经结合上下文生成回答后,网页仍显示“已记录”“当前累计”“下一步”“还差时间定位”等重复话术;部分操作还会新增程序模板气泡,使对话像问卷而不是连续的一问一答。
- 根因:编排层把事件提取结果、`followUp` metadata 和收敛状态当成可见文案的唯一真源,在模型生成之后又根据分支拼接或替换 narrative。原本用于事实安全和失败兜底的确定性逻辑因此越界成了对话作者,并且 metadata 的偶发误判会直接中断正常语义交流。
- 修复:可见文案统一以 Agent narrative 为准;删除首轮候选前后缀、事件保存与计数进度、领域推荐、方向切换、无日期、未来事件、暂停、放弃和确认成功等确定性模板。事件提取、未来事件不计分、去重、更正、候选计算和确认时间原子落库继续作为不可见业务状态运行,提取失败或 metadata 标错不得覆盖模型回答。增加脱敏诊断日志,仅记录阶段、校验问题代码、重试和 fallback 类别,不记录用户原文或完整模型输出。
- 表格边界:稳定层/敏感层、技法审计和事件证据三类表可在中间轮按上下文需要出现;最终确认前必须提供完整汇总。表中候选、范围、分盘、技法和引用只能来自 technical packet,不得编造、泄露私有分数或声称运行了未执行的技法。
- 验证:叙事与编排单元测试覆盖 Agent 原文保留、结构化 metadata 不覆盖自然回答、未来事件后台排除、原因补充合并、暂停/放弃/确认无新增模板气泡,以及最终确认仍更新正式出生时间;合成 E2E 覆盖首轮、换方向、无日期和未来事件均保留模型可见回答。目标 ESLint、TypeScript、测试与补丁检查结果见本轮验证记录。
- 防复发:状态机只能决定允许的动作和持久化状态,不能编写用户可见话术;新增任何确定性文本前必须证明它属于安全错误而非正常对话,并增加“模型输出未被覆盖”的回归断言。
- 相关记录:BUG-046、BUG-047、BUG-049、BUG-051
- 修复版本:待提交(本地可测)
## BUG-053 | 首轮结构化结果为空时错误展示确定性引导模板
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正首轮 Agent 引导、Mastra 结构化输出兼容、首轮等待时限
- 用户现象:新建生时校正后固定显示“当前仍在核对……先说一件已经发生的学业事件”,看起来像 Agent 回答,实际没有使用模型生成的自然文案。
- 触发条件:DeepSeek 已完成一次 Mastra 调用,但 `result.object` 未形成结构化对象;提供商返回的文本结果未被继续解析,两次尝试均被记录为 schema 失败。
- 根因:生产叙事适配器只解析 `result.object`,没有兼容 Mastra/模型组合返回 JSON 文本的情况;首轮失败后又调用 `fallbackNarrative()`,把程序模板伪装成了 Agent 消息。脱敏 receipt 为 `fallbackUsed=true``schemaValidated=false``issues=["root:invalid_type"]`,不是超时错误。
- 修复:优先使用通过 schema 的 `result.object`,为空时把非空 `result.text` 交回既有 JSON 提取和 packet 校验流程;首轮连续两次仍失败时返回可重试服务错误并释放预留计费,不再持久化或展示确定性首轮模板;中间轮既有安全 fallback 暂不改变。首轮等待策略的后续修复见 BUG-054。
- 验证:叙事测试覆盖“两次不合格时拒绝而非展示模板”;路由测试覆盖 `result.object` 为空但 `result.text` 有 JSON 的兼容路径;目标叙事、编排和路由测试通过。
- 防复发:模型适配层必须兼容结构化对象与 JSON 文本两种合法承载方式;正常首轮不得用程序模板冒充 Agent 成功回答,timeout、schema 和 grounding 失败必须在脱敏日志中分别可辨认。
- 相关记录:BUG-046、BUG-052
- 修复版本:待提交(本地可测)
## BUG-054 | 首轮两次生成共用超时信号导致重试立即失败
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`POST /api/birth-time-conversation` 首轮生成与重新生成
- 用户现象:移除首轮固定模板后,重新生成等待约 30 秒返回可重试的 `service_unavailable 503`
- 触发条件:首轮模型生成超过共享的 30 秒 deadline;第二次尝试继承已经取消的 `AbortSignal`,无法获得独立生成时间。
- 根因:两次尝试在循环外创建并共用一个超时信号;诊断日志又只保留最后一次异常,把首次超时误记为 `schema_invalid`
- 修复:每次首轮尝试创建独立的 45 秒超时信号,累计两次尝试的脱敏问题代码,并将 `TimeoutError` / `AbortError` 明确归类为 `timeout`
- 验证:本地开发日志复现请求在 `30331ms` 失败;回归测试锁定两次尝试使用不同信号并将超时记录为 `timeout`,目标测试、ESLint、TypeScript 与补丁检查通过。
- 防复发:重试不得复用已取消的信号;超时、schema 和事实校验必须在脱敏日志中保持不同类别。
- 相关记录:BUG-016、BUG-053
- 复发自:BUG-053
- 修复版本:待提交(本地可测)
## BUG-055 | 首轮完整技术输出与重复重试放大模型延迟并触发 503
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`POST /api/birth-time-conversation` 首轮 Agent 引导、Mastra 结构化输出、60 秒路由预算
- 用户现象:本地首轮请求等待约 49–90 秒后返回可重试的 `service_unavailable 503`;同一模型的轻量探针正常。
- 触发条件:正式首轮把候选状态、时间范围、稳定层、敏感层、引用与完整 expert workflow 全部交给模型重复输出;一次超时后又立即进行第二次完整生成。
- 根因:模型承担了程序已经确定的 packet 字段复制工作,正式 prompt 曾达到约 8.4k 字符;提供商延迟波动时,两次 45 秒调用可超过路由声明的 60 秒预算。一次 26 秒成功结果还曾因旧引用正则误判被丢弃。
- 修复:生产模型只生成 `narrative``evidenceRequest`,候选状态、时间、范围、分盘层、引用和领域依据由服务端从 packet 确定性补全;首轮 prompt 精简到必要边界和建议领域;第一次纯超时不再触发第二次完整调用;单次等待上限调整为 47 秒,校验失败的短重试最多 7 秒;首轮若只写了介绍但漏掉问题,追加模型自己生成的 `evidenceRequest.prompt`,不使用程序话术模板。
- 验证:正式本地认证请求的 prompt 从 4785 字符进一步降到 1399 字符,provider 在 29066ms 返回,接口 HTTP 200 并生成自然首轮回答;定向测试 66/66、ESLint、TypeScript 与补丁检查通过。
- 防复发:模型结构化输出不得重复由服务器掌握的确定性 packet;生成总预算必须小于路由平台预算;首轮 timeout、schema、grounding 继续使用脱敏分类日志,不记录提示词、用户资料或模型正文。
- 相关记录:BUG-053、BUG-054
- 复发自:BUG-054
- 修复版本:待提交(本地可测)
## BUG-056 | 关键词领域分类阻断自然语义并在生成失败后回退到错误领域模板
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正自然语言事件分类、中间轮 Agent 回答与候选评分输入
- 用户现象:带明确年月的研究院实习、研究员经历被保存为 `other`;同轮模型生成失败后,页面又固定追问学业事件,看起来像 Agent 没有理解刚才的事业经历。
- 根因:确定性关键词分类被当作最终语义判断,事业词表没有覆盖所有自然表达;中间轮叙事连续失败后仍允许 `fallbackNarrative()` 生成固定业务话术,并按技术 packet 的首个建议领域继续提问。
- 修复:单条事件落入 `other` 时,优先调用 narrative Agent 结合最近事件上下文返回允许的语义领域;模型不可用、超时或仍返回 `other` 时才保留确定性结果。首轮和中间轮叙事连续失败统一返回可重试服务错误且不保存本轮,只有最终确认阶段保留包含安全边界与三类表的确定性 fallback。
- 验证:新增“2020年4月去石油化工研究院实习做研究员”语义分类回归,确认保存为 `career` 并可评分;新增中间轮不合格输出回归,确认返回 `service_unavailable`、不保存事件、不推进版本且不产生模板消息。事件提取、叙事、编排和路由定向测试 134/134 通过。
- 防复发:正则只负责低成本初筛和模型不可用时的降级,不得覆盖 Agent 对自然语言事件的语义判断;非最终轮生成失败不得用业务模板伪装成成功回答;候选时间、分盘事实、未来事件和重复计分仍由程序校验。
- 相关记录:BUG-029、BUG-032、BUG-047、BUG-053
- 复发自:BUG-053
- 修复版本:待提交(本地可测)
## BUG-057 | 中间轮重跑在 Pro 模型延迟波动时直接返回 503
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`POST /api/birth-time-conversation``regenerate` 与其他非最终叙事轮、Mastra 模型降级、validation receipt
- 用户现象:本地生时校正点击重跑后返回可重试的 `service_unavailable 503`,原回答仍保留但无法得到新的 Agent 回答。
- 触发条件:中间轮技术 packet、事件账本和上下文组成约 5.7k 字符的提示词,首选 Pro 模型在提供商延迟波动时超过 47 秒单次等待上限。
- 根因:BUG-055 为避免两次慢模型调用超过 60 秒路由预算,曾规定第一次纯超时直接结束;这避免了重复 Pro 调用,却也让瞬时延迟直接变成 503。受控本地复现中,同一重跑请求成功时总耗时约 45 秒,其中模型约 32 秒,证明服务和 Mastra 结构化输出可用,但原策略没有快速恢复路径。
- 修复:将叙事预算调整为首选模型 38 秒和独立恢复模型 7 秒;第一次调用使用 `deepseek-v4-pro`,超时、schema 或事实校验失败后以新的 `AbortSignal` 调用 `deepseek-v4-flash`。生成结果携带实际模型 IDvalidation receipt 记录真正成功的模型;两次均失败时继续返回 503,不恢复固定业务模板。
- 验证:新增 Pro 超时后 Flash 成功的回归,断言两次调用使用独立 signal、第二次 attempt 标记正确、receipt 写入 Flash 模型且不使用模板;新增 regenerate 双模型均失败的事务回归,断言不推进 `turnVersion`、不覆盖上一条 Agent 消息、不新增事件、不改变候选和计费。叙事、路由与编排定向测试 110/110 通过。
- 防复发:慢模型与快速恢复模型必须共享明确的总预算而不是各自获得完整路由时限;receipt 必须记录实际成功 provider/model;生成失败只能作为可重试技术错误,不能提交半轮状态或用业务模板伪装成功。
- 相关记录:BUG-053、BUG-054、BUG-055、BUG-056
- 复发自:BUG-055
- 修复版本:待提交(本地可测)
## BUG-058 | 私有事件路由与未执行技法清单泄露到 Agent 回答并中断追问
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正中间轮自然语言回答、事件评分路由、Technique Audit 上下文与下一问题生成
- 用户现象:用户提供带年月的真实经历后,Agent 主动解释内部“事件归类”,展示大量 `not_evaluated``blocked` 技法和确认权限,却没有继续提出下一个问题;回答像调试报告而不是连续的一问一答。
- 根因:叙事上下文直接暴露事件的私有 `domain`、建议领域和完整 expert workflow 状态,模型因此围绕工程元数据组织回答;同时问题修复器只处理首轮或明显截断的句子,语法完整但没有问号的中间回答会原样通过。技法保持 `not_evaluated` 的直接原因则是当前有效、受支持且可评分的事件不足三条,路由尚未进入 `scoreEvents()`,并非 Mastra 无法调用技法。
- 修复:新增叙事专用上下文,移除事件 `domain`、建议领域和内部评分;证据采集阶段只允许暴露已实际 `used``partial` 的技法,不再展示未调用清单、阻塞项或确认权限。私有语义领域仍保留在服务端用于事件去重、跨领域路由和候选评分,但不得进入用户可见文案。所有非最终回答必须包含且只引导一个下一问题;模型正文漏问时追加同一次模型生成的 `evidenceRequest.prompt`,不使用固定业务模板。
- 验证:新增回归覆盖事件正文可见但内部领域不可见、建议领域不进入叙事 packet、未调用技法不在采集轮展示,以及完整陈述漏问时补入模型自写问题;叙事、路由和编排聚焦测试通过,目标 ESLint、TypeScript `--noEmit``git diff --check` 通过。
- 防复发:私有语义路由与用户叙事必须使用不同数据视图;未执行的技术状态不得被包装成已完成分析;每个非最终 Agent 回答都必须断言存在一个明确的下一问题,同时最终轮仍须使用真实技术 packet 生成可审计的三类表。
- 相关记录:BUG-052、BUG-053、BUG-055、BUG-056、BUG-057
- 修复版本:待提交(本地可测)
## BUG-059 | Flash 恢复窗口过短导致模型波动时偶发 503
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:`POST /api/birth-time-conversation` 非最终叙事轮、Pro 超时后的 Flash 恢复请求
- 用户现象:同一条合法回答偶发返回可重试 `service_unavailable 503`,随后原 action 幂等重试又能正常生成回答。
- 根因:首选 Pro 模型等待上限为 38 秒,但恢复模型只有 7 秒;提供商延迟稍高时,两次尝试都会在接口 60 秒总预算之前被主动中止。原请求幂等重试约 43 秒成功并正常推进一轮,排除了数据库、case 版本和技术 packet 故障。
- 修复:保留 38 秒 Pro 窗口,将独立 Flash 恢复窗口从 7 秒放宽到 14 秒;最坏模型等待仍为 52 秒,为鉴权、技术计算和持久化保留约 8 秒,不引入第三次请求或固定模板回退。
- 验证:使用原 action 幂等重试返回成功并生成自然下一问;叙事、路由和编排聚焦测试、目标 ESLint、TypeScript `--noEmit``git diff --check` 通过。
- 防复发:两次模型尝试的总预算必须显式小于路由 `maxDuration`;恢复模型窗口不能短于其实际常见尾延迟;非最终轮仍不得用确定性业务模板伪装生成成功。
- 相关记录:BUG-055、BUG-057、BUG-058
- 复发自:BUG-057
- 修复版本:待提交(本地可测)
## BUG-060 | 生时校正模型选择器显示 GPT-5.5 但请求实际使用默认 DeepSeek
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正首次引导、用户回答和重跑的叙事模型选择,以及失败操作的幂等身份
- 用户现象:生时校正输入框下方明确选择 GPT-5.5,但接口请求没有携带模型标识;Agent 回答的延迟和实际调用模型与界面选择不一致。
- 根因:普通会话会把当前会话的 `modelId` 发送给 `/api/consult`,生时校正控制器和首页首次 `start` 请求却没有把同一字段写入命令;服务端叙事生成器因此只能使用 `RECTIFICATION_NARRATIVE_MODEL_ID`,未配置时默认选择 DeepSeek。模型选择器此前只改变了会话 UI 状态,没有贯通生时校正 API。
- 修复:为会生成 Agent 文本的 `start``answer``regenerate` 命令加入可选 `modelId`;首页首次启动使用新建或恢复的生时校正会话模型,控制器每次发送时读取当前选择。路由从既有服务端模型目录解析所选模型,合法的 GPT-5.5 优先于配置默认模型,原恢复模型仍作为第二次尝试;过期或不可用模型以安全的 `model_unavailable 409` 返回,且不扣点。操作身份同时纳入模型 ID,避免失败后切换模型仍复用旧 action fingerprint。
- 验证:增加请求序列化、首次启动、回答、切换模型后重跑、路由模型解析、不可用模型拒绝和服务分发回归;receipt 继续记录实际成功的 provider/model,而不是只记录界面选择。
- 防复发:任何带模型选择器的会话入口都必须证明 UI 会话模型、发出的命令和服务端解析结果一致;新增生成命令时必须同时更新 schema、幂等身份、客户端和路由测试。
- 相关记录:BUG-055、BUG-057、BUG-059
- 修复版本:待提交(本地可测)
## BUG-061 | 同版本恢复响应被丢弃导致本轮双方消息同时消失
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正回答成功后的消息同步、冲突恢复与持久化对话历史
- 用户现象:接口已经成功返回新的自然语言回答和完整 `conversationMessages`,但页面没有显示本轮 Agent 回答,本轮用户气泡也在请求结束后消失。
- 触发条件:请求在途时,父级状态先同步到与成功响应相同的 `turnVersion`;临时用户气泡随旧版本结束而移除,随后控制器的同版本保护又直接丢弃成功响应。
- 根因:控制器把“同版本业务 turn 不应被晚响应覆盖”扩大成了“同版本响应的全部内容均不可采用”,没有区分业务状态与更完整的持久化消息 transcript。
- 修复:更高版本响应仍优先、较旧响应仍丢弃;同版本响应只有在携带更完整的持久化历史,且其最后一条 Agent 文案与当前轮一致时,才只补齐消息列表并保留当前 turn。新版本响应也优先采用服务端持久化历史,避免本地推断覆盖耐久真源。
- 验证:新增在途回答期间父级先同步到版本 8、随后恢复响应携带完整 `Agent → 用户 → Agent` 历史的回归;确认本轮用户与 Agent 气泡均恢复,同时原有“外部同版本 turn 不被晚响应覆盖”测试继续通过。controller、client、route 聚焦测试 61/61 通过。
- 防复发:同版本保护必须分别比较业务 turn 与消息 transcript;完整且与当前 narrative 一致的耐久历史可以修复 UI,但不得用晚响应覆盖同版本外部业务状态。
- 相关记录:BUG-046、BUG-048
- 复发自:BUG-046
- 修复版本:待提交(本地可测)
## BUG-062 | 两位年份与模型领域标签导致合法经历被降级为未完成分析
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正自然语言事件提取、非最终轮叙事校验与恢复模型调用
- 用户现象:用户用“21年”“23年”描述一段连续经历后,系统没有正常理解时间线,而是显示“这次分析暂时没有完成”的固定失败提示。
- 根因:事件提取器只识别四位年份,两位年份会被视为缺少日期;同时,首选模型虽然成功生成了完整回答,但旧版结构化输出中的下一问题领域标签不在技术 packet 当前建议集合内,整段回答因此被判为 `ungrounded_evidence_domain`。恢复模型随后超时,编排层最终保存了确定性失败提示。
- 修复:两位中文年份根据当前日期展开为最近且不晚于当前年的四位年份;旧版完整模型输出继续校验候选时间、范围、分盘和引用等事实字段,但下一问题的内部领域只保留与 packet 建议集合的交集,交集为空时才使用 packet 建议,不再因无害的领域措辞差异丢弃自然回答。
- 验证:新增“21年元旦回家备考、21年底封控、在家到23年”的提取回归,以及旧版模型选择非建议领域的叙事回归;事件提取、叙事和编排聚焦测试共 109 项通过。
- 防复发:自然语言年份解析必须覆盖常见缩写表达;服务端安全校验只约束可核验事实,不应让模型的对话选题标签覆盖或中断已通过事实校验的回答。
- 相关记录:BUG-052、BUG-057、BUG-059
- 修复版本:待提交(本地可测)
## BUG-063 | 精确日期追问收到月日后重复确认同一节点
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正事件日期追问、上下文日期补全与 append-only 事件修订
- 用户现象:Agent 已询问 2026 年 7 月上旬决定开公司的具体日期,用户回答“7 月 10 号”后,Agent 又询问是否指 2026 年 7 月 10 日及其对应节点。
- 根因:日期提取器只直接识别带年份的中文日期;编排器只会补全“来年/同年”等相对年月,并且补全逻辑仅支持空日期,不支持把已有 `2026-07` 提升为 `2026-07-10`。简短回答因此被保存成新的未定日期事件,而不是目标事件的精度修订。
- 修复:仅在明确指向既有事件的 `event_date` 追问中,从目标事件继承年份或年月;允许兼容的年到月、月到日精度提升,并继续以 `correctsEvidenceIds` 保存修订链。普通新事件不猜测缺失年份。
- 验证:新增 `2026-07 · 决定开公司 → 7 月 10 号` 回归,断言生成 `2026-07-10` 修订、保留原事件、沿用原摘要且不再次询问日期归属。
- 防复发:无年份月日只能在定向日期追问且存在可靠目标日期时补全;日期精度提升必须与目标已有年份或月份前缀一致。
- 相关记录:BUG-024、BUG-047、BUG-062
- 修复版本:待提交(本地可测)
## BUG-064 | 窄候选被本地稳定性前置门控阻断导致长对话无法进入 VedAstro 验证
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正事件评分、三引擎校验、VedAstro 外部验证与最终确认阶段
- 用户现象:用户跨教育、事业、搬迁、关系和财务等领域补充大量带时间事件后,候选范围已经缩窄,但系统仍持续追问,始终不给出可确认的纠正时间。
- 触发条件:本地评分已经产生唯一领先且不超过 15 分钟的候选范围,但领先幅度、正负 1/2/5 分钟邻域稳定或 leave-one-event-out 诊断未全部通过。
- 根因:外部验证入口错误依赖本地 `can_apply=true`;而本地 `can_apply` 又把邻域稳定和 leave-one-event-out 当作硬门槛。最终确认合同同时要求 VedAstro 通过,形成“本地未完全稳定则不调用 VedAstro、未调用 VedAstro则永远不能确认”的循环门控。
- 修复:新增独立的外部验证就绪判定;当至少 3 条事件、2 个领域、必需分层完整、候选唯一领先且范围不超过 15 分钟时,即进入三引擎和 VedAstro 事件区分验证。邻域稳定与 leave-one-event-out 继续保留在审计 gate 和置信度诊断中,但不再进入 `hard_blockers`;最终确认仍要求本地窄候选、必需分层、三引擎一致、VedAstro 官方响应与事件区分全部通过,并继续等待用户明确确认后才写入出生时间。
- 验证:将一组 12 条、覆盖 5 个领域的长对话事件固化为回归;修复前本地得到 `05:0705:08` 且 VedAstro 调用次数为 0,测试失败;修复后同一 fixture 进入外部验证,模拟官方事件扫描严格区分首选与次选后返回 `confirm_minute`。活动事件评分、API 与技法合同相关测试 42/42 通过;前端相关 95 项中 94 项通过,唯一失败为既有“最多十条事件”兼容测试,与当前已取消最大事件条数的产品规则冲突,不属于本次改动。
- 防复发:测试必须同时覆盖“本地低置信但已形成窄候选”和“VedAstro 未执行或未区分时仍禁止确认”;研究稳定性诊断不得再次被用作外部验证的前置开关。
- 相关记录:BUG-051、BUG-055、BUG-058
- 修复版本:待提交(本地可测)
## BUG-065 | 真实 VedAstro 事件扫描请求乘法导致生时校正数分钟无响应
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正进入外部候选区分后的同步响应时间、VedAstro `SearchEvents` 调用量
- 用户现象:本地候选已经缩窄并开始真实 VedAstro 验证后,请求运行超过五分钟仍未完成,网页表现为长时间等待或最终服务不可用。
- 触发条件:高严谨生时校正包含多条月级或年级事件,并且至少有两个候选分钟需要外部比较。
- 根因:每个候选都会扫描全部可映射事件,而 VedAstro adapter 又把月级事件拆成约十二个采样日;十二条事件、两个候选时最坏接近 288 次同步 HTTP 请求。mock 每次立即返回,因此原回归只证明进入了 VedAstro,没有暴露真实网络延迟。
- 修复:生时校正专用调用层按 VedAstro 原生 `career``wealth``marriage` 领域各选择一条最强事件;直接映射优先于代理映射,日级优先于月级和年级,同精度选择较新事件。月级和年级事件只取范围中点作为代表日,两个候选最多发起六次外部事件扫描;响应明确记录符合条件事件数、实际选中数和选择策略,不再暗示全部事件均已外部验证。通用 range scan 保持不变。
- 验证:长对话回归断言十二条符合映射的事件只产生六次扫描,两个候选各覆盖事业、财富、关系三领域,所有扫描均为单日;相关 Python 测试 42/42 通过。使用真实 VedAstro key 重跑同一十二事件 fixture,首次完整命令约 9.7 秒、缓存后约 3.6 秒,官方响应状态为 `official_verified`。真实 `SearchEvents``05:07` 与第二候选 `05:21` 返回相同的三领域命中与 36 分信号,因此正确保持 `vedastro_candidate_not_discriminated`,没有把无法区分的外部结果伪装成分钟确认。
- 防复发:任何真实外部服务回归都必须同时锁定请求数量与代表事件选择,不得只用零延迟 mock 证明“已调用”;新增事件或采样策略时必须评估候选数 × 事件数 × 日期采样数的乘法上限。
- 相关记录:BUG-055、BUG-064
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-066 | SearchEvents 被误作最终分钟裁判且未比较官方分钟敏感层
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:高严谨生时校正候选比较、VedAstro 官方验证与最终分钟确认
- 用户现象:本地事件评分已经得到首选与次选分钟后,系统仍依赖 VedAstro `SearchEvents` 的事件命中差异决定能否确认;两个候选即使 Ascendant、宫位、D9、D10 或 Dasha 技术状态不同,只要事件搜索指标相同就无法继续收敛。
- 触发条件:进入 VedAstro 外部验证的两个窄候选在 `SearchEvents` 中返回相同事件数量和 signal lift,或反过来事件搜索指标不同但官方分钟敏感快照相同。
- 根因:旧外部验证合同把事件搜索指标当成候选分钟的最终判别器,却没有从 VedAstro 官方完整快照提取并比较 Ascendant/宫位边界、D9、D10 和 Dasha 边界身份。`SearchEvents` 适合补充事件背景,不足以独立证明某个出生分钟正确。
- 修复:为两个排名最高的候选生成不含原始响应的 VedAstro 官方分钟快照,分别对 Ascendant 与宫位、D9、D10、Dasha 边界生成安全 fingerprint;至少一个官方分钟敏感层存在差异时才通过候选身份区分 gate。候选排序继续以本地已发生事件评分为准;`SearchEvents` 改为 `event_background_validation`,明确 `used_for_decision=false`。KP cusp/sub-lord 因当前已验证官方接口没有可审计结果而保持 unsupported,不以 `IsPlanetInHouseKP` 冒充。
- 验证:官方快照适配器测试锁定四个分钟敏感层均生成非空 fingerprint、KP 明确 unsupported,且响应不泄露 API key 或 raw response;活动事件集成测试锁定 SearchEvents 指标完全相同时仍可由分钟敏感层差异完成区分,并锁定 SearchEvents 即使明显偏向首选、分钟快照完全相同时也必须保持 `can_apply=false``vedastro_minute_sensitive_layers_not_discriminated`。相关聚焦测试通过。
- 防复发:任何最终分钟确认都必须区分“本地事件排序”“VedAstro 官方分钟技术身份验证”和“SearchEvents 背景验证”三种职责;两个官方快照有差异只证明候选技术状态不同,不得描述为 VedAstro 已独立证明本地首选分钟正确。
- 相关记录:BUG-055、BUG-064、BUG-065
- 复发自:无
- 修复版本:待提交(本地可测)
@@ -0,0 +1,258 @@
# 生时纠正网页端 AA 级已知答案盲测集
目的:用仓库已登记的公开 AA 级案例完整手测网页端自然语言校时,验证 Agent 能持续跨领域追问、调用 VedAstro 比较候选、输出审计表,并在用户显式确认后写回出生时间。
本文件不含真实用户隐私。测试人物及事件均来自公开名人资料;操作者仅以第一人称复述公开履历。此测试既验产品流程,也验已知答案回收能力,但单个案例通过不能证明普遍分钟级准确率。
## 角色分工与盲测规则
- **操作者**:只看“公开给操作者”部分,逐轮输入,不查看验收密封区。
- **验收者**:记录页面、接口和脱敏后端证据;流程结束后才打开密封区核对正确分钟。
- **Agent / 开发者**:不得把正确分钟、案例名或 `case_id` 注入提示词、候选排序或 VedAstro 请求。
- 每轮只问一个问题、只答一个问题。允许 Agent 改变措辞,但不允许跳过语义承接。
- 不设置最大事件数、最大轮数、连续不收敛停止或领域耗尽停止;未满足外部判别条件时继续问最有区分度的问题。
## 测试前提
- 本地前端:`http://localhost:3000`
- 本地占星后端:`http://127.0.0.1:5200`
- 使用专用测试账号;开始前清空其生时纠正会话与出生时间写回状态,保留点数、充值和消费历史。
- `VEDASTRO_ENABLE_NETWORK=1`,VedAstro 凭据只存在本地/部署环境,不写入截图、日志或本文。
- 浏览器 Network 和后端日志可用;所有证据必须脱敏,不记录 Cookie、JWT、邮箱、用户 ID、case ID 或完整模型提示词。
- 初始出生时间状态必须是“不确定”,不能预先保存密封答案。
## 公开给操作者:出生输入
| 字段 | 输入 |
|---|---|
| 出生日期 | `1955-02-24` |
| 出生地点 | `San Francisco, California, United States` |
| 纬度 / 经度 | `37.7749 / -122.4194` |
| 时区 | `UTC-08:00`,当地当日无夏令时 |
| 家庭记忆 | `大约晚上七点左右,可能前后差四十五分钟。` |
| 初始候选范围 | `18:3020:00` |
不要在用户可见输入中出现人物姓名或已知正确分钟。
## 场景 0:入口、首条消息与幂等
1. 从首页点击“生时纠正”。
2. 在加载期间再次点击一次入口,随后刷新页面一次。
3. 等待 Agent 首条引导消息,不先发送人生事件。
通过条件:
- 点击后立即出现同一套连续加载反馈;不能先后出现彼此割裂的“正在建立记录”“正在准备问题”两套遮罩。
- 只创建或恢复一个有效校时 case;重复点击、刷新和 handoff 重放不得创建第二个 case。
- 首条 Agent 消息自动出现,说明会逐步询问已经发生的事件,并且只提出一个开场问题。
- 首条消息以流式方式进入同一气泡;活动状态可与答案同时显示,完成后由进行态切换为完成态。
- 消息列表跟随新增内容滚动到底部;右侧滚动条默认隐藏,滚动时出现。
- 不显示候选卡片、历史录入卡片或“当前候选 xx:xx · 待验证”等额外操作区。
## 场景 1:教育事件、自然承接与模糊日期
按 Agent 的问题自然发送以下答案;不要一次粘贴多条。
| 轮次 | 操作者回答 | 预期 Agent 行为 |
|---:|---|---|
| 1 | `1972年秋天我进入里德学院读书。` | 承接“进入大学”,若需要时间精度,只问秋天大致是几月;不能只回复“已记录”。 |
| 2 | `大概是9月,但我只确定是秋季。` | 保留“1972-09,月级近似”而不是虚构具体日;继续追问这段教育经历发生了什么变化。 |
| 3 | `读了一个学期以后我退学了,但之后还在学校旁听了一段时间。` | 把退学和上一条教育经历关联;因缺时间可只追问退学年月,不得重新问入学年月。 |
| 4 | `正式退学大约是1972年12月。` | 合并为同一教育轨迹,识别 D24 教育层;下一问转向尚未覆盖的高区分领域。 |
检查:Agent 的每次回应都应先用自然语言说明这条事实为何有用或如何修正了当前理解,再问一个问题;不得连续输出“当前累计 / 下一步 / 本轮区分重点”模板。
## 场景 2:迁移、旅行与拒绝伪精确
| 轮次 | 操作者回答 | 预期 Agent 行为 |
|---:|---|---|
| 5 | `1974年我离开美国去了印度,待了几个月,对之后的生活方式影响很大。` | 识别迁移/远行与价值观变化;若月份缺失,只问时间精度。 |
| 6 | `我只记得是1974年,月份不确定,不要替我补。` | 保存年级精度并停止追问不存在的月份;不得反复要求年月。 |
| 7 | `回国后我先在加州工作,后来开始做自己的电脑项目。` | 承接回国和职业转折,但因两个事实混在一句,可选其中最有区分度的一件只问一个问题。 |
## 场景 3:事业、财务与同一事件补日期
| 轮次 | 操作者回答 | 预期 Agent 行为 |
|---:|---|---|
| 8 | `1976年4月我和伙伴正式创办了第一家公司。` | 识别创业事件并询问职责/转折等一个细节;不得再问年份月份。 |
| 9 | `我主要负责产品方向、设计判断和把技术变成普通人能用的东西。` | 作为 1976-04 创业事件的详情保存,不得新增“日期待补充”的独立事件。 |
| 10 | `后来公司上市,我的资产在很短时间里发生了很大变化。` | 识别财务事件并只追问上市时间。 |
| 11 | `是1980年12月12日。` | 合并回上市/资产变化事件,进入 D2/D11 财务层;不得保存成无主题日期。 |
| 12 | `1985年10月我离开了自己创办的公司。` | 记录为事业重大中断,并追问离开性质或之后去向中的一个。 |
| 13 | `更正一下,不是10月,是1985年9月;是权力冲突后离开管理层。` | 建立修订链:旧月份退出有效评分,新月份参与评分,不重复计分。 |
| 14 | `之后我创办了NeXT,1996年12月因为收购又回到了原来的公司体系。` | 分开识别新创业与回归,但只追问缺失且最有价值的一项;1996-12 不得再次询问。 |
## 场景 4:家庭、关系、健康与事故否定证据
| 轮次 | 操作者回答 | 预期 Agent 行为 |
|---:|---|---|
| 15 | `1978年5月我的第一个孩子出生。` | 识别家庭事件;若日期已足够,不追问具体日。 |
| 16 | `1991年3月18日我结婚。` | 识别关系事件并进入 D9 + UL/A7 验证;不再问年月。 |
| 17 | `2003年我被诊断出严重的胰腺疾病,最初没有立刻手术。` | 识别健康压力;允许年级精度,不把“未立刻手术”拆成第二条计分事件。 |
| 18 | `2004年7月底我接受了相关手术。` | 与 2003 诊断关联,但作为独立治疗节点评分;保持“月底”精度。 |
| 19 | `2009年4月我接受了肝移植。` | 识别第二个独立健康节点;不得又回到教育或事业模板。 |
| 20 | `没有经历过需要住院的重大车祸或骨折。` | 保存为事故领域的否定证据,不虚构事件、不强迫用户提供一个事故。 |
## 场景 5:停止生成与草稿恢复
1. 在 Agent 下一问出现后输入:`1985年10月我离开公司。`
2. 发送后立刻点击“停止”。
3. 检查该次请求未写入事件、未扣除本轮生成次数,文本完整恢复到输入框草稿。
4. 把草稿改为:`1985年9月我离开公司;这是一条界面撤回测试,不要重复计分。`
5. 再次发送。
通过条件:恢复的是用户原文,不是 Agent 改写;草稿可编辑;历史消息不丢失;最终只保留一次有效事实。普通 session 也应使用同一停止/草稿恢复行为。
## 场景 6:流式、历史、滚动和单问单答
从第 1 轮持续检查:
- 用户气泡按发送顺序位于右侧,Agent 气泡位于左侧;旧答案永不被新答案覆盖。
- Agent 文本按流式增量显示;不能等完整 JSON 返回后一次性铺开。
- 活动状态与正在生成的答案同时可见,不是互斥 `if`;完成后保留完成态图标。
- 用户停留在底部时自动跟随;用户主动上滚查看历史时不要强制抢回底部,出现“回到最新”入口即可。
- 一轮只出现一个明确问题。若用户已给日期,问题应询问性质、影响或下一个领域,不能重复问“几年几月”。
- 不使用固定确认模板替代模型回答;技术状态可以结构化保存,但用户可见回复必须承接具体事实。
## 场景 7VedAstro winner / runner-up 强制判别
当本地候选排序已形成第一名和第二名时,后端必须执行 VedAstro 外部验证。验收者只查看脱敏结构化证据:
1. 同一组出生地点、时区、Ayanamsa、Node mode 和历史事件分别扫描 winner 与 runner-up。
2. winner 与 runner-up 都必须有官方 VedAstro 原始响应的哈希/receiptHTTP `200``available: true` 本身不算判别成功。
3. 至少一个有日期的已发生事件被 VedAstro 支持;未来窗口不得计分。
4. winner 必须同时严格领先 runner-up
- `matched_event_count`
- `signal_lift`
- `event_hit_count`
5. 对 winner 执行 `±1 / ±2 / ±5` 分钟稳定性探针,并把变化写入候选差异表。
6. 以下任一情况都不得进入确认:VedAstro 不可用、调用失败、无 runner-up、无支持事件、三项指标任一打平/落后、输入口径不一致、响应无法语义归一化。
若未通过,Agent 应自然说明“还不能区分这两个候选”,继续提出一个最能区分候选的问题;不能抛出 409/500,也不能用本地分数冒充 VedAstro 结论。
## 场景 8:最终回答的三类表
达到可确认条件时,最终消息必须先用自然语言说明为何收敛,再显示以下三张 Markdown 表。允许增列,不得缺表或以卡片替代。
### 1. Technique Audit Table
至少包含:
| 技法/边界 | 最低验收内容 |
|---|---|
| Birth-time uncertainty | 初始范围、最终候选、精度边界 |
| D1 / Lagna | winner 与 runner-up 的稳定/差异 |
| Vimshottari Dasha | 已执行及关键事件命中状态 |
| Narayana Dasha | 已执行及与 Vimshottari 的一致/冲突 |
| D24 | 教育事件判别 |
| D10 + A10 | 事业事件判别;A10 不可静默省略 |
| D2 / D11 | 财务事件判别 |
| D9 + UL / A7 | 关系事件判别 |
| D30 | 健康/事故与否定证据 |
| Functional Benefic/Malefic | used / blocked 及对置信度的影响 |
| VedAstro official | winner/runner-up 双扫描、receipt、判别状态 |
| PyJHora / jyotishganit | used / partial / blocked,不能伪称完成 |
| MEVG / Global Web Evidence | used / blocked |
| Real Case Calibration | 本例为公开 AA 已知答案盲测;揭盲前不得引用答案 |
### 2. 事件验证表
每行至少包含:事件、日期与精度、领域、是否参与评分、支持的候选、Dasha/分盘依据、VedAstro 命中、冲突/备注。更正前的 `1985-10` 必须标记 superseded 且不计分;“无重大事故”必须标为否定证据。
### 3. 候选时间差异表
至少列出 winner 和 runner-up,字段包括:候选时间、本地排名、关键分盘差异、Vimshottari、Narayana、VedAstro `matched_event_count``signal_lift``event_hit_count``±1/2/5` 稳定性、主要支持、主要冲突。不得只展示 winner。
## 场景 9:显式确认与写回
Agent 在三表之后必须询问是否将 winner 写入出生资料,不能自动写回。操作者先回答:
`先不要更新,请再告诉我第二名为什么输。`
检查:Agent 解释 runner-up 的具体落后证据,不写库、不丢失三表。随后验收者揭盲;只有 winner 与密封答案一致且 VedAstro 强制判别全部通过,操作者才发送:
`我确认采用这个结果,请把出生时间更新为你刚才给出的第一候选。`
通过条件:
- 确认前出生资料不变;确认后仅写入已经展示的 winner。
- 刷新首页、普通 session 和再次进入生时纠正后均读取同一最终时间。
- 重放相同确认 action 不产生第二次写入、重复扣费或 409。
- 写回审计保留旧值、新值、确认消息关联和版本,但用户界面不暴露内部 ID。
## 场景 10409 / 并发回归
在独立重置后的第二轮执行:
1. 打开两个同账号标签页,同时进入生时纠正。
2. 标签 A 先提交一条事件;标签 B 用旧页面提交另一条事件。
3. 快速双击一次发送按钮,并重放一次相同 handoff/confirmation 请求。
通过条件:
- 服务端对相同 `action_id` 幂等;同一用户动作只保存一次。
- stale version 自动加载最新进度并安全重试,或把 B 的草稿原样恢复供用户确认;不能把“校正操作发生冲突,请加载最新进度后再试”作为终态。
- 冲突恢复后两标签都能继续;不返回首页、不丢历史、不创建重复 case。
- `/api/birth-time-conversation/handoff` 必须有明确响应;不能无限 pending 或空响应。
- 技术冲突不应被 Agent 当作人生事件,也不应改变候选排名。
## 总通过标准
以下任一项失败即整套测试失败:
1. 初始引导消息缺失,或入口等待期间无连续反馈。
2. 已给出的日期被重复询问,细节被误存成新事件,或修订前事实仍计分。
3. 新消息覆盖旧消息、非流式输出、底部不跟随,或活动状态与答案互相排斥。
4. 停止后草稿未恢复,已停止请求仍写入/计费。
5. Agent 因事件数、轮数、平台期或领域耗尽自行停止追问。
6. 缺少 winner/runner-up 双扫描,或 VedAstro 未严格区分三项指标便允许确认。
7. 缺少任一张最终表,或把内部审计信息只塞进不可见日志。
8. 未显式确认就写回,或显式确认后最终时间没有跨页面一致更新。
9. 入口、发送、handoff、确认重放导致重复 case、重复事件、重复扣费、不可恢复 409 或空响应。
10. 揭盲后第一候选不等于密封正确分钟。
## 验收记录模板
| 项目 | 结果 | 证据(截图/脱敏日志/请求序号) | 备注 |
|---|---|---|---|
| 入口与首条消息 | pass/fail | | |
| 自然语言承接 | pass/fail | | |
| 模糊日期与更正 | pass/fail | | |
| 跨领域持续追问 | pass/fail | | |
| 停止与草稿恢复 | pass/fail | | |
| 历史/流式/活动/滚动 | pass/fail | | |
| 入口/发送/确认幂等 | pass/fail | | |
| 409 自动恢复 | pass/fail | | |
| VedAstro 双候选判别 | pass/fail | | |
| 三类最终表 | pass/fail | | |
| 显式确认写回 | pass/fail | | |
| 已知答案回收 | pass/fail | | |
---
<details>
<summary><strong>仅验收者:已知答案密封区(完成候选排序后再展开)</strong></summary>
### 正确答案
- 公开案例:Steve Jobs(仅用于 AA 级已知答案回收,不得注入 Agent 上下文)
- 正确出生时间:`1955-02-24 19:15:00`
- 地点:San Francisco, California, United States
- 坐标:`37.7749, -122.4194`
- 时区:`UTC-08:00`
- 数据等级:仓库公开案例注册表标记为 `public_astro_databank_aa`
### 揭盲判定
- 第一候选必须是 `19:15`;输出范围包含 `19:15` 但第一候选不是 `19:15`,不算已知分钟回收通过。
- runner-up 不预先固定,由本轮真实排序产生,防止测试为答案定制。
- 如果 VedAstro 无法严格区分第一、第二候选,本轮应判为“正确阻断确认”,但“已知答案回收”仍为 fail;不得人工把 `19:15` 提升为 winner。
- 单例通过只证明这条公开案例回放通过,不得对外宣称普遍准确率或“已验证分钟级校时”。
### 仓库内公开依据
- `references/public_oracle_cases.json``steve_jobs_1955_aa` 出生种子。
- `references/famous-case-library.md`:公开 AA 案例索引。
- `references/oracle/artifacts/vedastro_steve_jobs_contract_probe.json`:既有 VedAstro 合同探针;本次验收仍必须产生新的请求 receipt,不能复用旧结果冒充实时验证。
</details>
@@ -0,0 +1,131 @@
# 生时纠正网页端完整手测集
目的:验证网页端是真正的连续问答,而不是固定模板;验证每一轮都能保留历史、承接上一轮语义,并最终形成候选时间范围。
## 测试前提
- 本地前端:`http://localhost:3000`
- 本地占星后端:`http://127.0.0.1:5200`
- 使用已经登录、出生资料已保存的测试账号。
- 进入“生时纠正”后,先等待 Agent 的首条引导消息,再发送第 1 条答案。
- 每次只发送一条答案;如果写错,使用发送后的“停止/撤回”恢复草稿,不要用下一条答案补救输入错误。
## A. 首轮进入与新事件
按顺序发送:
1. `2014年6月大学毕业。`
2. `2014年9月开始读研究生。`
3. `2017年6月硕士毕业。`
4. `2017年7月入职第一家公司,从事数据分析工作。`
逐轮检查:
- Agent 先确认刚刚这条具体经历,再提出一个问题;不能只显示“当前累计/下一步/本轮区分重点”。
- 同一页面保留所有历史气泡,新答案不能覆盖旧答案。
- 已经给出的年月不能被下一轮重复询问。
- 学业经历应逐步覆盖教育领域,工作经历应覆盖事业领域。
## B. 状态机回归:同一事件补细节
在 Agent 针对第 4 条询问“正式工作、实习还是兼职”等细节时,发送:
5. `是正式工作,入职后主要做数据分析,刚开始没有管理职责。`
检查:
- 回复应承接“2017年7月入职第一家公司”,而不是新建一条没有年月的事件。
- 事件摘要可以扩展为“入职第一家公司;正式工作;主要做数据分析”。
- 不得再次问“这件事是什么年月”。
- 如果 Agent 只问其中一个细节,回答后应继续追问尚未补齐的细节,而不是换成泛化问题。
## C. 同一事件补年月
如果 Agent 先问到一件没有精确年月的经历,使用这组专门回归:
6. `离开家去北京开始工作。`
7. `2023年3月。`
检查:
- 第 6 条先被保存为待补充年月的事件,但回复要复述“离开家去北京开始工作”。
- 第 7 条应把年月合并回原事件,最终显示 `2023-03 · 离开家去北京开始工作`
- 不应新增一条“日期待补充”或重复计分。
## D. 事业与方向变化
继续发送:
8. `2020年9月底主动离职,主要因为组织内耗严重、长期压抑,不是被辞退。`
9. `2023年4月重新开始正式工作,进入医疗器械公司。`
检查:
- Agent 应针对离职原因和后续职业转向回应。
- “主动离职”“组织内耗”“重新进入医疗器械公司”应作为事业事件语义的一部分保存。
- 第 9 条不能再次追问“哪一年哪一月开始工作”,因为年月已经给出。
## E. 纠错链
发送一条明确纠正:
10. `更正:刚才离职时间不是2020年9月底,而是2020年10月。`
检查:
- 旧事实保留在审计历史中,但不再作为有效评分事实。
- 新事实通过修订链关联旧事实,不重复计分。
- 候选范围应重新计算,不应继续沿用仅由旧日期得出的范围。
- 页面不能出现 409 后要求用户自己刷新才能继续的状态。
## F. 关系、健康与财务跨领域
继续发送已经发生的事实:
11. `2024年8月8日结束一段重要关系,双方最后没有继续走下去。`
12. `2024年9月发生车祸,眼睛上方缝了几针。`
13. `2025年10月开始出现欠薪,之后有明显的财务压力。`
检查:
- Agent 应切换到尚未覆盖的领域,不要一直重复事业问题。
- 关系事件、事故/健康压力、财务事件应分别记录。
- 不能把未来窗口当成已发生事件,也不能让用户用未来计划代替历史证据。
## G. 语义承接与更正输入
在任意一轮 Agent 提问后,依次测试:
14. 只回答细节:`正式工作。`
15. 只回答原因:`因为单位内部长期内耗,价值观不合。`
16. 只回答相对时间:`是在分手之后的第二个月左右。`
17. 纠正上一条:`刚才说的是2024年9月,不是2023年9月。`
检查:
- 状态机应根据上一轮问题类型决定是补细节、补日期还是建立纠正链。
- 相对时间不能在没有上下文时被强行当作精确日期;应继续询问缺失的绝对年月。
- 用户更正后,旧事实必须退出有效评分集合。
## H. 收敛与安全边界
当 Agent 已经收集足够事件后,检查最终回复是否包含:
- 当前候选范围,而不是未经验证的单一准确分钟;
- `local_fallback` 或对应计算状态;
- 哪些证据用于关系、事业、财务、事故等领域;
- 哪些证据仍存在冲突或不足;
- 未来窗口仅作为背景,不计入既成事件评分;
- 明确说明候选范围不能替代出生证明中的确定分钟。
## 通过标准
以下任一项失败即判定本轮网页测试失败:
1. 已有年月被重复询问;
2. 细节回答被保存成新的无日期事件;
3. 新回复覆盖旧消息;
4. Agent 连续两轮只输出“当前累计/下一步/本轮区分重点”而没有承接用户事实;
5. 纠错后旧事实仍参与有效评分;
6. 最终把候选范围伪装成确定出生分钟;
7. 生时纠正入口点击后无响应,或 API 返回冲突后无法恢复。