Files
Jyotisha/docs/BUG_HISTORY.md
T

732 lines
68 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Bug History
本文件是本仓库产品与代码 Bug 的长期知识库。处理任何 Bug 前先读并搜索本文件;完成修复或确认阻塞后,在同一变更中更新本文件。
基础设施、外部引擎、碎片目录和开工预检类风险仍记录在 `docs/research/pre_work_error_ledger.md`。同一问题若同时影响两类台账,应互相引用,不复制大段内容。
## 使用流程
1. 用报错原文、接口路径、状态码、模块名和用户操作搜索本文件。
2. 找到相似记录时,先验证既有防复发措施是否仍存在,再定位新的回归入口。
3. 修复必须包含与风险相称的自动化测试;生产问题还要记录部署版本和脱敏后的生产验证。
4. 完成后更新已有记录,或按下方模板添加新记录。复发问题必须填写 `复发自`,不能伪装成无关的新问题。
5. 不记录姓名、出生资料、邮箱、用户/案例 ID、Cookie、JWT、密码、密钥、完整请求体或模型原文。
## 状态定义
- `investigating`:现象已确认,根因未确认。
- `blocked`:缺少权限、环境或外部条件,当前无法继续验证。
- `mitigated`:用户影响已被安全降级,但根因或全链路尚未闭环。
- `resolved`:根因已修复,并有自动化或真实环境证据。
- `regressed`:历史问题再次出现;必须关联原记录并说明旧防线为何失效。
## 新记录模板
```markdown
## BUG-NNN | 简短标题
- 状态:investigating | blocked | mitigated | resolved | regressed
- 首次发现:YYYY-MM-DD
- 最近更新:YYYY-MM-DD
- 影响面:页面、接口或模块
- 用户现象:脱敏后的可观察现象
- 触发条件:最小复现路径
- 根因:已验证的技术原因;未知时明确写未知
- 修复:实际采取的最小修复
- 验证:测试、生产 smoke、迁移账本或监控证据
- 防复发:测试、约束、监控或发布门禁
- 相关记录:BUG-NNN / ERR-NNN;没有则写无
- 复发自:BUG-NNN;首次出现则写无
- 修复版本:Git SHA、迁移版本或待发布
```
## 已记录问题
## BUG-001 | 对话删除不可用或只改变前端状态
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-21
- 影响面:会话列表、`DELETE /api/sessions/[id]`、Supabase `chat_sessions`
- 用户现象:用户点击删除后对话无法真正删除,或删除交互与账户弹窗不一致。
- 触发条件:登录后删除自己的一条历史会话。
- 根因:删除曾缺少所有者约束的服务端入口和对应数据库授权,前端也没有统一的站内确认面。
- 修复:增加所有者约束的服务端删除接口与数据库 grant,并统一站内确认弹窗。
- 验证:`tests/test_session_management_entrypoints.py``frontend/tests/chat-session-delete-contract.test.ts`
- 防复发:删除必须经服务端所有权校验;测试同时锁定 API、迁移和前端入口。
- 相关记录:无
- 复发自:无
- 修复版本:`e3d619c``65b8c42``5650ed1``fcf5794`
## BUG-002 | Onboarding 首次请求 409、重复请求或读到旧问题
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-21
- 影响面:`POST /api/onboarding`、首页入门问题缓存与恢复
- 用户现象:出生资料已经填写,第一次请求仍返回资料未完成或 `pending`,随后又请求一次并返回缓存结果。
- 触发条件:入门资料完成、缓存生成尚未结束或资料在并发生成期间发生变化。
- 根因:缓存就绪/处理中状态没有完整绑定当前资料指纹,旧请求完成时可能覆盖新资料的生成结果。
- 修复:用全部决策字段生成无明文 SHA-256 身份;领取和完成都使用版本、时间戳和 pending 身份 CAS,并校验账户所有权。
- 验证:onboarding route/cache/candidate completion 测试矩阵,历史完整前端套件 455/455。
- 防复发:缓存命中、pending、超时回收和完成写入必须绑定同一资料身份;任何旧请求只能返回安全 `pending`
- 相关记录:无
- 复发自:无
- 修复版本:`28d58d4``5ee925c``308e5d5``e13d595``346ec02``a9ffbd9`
## BUG-003 | 浏览器原始报错 “The string did not match the expected pattern” 泄露给用户
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-21
- 影响面:生时校正自动流程、候选时间确认、“都不符合”分支
- 用户现象:选择候选时间或“都不符合”后直接看到浏览器英文 DOMException。
- 触发条件:自动或手动生时流程中的底层浏览器/传输异常进入未归一化错误分支。
- 根因:部分 journey effect 直接展示实现层异常信息,没有统一转换为安全、可操作的中文错误。
- 修复:所有相关 mutation/effect 统一经 `birthTimeUserError` 归一化,屏蔽 DOMException、网络和语法实现细节。
- 验证:`frontend/tests/birth-time-user-errors.test.ts` 包含该英文原文回归用例。
- 防复发:禁止 `setError(caught.message)`;新增异步入口必须走统一错误映射。
- 相关记录:无
- 复发自:无
- 修复版本:`a38a096`
## BUG-004 | 点击生时校正后先进入中间卡片,失败时无法自然回到首页
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:首页生时校正入口、首轮加载与恢复 UI
- 用户现象:点击入口后先看到“开始生时校正”或“正在恢复账户里的校正进度”卡片;依赖失败时停留在重试卡片。
- 触发条件:首轮校时 RPC 尚未返回或返回失败。
- 根因:页面在首轮有效 turn 生成前就切换到专用校正会话。
- 修复:首轮生成期间保持首页;只有有效首轮 turn 返回后才进入校正会话,失败以首页输入区提示呈现。
- 验证:`frontend/tests/consultation-entrypoint.test.ts` 及生产入口 smoke。
- 防复发:会话切换以“首轮可展示结果”而非“请求已发出”为边界。
- 相关记录:ERR-087
- 复发自:无
- 修复版本:`dc0077e`
## BUG-005 | 保存出生时间返回 `PATCH /api/account` 500
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:账户出生资料、Supabase `profiles`
- 用户现象:选择记录时间和误差后显示“暂时无法保存账户资料”,接口返回 500。
- 触发条件:账户已被候选时间写入 reported 字段,之后再次编辑原始出生时间声明。
- 根因:旧触发器把候选/活动时间复制为用户报告时间,同时数据库又把报告时间视为不可变字段。
- 修复:报告时间保持可编辑,禁止候选时间反写原始声明,修复不一致历史行并增加来源/时间一致性约束。
- 验证:迁移 `20260721140000` 已进入生产账本;生产合成账户修改与恢复均返回 200。
- 防复发:候选、活动、用户报告时间保持独立语义;profile persistence 测试锁定迁移行为。
- 相关记录:ERR-088
- 复发自:无
- 修复版本:`dc0077e``20260721140000_repair_reported_birth_time_revision.sql`
## BUG-006 | 生时校正首轮在没有历史事件时进入确认态并返回 409
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:`POST /api/birth-time-conversation` 首轮、计费释放
- 用户现象:等待约一至两分钟后首轮返回 `action_conflict`,费用预留被释放。
- 触发条件:技术扫描在零条历史事件时直接返回 `ready_for_confirmation`
- 根因:应用构建了 `confirming` 首轮,而数据库契约只允许首轮为 `active`;技术就绪错误绕过了三条有效历史事件的业务门槛。
- 修复:所有技术包统一经过 `MINIMUM_SCOREABLE_EVENTS` 门禁;未满三条时清除 result ID,并保持 `pending_validation/active`
- 验证:核心 orchestrator/e2e 回归测试;生产首轮随后成功创建并收费一次。
- 防复发:确认态只能由三条以上有效、已发生、可评分事件触发。
- 相关记录:ERR-089
- 复发自:无
- 修复版本:`b8ed740`
## BUG-007 | `finance` 证据通过应用校验但被数据库拒绝为 409
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:生时校正技术包、事件证据、Supabase durable validators
- 用户现象:真实首轮计算完成后仍返回统一的 `action_conflict`
- 触发条件:D2/D11 产生 `finance` 建议主题,或摘要包含应用已支持的 `domain` 字段。
- 根因:TypeScript 已支持 `finance` 和摘要 `domain`,初始 SQL 枚举及表约束仍是旧版本。
- 修复:向前迁移同步 evidence request、life event、private candidate、public recap 和事件表约束。
- 验证:迁移 `20260721150000` 已进入生产账本;迁移契约测试和生产首轮 smoke 通过。
- 防复发:应用证据枚举变更必须同时更新 durable SQL,并由迁移契约测试检查。
- 相关记录:BUG-006、ERR-090
- 复发自:无
- 修复版本:`b981c4e``20260721150000_align_conversational_finance_domain.sql`
## BUG-008 | 后续证据把范围压得过窄后返回 503
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:生时校正后续回答、技术包构建
- 用户现象:首轮、“都不符合”、暂停恢复均正常,但提交后续明确事件时返回 `service_unavailable`
- 触发条件:事件评分产生的窄区间不足两个时间样本或不足两个可区分分盘主题。
- 根因:这是“证据仍不足”的正常业务状态,代码却把它作为 `TypeError` 依赖故障终止。
- 修复:显式分类区间区分度不足;撤回本次过度收窄,保留上一候选范围、评分证据和未确认状态,然后继续提问或安全保存范围。
- 验证:核心回归 85/85;生产从失败点续跑通过;全新生产 smoke 覆盖资料、首轮、“都不符合”、暂停恢复、多事件、范围终态、计费和问题交接。
- 防复发:任何新候选范围必须先满足技术证据契约;不满足时回退,不返回 503,也不伪造确定分钟。
- 相关记录:ERR-091
- 复发自:无
- 修复版本:`b981c4e`
## BUG-009 | 未校正的已填报时间被降级为零星盘咨询
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生日初始化、普通咨询路由、生时校正提示
- 用户现象:已填写具体出生时间但没有完成生时校正时,普通问题仍提示只能回答一般知识或必须先完成校正。
- 触发条件:出生时间来源包含有效具体分钟,但状态不是 `confirmed`,且当前聊天没有旧的临时授权状态。
- 根因:前端把“使用未校正填报时间”设计成逐会话授权;没有授权时默认退回 `general_no_birth_time`,因此完全绕过现有的未校正星盘安全模式。
- 修复:具体填报时间现在自动进入 `unverified_birth_time`,保留禁止精确应期的安全边界但不禁用个人分析;无具体分钟时直接进入无分钟模式;移除校正 toast、弹窗和每条回答前重复的校正警告,并把生日入口收敛为“知道准确时间 / 不确定准确时间”两个选择。
- 验证:`frontend/tests/birth-time-consultation-consent.test.ts``frontend/tests/consultation-entrypoint.test.ts``frontend/tests/consultation-birth-time-mode.test.ts``frontend/tests/birth-time-intake.test.ts`
- 防复发:咨询路由测试锁定“有效填报分钟无需授权即可使用”;页面契约禁止重新引入生时校正 toast 或阻断式选择。
- 相关记录:BUG-003、BUG-004
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-010 | 浏览器直连 Supabase 写会话泄露 `TypeError: Load failed`
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:回答完成后的聊天记录持久化、移动 Safari 错误提示
- 用户现象:回答已经生成,但页面反复显示“云端同步失败:TypeError: Load failed”,并要求复制保存后重试。
- 触发条件:浏览器直接向 Supabase `chat_sessions` 发起跨域写入时发生传输失败。
- 根因:会话读取和多数业务写入已经使用同源 Next.js API,但会话创建与更新仍由浏览器客户端直写 Supabase;异常原文又被拼进回答错误区域。
- 修复:新增同源 `POST /api/sessions``PATCH /api/sessions/[id]`,服务端校验登录、所有权和写入负载;客户端对可重试失败短重试一次,并把最终失败降级为输入区状态提示,不再把浏览器异常原文渲染成回答错误。
- 验证:`frontend/tests/chat-session-write.test.ts` 覆盖同源路由、所有者约束、短重试和 `Load failed` 脱敏;相关咨询与资料回归测试通过。
- 防复发:会话写入契约禁止页面直接调用 `supabase.from("chat_sessions")`;网络异常必须映射为稳定用户文案。
- 相关记录:BUG-001、BUG-003
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-011 | 对话消息暴露内部证据审计状态
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:普通问答消息、Agent 回答顶部区域
- 用户现象:回答正文上方显示“证据状态:not-applicable”和“证据链摘要 · not-applicable”等工程审计信息,正文下方还显示单条回答的报告下载控件。
- 触发条件:任意已完成的 Agent 回答,尤其是后端返回 `not-applicable` 时。
- 根因:消息行对每条非思考态 Agent 消息无条件渲染 claim boundary 与 Technique Audit Table;未识别状态又直接回退显示原始状态值。
- 修复:从普通聊天消息行移除内部证据徽章、审计面板和单条回答报告下载控件;证据状态和 workflow receipt 仍随消息保存并供内部约束使用。
- 验证:`frontend/tests/claim-boundary-badge.test.ts``frontend/tests/evidence-audit-panel.test.ts``frontend/tests/consultation-report-export.test.ts` 锁定聊天消息不再挂载这些内部组件。
- 防复发:聊天消息渲染契约禁止直接展示 `techniqueTruth``workflowReceipt`;需要运营或调试时使用独立的受控界面。
- 相关记录:BUG-009
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-012 | 自动生时流程重构后再次直出实现层错误
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正自动出题与评分轮询、`use-birth-time-automatic-journey-effects`
- 用户现象:自动流程失败时可能再次显示底层异常原文,而不是稳定、可操作的中文提示。
- 触发条件:自动出题或评分轮询 Promise 进入异常分支。
- 根因:出生时间流程重构保留了 automatic effect 中直接读取 `caught.message` 的旧分支;原回归测试后来只检查 guided hook,因此没有锁定 automatic hook 的两个入口。
- 修复:automatic effect 的出题与轮询异常统一经过 `birthTimeUserError`;回归测试同时检查 guided 与 automatic 两个 hook,并禁止两种直接展示 `caught.message` 的写法。
- 验证:`frontend/tests/birth-time-user-errors.test.ts`
- 防复发:错误归一化测试按入口文件验证安全不变量,不再依赖单文件精确调用次数。
- 相关记录:BUG-003
- 复发自:BUG-003
- 修复版本:待提交(PR #25
## BUG-013 | 精简生时校正文案后真实 Chromium 测试等待已删除标题
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:`frontend/tests/conversational-rectification-component.test.ts`、前端完整质量门禁
- 用户现象:页面已经正确渲染候选时间、经历和输入控件,但测试持续等待并最终报 `Timed out waiting for async initial turn`
- 触发条件:运行真实 Chromium 390px 组件回归测试,并把 active turn 注入精简后的生时校正界面。
- 根因:产品把重复的“当前判断”叙事改成“已记录”行动文案后,浏览器测试仍以旧标题作为异步渲染完成信号。
- 修复:测试改为等待候选代表时间与已记录经历两个稳定结构信号,不再绑定可变标题文案。
- 验证:`frontend/tests/conversational-rectification-component.test.ts`
- 防复发:真实浏览器等待条件优先绑定语义结构和状态数据,不用已批准可调整的展示标题充当加载边界。
- 相关记录:BUG-004
- 复发自:无
- 修复版本:待提交(PR #25
## BUG-014 | 能力审计新增案例验证主题后质量门禁仍断言旧主题集合
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:`tests/test_api_server_security.py`、Python quick quality gate
- 用户现象:能力审计正确返回 `Case Validation` 并将案例验证评为可产品化,但质量门禁仍按旧主题集合和 `thin` 等级失败。
- 触发条件:运行能力审计测试,且应用可见主题已包含案例验证入口。
- 根因:案例验证能力进入 `_app_visible_topics` 后,对应主题集合和 UX 等级断言都没有同步更新。
- 修复:把 `Case Validation` 纳入预期可见主题,并把 `case_validator``thin` 队列移入 `excellent` 断言,保持测试与同一审计规则一致。
- 验证:`tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources`Python quick quality gate。
- 防复发:扩展应用可见主题时必须同时更新能力审计契约测试;精确集合断言继续用于发现意外增删。
- 相关记录:无
- 复发自:无
- 修复版本:待提交(PR #25
## BUG-015 | 分钟校正 safeguards PR 与主线证据契约分叉
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:分钟候选输入身份、公共 holdout 校验、三引擎证据包、PR quality gate
- 用户现象:PR 与 `main` 在五个文件发生冲突,聚焦测试通过但完整 quick quality gate 在 API 安全测试收集阶段失败。
- 触发条件:基于旧基线开发 v1 holdout safeguards,同时主线独立升级到 v2/v3 证据契约并移除旧 API store 导入。
- 根因:PR 复用了旧 holdout 字段和 `true node` 默认,未执行与 CI 相同的 quick quality gate;主线已经采用 `mean node` 和更严格的 sealed holdout schema。
- 修复:以主线为准新增兼容的输入指纹、邻近分钟探针和语义哈希;新增 v4 独立审核、日级事件、假分钟承诺门禁及非生产 intake;不恢复旧自动评估循环。
- 验证:分钟校正聚焦回归、脚本直接执行检查、Ruff、Python compilation 和 quick quality gate。
- 防复发:新 safeguards 必须以当前 schema 向前升级;候选身份必须继承产品计算默认;PR 验收必须包含 workflow 实际执行的 quick quality gate。
- 相关记录:BUG-009、BUG-014、ERR-045、ERR-053、ERR-086
- 复发自:无
- 修复版本:d7d9703
## BUG-016 | 生时校正首轮被扫描稀疏度或候选分区超时打成 503
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:首页“先完成生时校正”、`POST /api/birth-time-conversation` 首轮技术包
- 用户现象:点击生时校正后仍停留在普通咨询页,并显示“生时校正暂时无法继续”;接口返回 503。
- 触发条件:候选扫描包含真实但不连续的时点差异,或动态候选分区依赖在 45 秒内没有返回。
- 根因:技术包只承认相邻分钟切换,错误拒绝了稀疏扫描中的真实范围差异;同时首轮把用于改善问题排序的候选分区依赖当成不可降级的硬依赖。生产版本 `b981c4e` 的日志分别记录了 `RectificationTechnicalPacketRangeError``candidate_differences TimeoutError`
- 修复:稀疏扫描差异改用明确的范围级文案,禁止伪称相邻分钟切换;超过六小时的宽范围首轮跳过分钟级候选分区,较窄范围在候选分区发生已知超时、取消、配置或服务故障时保留服务端扫描证据并继续收集,不再返回 503;未知编程或契约错误仍失败关闭。
- 验证:`frontend/tests/conversational-technical-packet.test.ts``frontend/tests/conversational-rectification-route.test.ts`、真实 Chromium 390px 组件回归和 `npm run build` 通过;完整前端套件 844 项中 837 项通过,余下 7 项均为沙箱 Chromium / PostgreSQL 端口环境失败,针对性 Chromium 在沙箱外 8/8 通过。
- 防复发:首轮必须区分核心扫描失败与可降级的候选排序依赖;稀疏差异文案必须显式否认相邻分钟切换;生产验收必须核对接口状态与实际进入校正会话。
- 相关记录:BUG-004、BUG-008、ERR-091
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-017 | 候选范围终态继续原问题必定返回 409
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正范围终态、`POST /api/consult`、durable 原问题交接
- 用户现象:生时校正安全结束并保存候选范围后,点击“继续原问题”仍提示原问题状态变化,接口返回 409。
- 触发条件:分钟确认发布门禁未通过,校正以 `candidate.status=pending_validation` 完成;页面按未确认分钟选择 `unverified_birth_time` 并携带 durable handoff。
- 根因:页面正确区分确认分钟与保存范围,但咨询接口把所有 handoff 硬限制为 `verified_chart`;范围终态因此在计费和模型调用前被拒绝。
- 修复:handoff 允许 `verified_chart``unverified_birth_time` 两种星盘模式,继续拒绝 `general_no_birth_time`、旧校正入口和不一致 request ID;分钟确认门禁保持不变。
- 验证:生产合成账号复现 `409 mode_changed``frontend/tests/consultation-birth-time-mode.test.ts` 锁定范围终态模式与一般咨询隔离;发布后需复跑 durable claim、正常咨询和聊天删除 smoke。
- 防复发:范围终态和精确确认必须分别覆盖 continuation 模式;不得把“拥有 handoff”错误等同于“已经确认出生分钟”。
- 相关记录:BUG-008、BUG-009、BUG-016、ERR-085、ERR-091
- 复发自:无
- 修复版本:待提交(生产 smoke 待当前精确 SHA 发布后补齐)
## BUG-018 | 首次保存未确认出生时间没有写入档案状态
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:账户出生资料保存、未确认星盘咨询、生时校正后继续原问题
- 用户现象:已经保存“大概时间”等出生资料,但继续咨询时仍提示出生时间状态发生变化,接口返回 409。
- 触发条件:档案没有旧的 active minute、legacy minute、candidate 或 case pointer,第一次经账户接口保存未确认出生时间声明。
- 根因:账户写入只在清除旧候选结果时补写 `birth_time_status=reported`;干净档案和首次 upsert 被提前跳过,留下完整声明但空状态,随后被服务端星盘真值校验拒绝。
- 修复:所有未确认声明变更都原子写入 `reported` 并清空不可沿用的应用结果;首次创建档案时也写入同一状态;确认分钟仍禁止被普通资料编辑覆盖。
- 验证:`frontend/tests/account-api.test.ts` 覆盖空状态档案与首次档案写入;生产使用认证账户重新保存合法声明后复跑范围终态 handoff、模型咨询、计费和会话删除。
- 防复发:出生声明与 `birth_time_status` 必须由同一次服务端写入建立,不允许依赖后续校正流程补齐状态。
- 相关记录:BUG-009、BUG-017
- 复发自:无
- 修复版本:本次自动滚动修复提交
## BUG-019 | 原问题交接租约过期后永久显示处理中
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正范围终态、跨设备/失败重试、durable 原问题 handoff
- 用户现象:一次继续咨询在输出前失败后,刷新仍无法重试,接口返回“原问题状态已经变化”。
- 触发条件:handoff 处于 `claimed``executing`,两分钟租约已经过期,客户端重新加载交接。
- 根因:数据库 projection 只看持久化 state,把过期租约仍投影为 `in_progress`;客户端因此不会发起 claim,而执行 RPC 又正确拒绝过期租约,形成永久卡死。
- 修复:projection 将已过期的 `claimed`/`executing` 租约投影为 `pending`,复用既有加锁 claim RPC 原子接管;未过期租约仍保持 `in_progress`,双设备隔离不变。
- 验证:迁移契约测试锁定过期租约恢复语义;生产制造过期 claim 后重新加载、claim、执行模型咨询和 settle。
- 防复发:所有租约型状态的读取投影必须同时解释 lease deadline,不允许只依赖枚举 state。
- 相关记录:BUG-017、BUG-018
- 复发自:无
- 修复版本:待提交
## BUG-020 | 生时校正把语言问答包装成高阻力卡片表单
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:首页生时校正会话、经历录入、候选核对与更正
- 用户现象:每轮同时出现候选卡、领域按钮、年份与月份下拉、经历回顾卡和确认卡;用户需要理解多层控件才能回答一个问题,移动端尤为费力。
- 触发条件:进入生时校正并提交或更正一条真实经历。
- 根因:后端已有自由文本证据契约,但前端仍用结构化表单和多卡片包装;校正叙事 Agent 也未加载 Jyotish Skill 来生成自然、逐问式的取证措辞。
- 修复:改为一段助理提问加一个自然语言输入框;候选状态和证据历史收进可展开进度区;保留明确确认、更正、持久化、计费、计算和分钟验证门禁;叙事 Agent 加载 Jyotish Skill,但服务端技术包仍是候选事实和确认权限的唯一来源。
- 验证:语言优先组件静态与真实 Chromium 390px 回归 8/8 通过;校正路由与叙事契约 23/23 通过;目标文件 ESLint 和 Next.js production build 通过。
- 防复发:语言问答不得重新拆成领域按钮或日期下拉;Skill 只能改进问题策略与措辞,不得生成、重算或确认候选时间。
- 相关记录:BUG-008、BUG-009、BUG-015、BUG-016、BUG-017
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-021 | 自然语言真实事件在分钟评分入口被静默丢弃
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正自然语言抽取、历史事件持久化、分钟评分、首轮与兜底叙事
- 用户现象:用户已经提供带日期的真实经历,系统仍反复索取证据;首轮回答展示完整 D 层技术清单,却没有用一个具体问题推进取证。19 世纪事件、疾病、手术、事故和丧亲尤其容易无法参与评分。
- 触发条件:日期早于 1900 年;事件属于健康或重大压力;累计有效事件超过 6 条;或叙事模型未满足首轮全量技术层合同而进入确定性兜底。
- 根因:前端日期正则写死 1900–2099;健康类关键词落入 `other`,并在路由中被过滤,尽管后端已有 `health_pressure` 与 D30 评分支持;路由另有独立的 6 条截断;叙事校验强制首轮正文列出全部稳定层、敏感层和值。
- 修复:日期抽取支持 1000–2099 的四位历史年份;健康、事故、丧亲映射到既有 `health_pressure`/D30 合同并贯通持久化、旧数据导入和公开 turn schema;评分截断统一复用 8 条收敛上限;技术包和 validation receipt 继续完整保存,但非最终用户正文只呈现候选范围、未确认边界和一个高信息量问题,确定性兜底也遵守相同语言交互。
- 验证:抽取、叙事、路由聚焦测试 59/59 通过;真实案例回放、编排和端到端契约 65/65 通过;目标文件 ESLint、Python replay/compilation、`git diff --check` 和 Next.js production build 通过。公开 smoke 中 Steve Jobs、Einstein、Marie Curie 分别保留 4、3、4 条可评分事件,产品过滤结果与结构化 oracle 一致。
- 防复发:新增 18xx 中文与 ISO 日期、健康/事故/丧亲、8 条事件上限和单问题兜底断言;技术层可进入服务端证据包,不得重新进入普通会话正文;family/other 仍是持久化背景,不得伪装成已评分领域。
- 相关记录:BUG-008、BUG-009、BUG-015、BUG-016、BUG-020
- 复发自:BUG-020
- 修复版本:待提交(本地可测)
## BUG-022 | 生时校正入口等待首轮请求完成后才切换会话
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:首页生时校正入口、普通咨询中的“先完成生时校正”、侧栏恢复校正会话
- 用户现象:点击生时校正后长时间停留在首页或原咨询 session,直到恢复、建案、计算和会话持久化全部完成才突然跳转,用户容易误以为点击无效并重复操作。
- 触发条件:`start``resume`、durable handoff 或 session persistence 任一请求响应较慢。
- 根因:`openBirthTimeRectification` 虽然先设置了 loading,但专属 session 的插入、激活和校正 surface 的开放都位于首个异步请求之后;surface 还要求首个 turn 已存在,因此 loading 状态无法在目标会话中显示。
- 修复:创建或复用专属 session 后,在任何 `await` 之前立即插入并激活该 session;校正 surface 允许以空 turn 渲染既有“正在建立校正记录…”状态;增加同步 in-flight 门禁防止快速重复点击发出多个启动请求;失败时新建 session 回滚到来源会话,复用 session 则保留可见错误与重试入口。
- 验证:`frontend/tests/consultation-entrypoint.test.ts` 21/21 通过,锁定 session 插入、激活和 loading surface 必须早于首个异步请求,并锁定单请求门禁;目标文件 ESLint、`git diff --check` 和 Next.js production build 通过;真实应用内 Chromium 从首页点击时,在后台恢复完成前已经显示“正在建立校正记录…”,从普通会话切回校正 session 也立即显示目标会话。
- 防复发:首页卡片、普通 session 建议和侧栏恢复必须继续共用同一启动函数;不得重新用首个 turn 是否返回作为 session 可见性的条件。
- 相关记录:BUG-016、BUG-020、BUG-021
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-023 | 初始化地址保存后的加载提示仍指向生时评估
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:新用户初始化流程、出生地点保存后的全屏加载提示
- 用户现象:用户填完出生地址后,页面实际准备进入首页,但加载提示仍显示正在生成生时评估,造成流程去向与界面文案不一致。
- 触发条件:初始化流程完成出生时间填写,并提交最后一步出生地点。
- 根因:出生时间保存和出生地点保存共用 `saving_profile` 展示阶段;该阶段文案仍按旧流程描述为即将生成生时评估。
- 修复:新增独立的 `entering_home` 展示阶段,仅在 `saveOnboardingPlace` 提交地址时使用,显示“正在进入首页 / 出生资料已保存,正在为你准备首页。”;出生时间保存继续使用原 `saving_profile` 阶段。
- 验证:聚焦测试锁定地址提交与 `entering_home` 的绑定及目标文案;目标文件 ESLint 与 `git diff --check` 通过。
- 防复发:初始化步骤的加载文案必须绑定实际导航结果;不得通过修改共享 `saving_profile` 文案改变出生时间提交阶段的语义。
- 相关记录:BUG-022
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-024 | 侧栏会话标题与更多操作被拆成两块
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:聊天记录侧栏、当前会话选中态与更多操作入口
- 用户现象:会话标题显示在一块选中背景中,右侧更多操作却以独立圆形按钮悬在外侧,看起来像两个不一致的控件。
- 触发条件:侧栏展开并选中任意会话。
- 根因:选中背景只应用在左侧 `.session-main`,右侧 `.session-menu-trigger` 又单独使用 `50%` 圆角和悬停背景。
- 修复:把选中、悬停和聚焦背景统一应用到整行 `.session-row`;子按钮保持透明,并移除更多操作按钮的独立圆形底色。
- 验证:侧栏契约测试锁定整行选中背景、透明标题按钮和非圆形更多操作按钮;目标文件 ESLint 与 `git diff --check` 通过。
- 防复发:会话标题和行内操作必须共享同一个行级状态面,不得分别绘制互相竞争的选中背景。
- 相关记录:无
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-025 | 健康压力追问被数据库误判为校正操作冲突
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正首条自然语言回答、`POST /api/birth-time-conversation`、Supabase 对话持久化契约
- 用户现象:用户提交一条带年月的真实经历后等待约一分钟,接口返回 `409 action_conflict`,页面提示加载最新进度后重试;重复提交仍无法进入下一问。
- 触发条件:技术包为下一轮选择 `health_pressure`(健康与重大压力)作为待补证据领域。
- 根因:应用层已把 `health_pressure` 作为正式领域,并允许语言问答每轮只追问一个重点领域;durable SQL 既缺少该领域,又要求 evidence request 至少包含两个领域。完整计算和叙事生成完成后,`save_conversational_rectification_turn` 才以 `conversational_action_conflict` 拒绝公开 turn。
- 修复:新增向前迁移,将 `health_pressure` 同步加入四个 durable validator 和事件证据表约束,并把 evidence request 基数从 2–4 对齐为应用契约的 1–4;保留现有幂等、版本和候选确认门禁。
- 验证:真实浏览器复现确认只有 `conversational_rectification_valid_evidence_request` 失败,其余公开 turn 子结构、事件、回执和私有候选均通过;迁移应用到测试 Supabase 后,用同一条“2023 年 3 月离家来北京工作”重放,`POST /api/birth-time-conversation` 返回 `200`,日志为 `actionKind: answer``resultCategory: success`,事件规范化保存为 `2023-03 · 离家来北京工作`,页面继续自然追问学业经历且输入框恢复可用;聚焦迁移测试锁定四个 validator 与表约束必须共同支持该领域。
- 防复发:应用 evidence domain 枚举新增值时,必须同时更新 TypeScript schema、公开 recap/request、私有候选、事件表约束和迁移契约测试;不得把正常领域扩展映射为通用 409。
- 相关记录:BUG-007、BUG-020、BUG-021
- 复发自:BUG-007
- 修复版本:待提交(测试 Supabase smoke 通过)
## BUG-026 | 首页与会话改版后旧测试阻断生产发布门禁
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:production 发布门禁、首页主题入口、会话输入区、生时校正组件服务端渲染测试
- 用户现象:production 应用构建成功,但完整前端测试有 7 项失败,导致最新 `main` 无法通过发布前验证。
- 触发条件:运行完整前端测试;旧断言仍查找已移除的首页证据预览、固定 180px 模型菜单、无条件输入区类名和旧 `profile` 文案来源;旧手动 workflow 还固定在 Node 20,而当前 `@mastra/core` 明确要求 Node 22.13 以上。
- 根因:首页与普通 session 的布局和文案已经更新,测试契约没有随产品行为同步;`Jyotish Skill Tests` 的 Node 版本也落后于其他 production 门禁和应用依赖声明,使 Node 20 把 GSAP ESM 错误解析成 CommonJS namespace。
- 修复:测试改为锁定当前首页主题选择、内容自适应模型菜单、带首页修饰类的停靠输入区和 `profileDraft` 文案来源;GSAP 使用正式命名导出;手动 production 测试 workflow 与其余门禁统一到 Node 22。
- 验证:6 个相关测试文件、Node 22 完整前端测试、ESLint、production build 与 patch check 全部通过后方可发布。
- 防复发:产品 UI 结构调整时必须在同一提交同步源码契约测试;workflow 的 Node 主版本不得低于已锁定运行时依赖声明的最低版本。
- 相关记录:BUG-022、BUG-024、BUG-025
- 复发自:无
- 修复版本:待提交(production gate
## BUG-027 | 已完成的生时校正会话打开后误建新案例
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正历史会话恢复、范围终态展示、首页继续入口与计费
- 用户现象:范围校正完成后刷新,点击侧边栏原会话会卡在“正在建立校正记录”,或跳到另一个会话并提示加载最新进度;首页同时重新显示开始入口。
- 触发条件:已绑定 case 的生时校正会话对应 `completed`/`abandoned` 终态,而账户接口只投影当前未完成 case;或者账户另有一个更新的未完成 case。
- 根因:会话打开逻辑只按账户当前 case 决定 resume/start,没有优先恢复所点击会话自己的 `rectificationCaseId`;历史终态加载后又会覆盖账户当前可恢复 case,异步账户刷新还拒绝接受 `null` 或另一个最新 case。
- 修复:已绑定的生时校正会话始终以自身 case id 执行只读 resume;只有首页或未绑定会话才按账户当前 case 复用或创建;历史终态不再覆盖另一个未完成 case,终态后账户刷新接受服务端最新投影。
- 验证:入口契约测试锁定历史会话按自身 case 恢复、首页重新校正仍创建独立会话,以及历史终态不覆盖当前未完成 case;发布后用 production 合成账号复查范围终态、刷新恢复、首页入口与余额不变量。
- 防复发:聊天会话的 `rectificationCaseId` 是历史查看的第一绑定键;账户 `rectificationCase` 只代表当前未完成案例,二者不得相互替代。
- 相关记录:BUG-017、BUG-019、BUG-020
- 复发自:无
- 修复版本:待提交(production gate
## BUG-028 | 生时校正收到具体经历后仍重复泛问
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正自然语言追问、事件补充信息合并、会话可见回复
- 用户现象:用户已经描述自己的具体经历,助理没有针对该内容继续追问或反馈,而是重复上一轮的泛化问题。
- 触发条件:用户提供的事件缺少年月并在下一轮单独补时间;或已经回答技术包当前首选领域后,下一轮仍沿用同一领域提示。
- 根因:存在三处断链:会话组件忽略服务端已经生成的针对性 `turn.narrative`,转而根据公开 evidence request 重组固定问题;用户先说事件、下一轮只补年月时,两轮分别保存为“无日期事件”和“无内容日期”,未形成可评分事实;服务端下一问直接采用技术包首选领域,没有排除本轮已经回答的领域。
- 修复:会话组件改用独立的可见叙事函数,优先承接用户最近一条具体但不完整的经历并只追问缺失项;编排器把下一轮仅含日期或仅含内容的补充合并回最近一条待澄清事件,同时用 `correctsEvidenceIds` 保留 append-only 修订链;下一问从技术包允许领域中优先选择尚未回答的领域,并同步覆盖公开 evidence request。
- 验证:相关自然语言抽取、叙事、编排、路由、公开案例回放与端到端测试共 111 条全部通过;回归覆盖“先说离家工作、再补 2023 年 3 月”后合并为 `2023-03 · 离开家去北京开始工作`、具体事件缺日期时回复必须复述该事件并询问年月、关系领域回答后下一问切换到事业领域。目标文件 ESLint 与补丁检查通过。
- 防复发:保持“服务端叙事是用户可见回复真相源”的纯函数测试;每条待澄清证据必须测试跨轮补全与修订链;下一领域必须测试排除有效 recap 已覆盖领域;端到端断言不得只绑定泛化提示词。
- 相关记录:BUG-018、BUG-019
- 复发自:BUG-019
- 修复版本:待提交(本地可测)
## BUG-029 | 生时校正仍有未回答区分领域时提前结束
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正多领域追问、候选范围停滞终止策略
- 用户现象:用户只回答两轮后,系统仍有财务或关系等可区分领域未询问,却直接保存宽候选范围并结束。
- 触发条件:自然语言单轮抽取出多条可评分经历,使累计数量达到最低门槛;候选范围连续两轮未变化,同时技术包仍建议尚未回答的领域。
- 根因:停滞终止只检查可评分经历数量和候选范围是否连续不变,没有检查当前技术包是否仍存在未回答的区分领域。
- 修复:停滞结束前增加未回答建议领域门禁;仍有可区分领域时继续自然追问,全部建议领域已覆盖后才允许按范围停滞安全结束。证据数量上限和无可区分问题终止保持不变。
- 验证:编排器回归锁定学业、搬迁、事业三轮后必须继续询问尚未覆盖的关系领域,补充关系事件后才保存未确认范围;本地真实流程修复前复现为两轮即结束且范围仍为 `04:3006:30`
- 防复发:停滞终止测试必须同时断言候选范围未变化和技术包建议领域已全部回答,不能只按事件条数结束。
- 相关记录:BUG-019、BUG-028
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-030 | 职位和管理职责变化未识别为事业证据
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正自然语言证据分类、事业领域覆盖判断、下一轮追问
- 用户现象:用户已经说明“开始承担管理职责”或“职位发生明显变化”,系统记录事件后仍继续要求事业经历。
- 触发条件:事业变化使用“职位”“任职”或“管理职责”描述,但没有出现“工作”“升职”“职业”等原有关键词。
- 根因:事业领域分类词表缺少常见的岗位和职责变化表达,导致可评分事件被归入 `other`,无法计入事业领域覆盖。
- 修复:将“职位”“任职”“管理职责”加入事业领域分类规则,保留既有日期、评分和持久化契约。
- 验证:抽取器回归覆盖三种带年月的岗位与职责变化表达,均分类为 `career`、保留月份并可参与评分;本地真实流程已复现修复前的漏判行为。
- 防复发:事业领域分类测试必须覆盖入离职、晋升以及不含“工作/职业”字样的职责变化表达。
- 相关记录:BUG-028、BUG-029
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-031 | 生时纠正未收敛仍永久扣费且事件事实未进入评分契约
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时纠正计费终态、事件评分输入、候选计算指纹、用户结果说明
- 用户现象:用户完成多领域问答后只得到宽候选范围,当前排盘时间没有更新,但已永久扣除点数;同时用户描述的具体经历虽然出现在对话记录中,传给分钟候选评分器时只剩领域和日期。
- 触发条件:公开分钟盲测门禁尚未通过,流程按范围结束或用户主动放弃;以及可评分经历带有具体事件摘要时进入生产评分路径。
- 根因:启动结算在候选计算完成后立即把预留点数标记为 `charged`,范围终态和放弃终态没有对应退款;前端评分载荷与 Python 输入规范只序列化 `id/domain/date/precision`,丢弃 `eventSummary`,导致不同事实可能共享同一规范输入。
- 修复:新增向前迁移,在范围完成或主动放弃的同一数据库事务中将已收费状态幂等转换为 `released`、退回点数并更新动作回执;只有通过分钟门禁且用户明确确认的分钟保留收费。评分载荷新增可选 `summary`,Python API 规范化并限制长度,候选输入契约升级为 `rectification-candidate-input-v2`,把具体事件摘要纳入 canonical hash。用户终态文案明确说明未纠正成功、本次不计费、候选代表时间不会替换当前排盘时间。
- 验证:前端生时纠正路由、编排器、存储和旅程引擎聚焦测试 77 条通过;Python 对话数据库契约、事件 API 与候选评分测试 67 条通过。回归明确断言事件摘要变化会改变 canonical input hash,但 `can_apply``confirmation_allowed``can_narrow_to_minute` 仍保持关闭;SQL 契约断言范围结束与放弃均在终态事务内退款,且迁移不写入 `active_birth_time`
- 防复发:任何新增事件评分字段都必须进入前端载荷、Python 规范化输入和 canonical hash;任何非 `confirmed` 终态都必须有幂等计费回收断言;不得用候选范围中点或代表时间替换用户的当前排盘分钟。真正分钟确认仍依赖独立来源审计、sealed blind replay、准确率/误确认率及 ±1/2/5 分钟稳定性门禁。
- 相关记录:BUG-019、BUG-028、BUG-029
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-032 | 生时校正覆盖历史回答且完成后无法返回原问题
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正逐轮问答、Agent 可见叙事、完成态原问题交接
- 用户现象:多轮回答后,用户经历全部连续显示在右侧,页面只保留最新一条 Agent 回复;Agent 收到具体事项后仍重复模板式说明;完成后点击“返回原问题”没有可见跳转。
- 触发条件:在同一个生时校正 case 中连续提交两轮以上经历;或从普通咨询问题进入校正并走到完成态。
- 根因:聊天区直接遍历 `evidenceRecap` 生成全部用户气泡,却只渲染当前 `turn.narrative`,因此旧 Agent 回复天然被覆盖;可见叙事层和编排器在 Agent 生成回答后又用固定进度文案覆盖,且 Agent prompt 没有收到最新具体事件;完成态页面会提前自动领取 continuation,handoff 缺失时又错误地把当前校正 session 当作来源 session。
- 修复:controller 为当前 case 维护交错的 `assistant/user` 消息序列,成功提交后原子追加用户原话与新 Agent 回复,聊天区只按该序列渲染;可见层优先采用 Agent 原始 narrative,中间轮 prompt 带入最新事件并要求先具体回应再追问,编排器保留通过校验的 Agent 文本;删除完成态自动 continuation,只在用户点击时领取,并按本地 handoff、持久化 return session、最近普通咨询 session 的顺序寻找真实返回目标。
- 验证:聚焦 controller、Agent narrative、visible narrative、orchestrator 和首页 handoff 测试 89 条通过,覆盖 `assistant → user → assistant` 历史、具体事件进入 prompt、旧模板不覆盖 Agent 回答、仅点击后返回来源 consultation session。组件 SSR 测试在当前 Node 环境仍被仓库既有 GSAP ESM `registerPlugin` 加载错误阻断,尚未进入业务断言。
- 防复发:主聊天区不得从 evidence recap 反推当前会话消息;任何 Agent narrative 后处理不得抹掉已校验的具体回应;完成态 continuation 不得自动领取,也不得以当前 rectification session 作为来源会话兜底。若需要刷新后永久恢复每轮 Agent 原文,必须扩展服务端 turn/RPC 持久化契约,不能用当前 narrative 冒充完整历史。
- 相关记录:BUG-018、BUG-019、BUG-028
- 复发自:BUG-018、BUG-028
- 修复版本:待提交(本地可测)
## BUG-033 | 生时校正缺少连续事件语义导致模板式跳问
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正中间轮 Agent prompt、事件纠错与追问顺序、聊天区可见回答
- 用户现象:用户已经补充某件经历的原因、主动或被动、结果或后续转折,Agent 没有继续处理当前事件,反而重复要求泛化领域事件;通过校验的自然回答后还会追加“累计条数”“下一领域”“本轮区分重点”等模板。
- 触发条件:同一事件需要两轮以上补齐,历史事件与最新表述存在日期矛盾,或某领域已有证据但当前事件仍缺关键细节。
- 根因:narrative context 只携带本轮抽取结果,没有完整事件账本、原始表述、纠错状态和未决事实;编排器又无条件把确定性进度模板拼到 Agent narrative 后面,因此模型既无法发现跨轮矛盾,也无法决定应先补完当前事件还是切换领域。
- 修复:中间轮 narrative context 新增最新用户原话、最近事件账本、有效/失效纠错状态和未决证据;prompt 明确要求先完成当前事件、核对日期冲突、合并同一事件的原因与结果且不得重复计分,每轮只问一个信息量最高的问题;通过 grounding 校验的 Agent narrative 直接作为可见回答,仅在模型 fallback 时保留确定性进度文案。
- 验证:聚焦测试覆盖完整事件账本进入 prompt、历史日期与最新原话同时可见、连续事件规则进入 output contract、有效 Agent 回答不再追加进度模板,以及 fallback 仍保持安全的一问式文案。测试数据全部为虚构案例,不写入真实用户经历。
- 防复发:不得把“领域是否出现过”当作切换话题的唯一条件;任何新增对话规划信息必须先进入受限 narrative context,并保持 `minute_holdout_not_ready``confirmation_allowed: false``can_narrow_to_minute: false` 等分钟安全门禁不变。
- 相关记录:BUG-028、BUG-029、BUG-032
- 复发自:BUG-028、BUG-032
- 修复版本:待提交(本地可测)
## BUG-034 | 生时校正 Agent 降级只记录 200 导致模板回退不可诊断
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正中间轮自然语言回答、服务端可观测性、网页端响应时延
- 用户现象:用户提交明确事件后等待约 53 秒,网页仍显示“当前累计”“下一步”“本轮区分重点”等固定进度模板;接口日志只有成功的 200,无法看出 Agent 已经连续两次生成或校验失败。
- 触发条件:叙事模型调用抛错、输出不是契约 JSON,或输出未通过 packet grounding 校验并在第二次重试后降级。
- 根因:`generateRectificationNarrative` 会把所有失败收敛为安全 fallback,但此前仅把失败类型写入持久化 validation receipt,没有输出脱敏运行日志;补充日志后真实网页请求确认了两类误杀:其一,校验器机械要求中文必须逐字包含“已经发生/过去、年、月”,导致语义正确的“后来在什么时候”“年份和月份”等自然问法被丢弃;其二,只要一段自然叙事同时提到多个既有年份并出现“还是”,就会被误判为禁止的宽年份选项问卷,即使“还是”实际在询问毕业方式或职业转折类型。
- 修复:fallback 边界保留结构化脱敏日志;日期请求改为识别“发生、后来、开始、毕业、入职、离职”等历史语义和“什么时候、年份、月份”等日期语义,缺少硬边界时只修补内部请求,不再用固定模板覆盖 Agent 正文;宽年份问卷仅拦截明确的年份二选一、A/B 年份选项或年份区间选择,不再因正文包含多个已知事件年份而误杀;当 Agent 的确认段只有说明、没有面向用户的具体问题时,将内部 `evidenceRequest.prompt` 投影到聊天气泡,且避免重复追加制度化提示。
- 验证:聚焦 narrative、orchestrator、route 测试 76/76 通过;新增“多个已知年份 + 非年份还是问句”与“说明需要更多信息但没有直接问题”的回归覆盖;真实网页账号完整保留历史气泡,2017 年入职和 2020 年主动离职均得到针对事件内容的自然确认,后者明确追问“离职后下一份工作或职业转向在何年何月开始”,没有再次落入“当前累计/下一步/本轮区分重点”模板。测试前先释放未确认校正费用并清理校正数据,点数从 207 恢复到 208;新测试正常收取 1 点后为 207。
- 防复发:叙事 fallback 必须同时保留安全用户文案、持久化回执和不含个人数据的运行时诊断;HTTP 200 不得作为自然语言 Agent 正常工作的唯一证据;禁止年份选项问卷的规则必须判断年份之间的选择结构,不能使用全文“出现多个年份 + 任意还是”作为替代。
- 相关记录:BUG-032、BUG-033
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-035 | 生时校正发送错误内容后无法撤回修改
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正文字输入、事件证据提交与对话历史
- 用户现象:用户发现刚发送的经历写错后只能等待 Agent 完成本轮,再通过下一轮更正;输入框在生成期间被锁定,错误内容可能直接进入事件账本。
- 触发条件:在生时校正中提交自由文本回答后立即发现日期或事实写错。
- 根因:校正回答会立即调用持久化命令,界面只有发送和等待状态,没有普通 session 已有的发送撤回窗口;仅中止浏览器请求也不能保证服务端停止保存。
- 修复:复用普通 session 的安全语义,在真正发起校正命令前提供 2.5 秒撤回窗口;发送后立即显示用户气泡和停止按钮,撤回时取消定时任务、移除临时气泡、把原文恢复到输入框并重新聚焦。只有窗口结束后才调用校正接口,因此成功撤回的本轮不会生成、不会写入证据历史,也不计入校正任务。
- 验证:新增真实 Chromium 回归断言,覆盖发送、停止、草稿恢复和延时后未调用 `answer`;本地登录账号手测中,唯一测试文本撤回后仍保留在输入框,等待超过窗口后未进入“正在核对”、未出现在用户历史消息中。目标文件 ESLint 与 `git diff --check` 通过;独立 Node 组件套件当前仍被既有 GSAP Node 导入错误阻断。
- 防复发:撤回必须发生在业务命令发出前;不得把客户端 `fetch.abort()` 误认为服务端事务已取消。
- 相关记录:BUG-018、BUG-032
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-036 | Agent 化生时校正上线前被旧模板组件断言阻断
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生产发布门禁、生时校正前端组件测试
- 用户现象:最新 `main` 已保留 Agent 自然语言回复与历史气泡,但生产测试门禁失败,无法进入部署。
- 触发条件:运行生产 `Jyotish Skill Tests` 的完整前端测试套件。
- 根因:组件测试仍断言旧版确定性模板会覆盖 Agent 叙事、经历摘要带固定“已记录”前缀,并直接调用无撤回窗口的提交表达式;真实 Chromium 等待条件也仍匹配旧文案。
- 修复:更新 5 条过期断言,使其验证 Agent 原始叙事、折叠进度中的经历摘要、2.5 秒撤回后提交路径和当前移动端文案;不回退现有业务实现。
- 验证:生时校正组件测试、完整前端测试、生产质量门禁与生产部署工作流;以对应提交 SHA 的线上健康检查为最终验收。
- 防复发:对话呈现测试应验证用户可见契约,不再把旧模板句式或内部调用参数写成不必要的固定实现约束。
- 相关记录:BUG-034、BUG-035
- 复发自:无
- 修复版本:待提交
## BUG-037 | 生时校正刷新后把真实对话重建成“已记录”模板
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正历史持久化、刷新/重新进入恢复、Agent 一问一答呈现
- 用户现象:同一轮实时回答时 Agent 会针对经历自然追问,但刷新页面或重新进入生时校正后,旧回复全部变成“已记录这段经历:……”;用户原话也被日期和事件摘要替代,看起来像 Agent 又退回固定模板。
- 触发条件:已有两轮以上生时校正回答后刷新网页、从首页重新进入,或在当前页面同步一个更新的持久化案例。
- 根因:数据库已保存每轮 Agent `narrative`,但恢复 RPC 只返回 `latest_turn`;前端为了补齐历史,使用 `evidenceRecap` 机械合成用户气泡和“已记录”助手气泡。实时链路使用内存中的原话与真实 narrative,恢复链路却使用另一套有损数据源,导致刷新前后表现不一致。
- 修复:为校正 turn 向前新增受约束的 nullable `user_message`,answer 保存事务同时持久化用户原话;新增仅限 `service_role` 的 history load/save/completion wrapper RPC,按轮次返回最近 200 轮 `userMessage + narrative`;客户端响应契约支持恢复历史,首次加载和同案例重新同步均直接渲染原始一问一答。旧记录只在有明确原始 evidence 时回填用户文本;无法恢复的旧轮次只显示真实最新 Agent narrative,不再伪造模板。
- 验证:控制器回归覆盖首次恢复、旧数据 fallback、同案例重新同步和“不得出现已记录这段经历”;聚焦校正测试 90/90 通过;本地 PostgreSQL 从头应用全部迁移成功,并验证新列与 history RPC 的 `service_role`/`authenticated` 权限边界。测试内容均为虚构经历,不写入真实用户资料。
- 防复发:任何对话历史必须从持久化的原始 user/assistant turn 恢复;事件摘要只能用于进度和评分,不得反向伪造聊天内容。网页验收必须同时检查实时回答和刷新后的同一历史。
- 相关记录:BUG-032、BUG-034、BUG-036
- 复发自:BUG-032
- 修复版本:待提交
## BUG-038 | 老案例摘要被误标成原始对话且第七条事件后无法继续
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生产生时校正老案例恢复、累计 7–8 条可评分事件后的继续问答
- 用户现象:部署历史恢复修复后,老案例刷新仍显示“已记录这段经历”;继续回答一条明确事件后,页面提示暂时无法继续并把文本退回草稿。
- 触发条件:案例来自原始消息持久化上线之前,且旧事件 evidence 能提供规范化 `raw_text`;或累计可评分事件超过 6 条。
- 根因:首版迁移把旧 evidence 的规范化摘要回填到 `user_message`,却没有标记它不是逐字捕获的聊天原文,恢复 RPC 因而把合成摘要当成真实历史;产品收敛上限允许 8 条事件,但 Python 事件评分 API 仍只接受最多 6 条,前端第 7 条后稳定收到 400。
- 修复:为 turn 增加 `user_message_captured` 来源标记,只有 answer 事务当场保存的逐字用户文本才进入可见历史;旧案例没有可靠原文时只显示真实最新 Agent narrative,不再展示伪造气泡。事件评分 API 上限与产品常量统一为 8,并增加八事件回归。
- 验证:迁移从零应用并检查来源标记与 RPC 权限;八事件 API 测试、聚焦对话测试、生产构建与真实生产浏览器继续问答/刷新/重新进入 smoke。
- 防复发:消息历史必须携带来源可信度,事件 evidence 不得默认等价于聊天原文;跨服务的事件数量上限必须由同一契约测试锁定。
- 相关记录:BUG-034、BUG-037
- 复发自:BUG-037
- 修复版本:0850619eaf5002736542463394720b0ab1949ce9
## BUG-039 | 生时校正 Agent 回答等待结束后一次性出现
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正首次进入、回答传输、Agent 生成状态、移动端对话可读性
- 用户现象:首次进入时只显示“正在建立校正记录”,看不到后台生成的第一条 Agent 引导;用户发送经历后也只能看到“正在核对星盘信息”,待模型、校验和保存全部完成后,Agent 整段回答一次性出现,与普通 session 的逐段生成体验不一致。
- 触发条件:任意生时校正 `start``resume``answer` 命令成功返回自然语言 narrative。
- 根因:`/api/birth-time-conversation` 固定使用 `Response.json(turn)`,客户端也完整读取并校验 JSON 后才更新控制器;即使 narrative 已生成,传输层和聊天渲染层都没有增量事件契约。首次入口另由首页直接执行 `start/resume`,没有把生成中的首条 narrative 投影给尚未初始化的校正组件。
- 修复:成功响应在客户端声明支持时改用 `application/x-ndjson`;服务端只对已经完成技法校验并持久化的 narrative 分块发送 `delta`,最后发送完整 durable turn。客户端逐行解析并即时投影到同一个 assistant 气泡,首次 `start/resume` 也把增量引导传入空白校正界面;最终 turn 到达后无缝转为 settled 历史。错误响应继续使用既有安全 JSON,已输出 delta 的中断不自动重放,避免重复文字。代理技法校验、事件事务和分钟安全门禁均保持在流输出之前。
- 验证:新增 route 分块顺序与禁用代理缓冲回归、client NDJSON 增量解析回归、controller 在 durable turn 未到达前发布流式文本回归、组件 streaming 气泡回归;聚焦校正测试、真实 Chromium 390px 组件测试、ESLint、TypeScript 与 production build 通过。
- 防复发:生时校正成功响应不得退回一次性 JSON 作为唯一前端路径;可见增量内容必须来自已校验 narrative,不得直接透传未完成或未通过约束的模型 token。
- 相关记录:BUG-034、BUG-037、BUG-038
- 复发自:无
- 修复版本:本次流式修复提交
## BUG-040 | Agent 活动状态与回答被错误渲染为互斥状态
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:普通 session 与生时校正共用的 Agent 消息行、流式回答反馈、完成态识别
- 用户现象:Agent 尚未产出正文时只显示 orb 活动状态;开始出现正文后状态与答案的关系不连续,完成后活动状态直接消失,用户无法从同一消息位置判断回答仍在生成还是已经结束。
- 触发条件:任意 assistant 消息在 `thinking``streaming``settled` 三种视图状态之间切换。
- 根因:共享 `ChatMessageRow` 使用条件分支把 `thinking` 渲染为仅有 `AgentActivityStatus`,又只在 `streaming` 时显示 composing 状态;`settled` 分支只保留正文,因此活动状态和正文被实现成了互斥内容,而不是同一消息的连续生命周期。
- 修复:assistant 消息始终在同一气泡内先渲染活动状态,再渲染当前已有正文;thinking/streaming 继续使用 working/composing orbsettled 只把状态文案切换为“回答已完成”,并把 orb 替换为 Lucide 完成图标。普通 session 与生时校正共享该行为,React 消息 identity 与正文内容保持不变。
- 验证:消息视图回归覆盖 working、composing、completed 三态、正文与状态同时存在、完成态 Lucide 图标;相关普通 session 与生时校正契约测试通过,并执行真实浏览器检查。
- 防复发:Agent 活动状态只能描述消息生命周期,不得控制正文是否渲染;完成态必须保留稳定的视觉反馈,不能以卸载整个状态区域代替状态转换。
- 相关记录:BUG-039
- 复发自:无
- 修复版本:`4a9b1dc`
## BUG-041 | 生时校正生成回答时消息列表不自动跟随到底部
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正消息历史、用户发送后的撤回窗口、Agent thinking / streaming / settled 回答阶段
- 用户现象:用户发送经历或 Agent 开始生成回答后,新内容出现在消息列表下方,但列表停留在旧位置;用户必须手动向下滚动才能看到生成状态和最新回答。
- 触发条件:生时校正已有足够内容使独立消息容器产生纵向滚动,然后发送新经历或接收流式 Agent 回答。
- 根因:普通 session 有独立的底部跟随 effect,生时校正虽然使用可滚动的 `.rectification-message-list`,但组件没有保存该容器的 ref,也没有在消息、提交状态、流式文本或最终 turn 更新时调整容器滚动位置。
- 修复:为生时校正消息容器增加专用 ref,并在用户消息、生成状态、流式增量、错误及最终 turn 生命周期变化后滚动该容器到底部;生成过程中直接跟随,完成后平滑定位,且尊重 reduced-motion,不调用会牵动外层页面的 `scrollIntoView`
- 验证:真实 Chromium 390px 回归制造独立消息容器 overflow,并分别断言初始历史、乐观用户消息、流式 Agent 增量和完成回答都保持在底部;相关聚焦测试、ESLint、TypeScript 与生产 smoke。
- 防复发:生时校正的消息生命周期新增阶段必须进入底部跟随依赖;真实浏览器测试必须验证容器自身的 `scrollTop`,不能只检查正文是否出现在 DOM。
- 相关记录:BUG-039、BUG-040
- 复发自:无
- 修复版本:本次自动滚动修复提交
## BUG-042 | 生时校正沿用系统滚动条导致视觉割裂
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正独立消息列表的桌面端滚动反馈
- 用户现象:生时校正右侧直接显示浏览器或操作系统默认滚动条,与站内温和、低对比的编辑式界面不一致。
- 触发条件:生时校正历史内容超过消息区高度,在桌面浏览器出现纵向滚动条。
- 根因:`.rectification-message-list` 只声明了 `overflow-y: auto`,没有提供跨浏览器的产品内滚动条颜色、宽度和交互状态。
- 修复:为生时校正消息区增加细宽、透明轨道、低对比圆角拇指;默认隐藏拇指,仅在指针移动、列表滚动或键盘焦点进入消息区时短暂显示,停止操作后自动隐藏。Firefox 使用标准 scrollbar 属性,Chromium / Safari 使用 WebKit 伪元素,颜色继续取现有设计 token。
- 验证:CSS 契约锁定默认透明、交互显现及标准与 WebKit 两套样式,聚焦组件测试、目标 ESLint、production build 与生产浏览器 smoke。
- 防复发:独立滚动容器必须复用产品 token,并同时覆盖标准 scrollbar 属性和 WebKit 伪元素;默认态不得持续抢占视觉注意力,也不得引入渐变、重阴影或高饱和装饰。
- 相关记录:BUG-041
- 复发自:无
- 修复版本:本次简约滚动条修复提交
## BUG-043 | 生时校正把后台证据状态重复渲染成可展开管理面板
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正消息流、候选进度、历史经历与更正入口
- 用户现象:Agent 已经在自然对话中确认和追问经历,消息区底部仍额外显示“当前候选 · 待验证”、候选范围、历史经历列表和“更正”按钮;用户需要理解并操作第二套记录界面,破坏一问一答的连续性。
- 触发条件:任何已有候选或至少一条 evidence recap 的生时校正案例。
- 根因:语言交互改版后仍保留旧产品流程的 `<details>` 进度与证据管理面板,把本应仅供后台评分和恢复使用的结构化状态再次暴露给用户。
- 修复:从生时校正消息流移除整块候选进度与历史经历管理面板,不再显示候选状态、范围、经历列表或逐条更正按钮;后台 evidence、评分与持久化保持不变,最终可确认状态仍通过明确确认动作呈现。
- 验证:组件与真实 Chromium 回归锁定页面不包含候选进度、历史经历面板或更正按钮,同时保留自然对话、流式回答、撤回窗口、自动贴底和最终确认能力。
- 防复发:结构化 evidence 是 Agent 的后台推理与持久化输入,不得在语言优先界面重复渲染成需要用户管理的卡片或表单;需要纠正时继续通过自然语言表达。
- 相关记录:BUG-020、BUG-034、BUG-042
- 复发自:BUG-020
- 修复版本:待提交