Files
Jyotisha/docs/BUG_HISTORY.md
T
Jesse_Chen 8169bd85f7
Independent Staging Quality Gate / validate (push) Failing after 13m12s
Independent Staging Quality Gate / publish (push) Has been skipped
feat(consult): classify consultation workflow failures for diagnosis
Every workflow fault except abort and timeout collapsed into the single
calculation_failed code, and the upstream message was discarded, so a failing
run left no evidence of whether the API rejected the call or returned a payload
that missed the response contract.

Classify failures into a closed vocabulary carried on ConsultationWorkflowError
and record it as the failureCode of the runtime step. The observability tool
call schema gains one controlled optional field; upstream error text stays out
of logs, as that contract requires. Forward request_id to the API so a run can
be aligned with its access log.

Build public receipts from an explicit allowlist. The internal failure code
must not reach the client contract, whose step schema is strict and would
otherwise reject a successful run.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 12:44:19 +08:00

482 KiB
Raw Blame History

Bug History

本文件是本仓库产品与代码 Bug 的长期知识库。处理任何 Bug 前先读并搜索本文件;完成修复或确认阻塞后,在同一变更中更新本文件。

基础设施、外部引擎、碎片目录和开工预检类风险仍记录在 docs/research/pre_work_error_ledger.md。同一问题若同时影响两类台账,应互相引用,不复制大段内容。

使用流程

  1. 用报错原文、接口路径、状态码、模块名和用户操作搜索本文件。
  2. 找到相似记录时,先验证既有防复发措施是否仍存在,再定位新的回归入口。
  3. 修复必须包含与风险相称的自动化测试;生产问题还要记录部署版本和脱敏后的生产验证。
  4. 完成后更新已有记录,或按下方模板添加新记录。复发问题必须填写 复发自,不能伪装成无关的新问题。
  5. 不记录姓名、出生资料、邮箱、用户/案例 ID、Cookie、JWT、密码、密钥、完整请求体或模型原文。

状态定义

  • investigating:现象已确认,根因未确认。
  • blocked:缺少权限、环境或外部条件,当前无法继续验证。
  • mitigated:用户影响已被安全降级,但根因或全链路尚未闭环。
  • resolved:根因已修复,并有自动化或真实环境证据。
  • regressed:历史问题再次出现;必须关联原记录并说明旧防线为何失效。

新记录模板

## 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.pyfrontend/tests/chat-session-delete-contract.test.ts
  • 防复发:删除必须经服务端所有权校验;测试同时锁定 API、迁移和前端入口。
  • 相关记录:无
  • 复发自:无
  • 修复版本:e3d619c65b8c425650ed1fcf5794

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
  • 相关记录:无
  • 复发自:无
  • 修复版本:28d58d45ee925c308e5d5e13d595346ec02a9ffbd9

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
  • 复发自:无
  • 修复版本:dc0077e20260721140000_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
  • 复发自:无
  • 修复版本:b981c4e20260721150000_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.tsfrontend/tests/consultation-entrypoint.test.tsfrontend/tests/consultation-birth-time-mode.test.tsfrontend/tests/birth-time-intake.test.ts
  • 防复发:咨询路由测试锁定“有效填报分钟无需授权即可使用”;页面契约禁止重新引入生时校正 toast 或阻断式选择。
  • 相关记录:BUG-003、BUG-004
  • 复发自:无
  • 修复版本:本次 staging 修复提交(精确 SHA 以远端分支与 staging 验收结果为准)

BUG-010 | 浏览器直连 Supabase 导致自托管 PostgreSQL staging 误报未配置

  • 状态:resolved
  • 首次发现:2026-07-22
  • 最近更新:2026-07-27
  • 影响面:聊天首页启动、会话模型选择、退出登录;自托管 PostgreSQL staging。
  • 用户现象:数据库和身份服务正常,首页却显示“Supabase 尚未配置”。
  • 触发条件:AUTH_PROVIDER=self-hosted 且不提供浏览器 Supabase 公钥。
  • 根因:页面启动、会话模型更新和退出登录仍直接创建浏览器 Supabase client;仅服务端 API 已切换到同源 PostgreSQL 路径。
  • 修复:/api/account 返回当前用户完整 profile;首页通过 /api/account/api/sessions/api/sessions/:id 读写,退出使用 self-hosted identity client。
  • 验证:frontend/tests/chat-session-write.test.tsfrontend/tests/session-model-persistence.test.tsfrontend/tests/account-api.test.ts、ESLint、next build
  • 防复发:首页不得引用 createBrowserSupabaseClient;会话与 profile 只能经同源 API 访问。
  • 相关记录:BUG-001、BUG-003
  • 复发自:BUG-010
  • 修复版本:待发布 staging

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.tsfrontend/tests/evidence-audit-panel.test.tsfrontend/tests/consultation-report-export.test.ts 锁定聊天消息不再挂载这些内部组件。
  • 防复发:聊天消息渲染契约禁止直接展示 techniqueTruthworkflowReceipt;需要运营或调试时使用独立的受控界面。
  • 相关记录: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_validatorthin 队列移入 excellent 断言,保持测试与同一审计规则一致。
  • 验证:tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sourcesPython 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 的日志分别记录了 RectificationTechnicalPacketRangeErrorcandidate_differences TimeoutError
  • 修复:稀疏扫描差异改用明确的范围级文案,禁止伪称相邻分钟切换;超过六小时的宽范围首轮跳过分钟级候选分区,较窄范围在候选分区发生已知超时、取消、配置或服务故障时保留服务端扫描证据并继续收集,不再返回 503;未知编程或契约错误仍失败关闭。
  • 验证:frontend/tests/conversational-technical-packet.test.tsfrontend/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_chartunverified_birth_time 两种星盘模式,继续拒绝 general_no_birth_time、旧校正入口和不一致 request ID;分钟确认门禁保持不变。
  • 验证:生产合成账号复现 409 mode_changedfrontend/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 处于 claimedexecuting,两分钟租约已经过期,客户端重新加载交接。
  • 根因:数据库 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 | 生时校正把语言问答包装成高阻力卡片表单

BUG-018 | 生时校正把语言问答包装成高阻力卡片表单

  • 状态: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 | 自然语言真实事件在分钟评分入口被静默丢弃

BUG-019 | 自然语言真实事件在分钟评分入口被静默丢弃

  • 状态: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 | 生时校正入口等待首轮请求完成后才切换会话

  • 相关记录:BUG-008、BUG-009、BUG-015、BUG-016、BUG-018
  • 复发自:BUG-018
  • 修复版本:待提交(本地可测)

BUG-020 | 生时校正入口等待首轮请求完成后才切换会话

  • 状态:resolved
  • 首次发现:2026-07-22
  • 最近更新:2026-07-22
  • 影响面:首页生时校正入口、普通咨询中的“先完成生时校正”、侧栏恢复校正会话
  • 用户现象:点击生时校正后长时间停留在首页或原咨询 session,直到恢复、建案、计算和会话持久化全部完成才突然跳转,用户容易误以为点击无效并重复操作。
  • 触发条件:startresume、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 | 初始化地址保存后的加载提示仍指向生时评估

  • 相关记录:BUG-016、BUG-018、BUG-019
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-021 | 初始化地址保存后的加载提示仍指向生时评估

  • 状态: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 | 侧栏会话标题与更多操作被拆成两块

  • 相关记录:BUG-020
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-022 | 侧栏会话标题与更多操作被拆成两块

  • 状态:resolved
  • 首次发现:2026-07-22
  • 最近更新:2026-07-22
  • 影响面:聊天记录侧栏、当前会话选中态与更多操作入口
  • 用户现象:会话标题显示在一块选中背景中,右侧更多操作却以独立圆形按钮悬在外侧,看起来像两个不一致的控件。
  • 触发条件:侧栏展开并选中任意会话。
  • 根因:选中背景只应用在左侧 .session-main,右侧 .session-menu-trigger 又单独使用 50% 圆角和悬停背景。
  • 修复:把选中、悬停和聚焦背景统一应用到整行 .session-row;子按钮保持透明,并移除更多操作按钮的独立圆形底色。
  • 验证:侧栏契约测试锁定整行选中背景、透明标题按钮和非圆形更多操作按钮;目标文件 ESLint 与 git diff --check 通过。
  • 防复发:会话标题和行内操作必须共享同一个行级状态面,不得分别绘制互相竞争的选中背景。
  • 相关记录:无
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-025 | 健康压力追问被数据库误判为校正操作冲突

BUG-023 | 健康压力追问被数据库误判为校正操作冲突

  • 状态: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: answerresultCategory: 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 | 生时校正收到具体经历后仍重复泛问

  • 相关记录:BUG-007、BUG-018、BUG-019
  • 复发自:BUG-007
  • 修复版本:待提交(测试 Supabase smoke 通过)

BUG-024 | 生时校正收到具体经历后仍重复泛问

  • 状态: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 | 生时校正仍有未回答区分领域时提前结束

BUG-025 | 生时校正仍有未回答区分领域时提前结束

  • 状态:resolved
  • 首次发现:2026-07-22
  • 最近更新:2026-07-22
  • 影响面:生时校正多领域追问、候选范围停滞终止策略
  • 用户现象:用户只回答两轮后,系统仍有财务或关系等可区分领域未询问,却直接保存宽候选范围并结束。
  • 触发条件:自然语言单轮抽取出多条可评分经历,使累计数量达到最低门槛;候选范围连续两轮未变化,同时技术包仍建议尚未回答的领域。
  • 根因:停滞终止只检查可评分经历数量和候选范围是否连续不变,没有检查当前技术包是否仍存在未回答的区分领域。
  • 修复:停滞结束前增加未回答建议领域门禁;仍有可区分领域时继续自然追问,全部建议领域已覆盖后才允许按范围停滞安全结束。证据数量上限和无可区分问题终止保持不变。
  • 验证:编排器回归锁定学业、搬迁、事业三轮后必须继续询问尚未覆盖的关系领域,补充关系事件后才保存未确认范围;本地真实流程修复前复现为两轮即结束且范围仍为 04:3006:30
  • 防复发:停滞终止测试必须同时断言候选范围未变化和技术包建议领域已全部回答,不能只按事件条数结束。
  • 相关记录:BUG-019、BUG-028
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-030 | 职位和管理职责变化未识别为事业证据

  • 相关记录:BUG-019、BUG-024
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-026 | 职位和管理职责变化未识别为事业证据

  • 状态:resolved
  • 首次发现:2026-07-22
  • 最近更新:2026-07-22
  • 影响面:生时校正自然语言证据分类、事业领域覆盖判断、下一轮追问
  • 用户现象:用户已经说明“开始承担管理职责”或“职位发生明显变化”,系统记录事件后仍继续要求事业经历。
  • 触发条件:事业变化使用“职位”“任职”或“管理职责”描述,但没有出现“工作”“升职”“职业”等原有关键词。
  • 根因:事业领域分类词表缺少常见的岗位和职责变化表达,导致可评分事件被归入 other,无法计入事业领域覆盖。
  • 修复:将“职位”“任职”“管理职责”加入事业领域分类规则,保留既有日期、评分和持久化契约。
  • 验证:抽取器回归覆盖三种带年月的岗位与职责变化表达,均分类为 career、保留月份并可参与评分;本地真实流程已复现修复前的漏判行为。
  • 防复发:事业领域分类测试必须覆盖入离职、晋升以及不含“工作/职业”字样的职责变化表达。
  • 相关记录:BUG-028、BUG-029
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-031 | 生时纠正未收敛仍永久扣费且事件事实未进入评分契约

  • 相关记录:BUG-024、BUG-025
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-027 | 生时纠正未收敛仍永久扣费且事件事实未进入评分契约

  • 状态: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_applyconfirmation_allowedcan_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 | 生时校正覆盖历史回答且完成后无法返回原问题

  • 相关记录:BUG-019、BUG-024、BUG-025
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-028 | 生时校正覆盖历史回答且完成后无法返回原问题

  • 状态: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 | 生时校正缺少连续事件语义导致模板式跳问

  • 相关记录:BUG-018、BUG-019、BUG-024
  • 复发自:BUG-018、BUG-024
  • 修复版本:待提交(本地可测)

BUG-029 | 生时校正缺少连续事件语义导致模板式跳问

  • 状态: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_readyconfirmation_allowed: falsecan_narrow_to_minute: false 等分钟安全门禁不变。
  • 相关记录:BUG-028、BUG-029、BUG-032
  • 复发自:BUG-028、BUG-032
  • 修复版本:待提交(本地可测)

BUG-034 | 生时校正 Agent 降级只记录 200 导致模板回退不可诊断

  • 相关记录:BUG-024、BUG-025、BUG-028
  • 复发自:BUG-024、BUG-028
  • 修复版本:待提交(本地可测)

BUG-030 | 生时校正 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 | 生时校正发送错误内容后无法撤回修改

  • 相关记录:BUG-028、BUG-029
  • 修复版本:待提交(本地可测)

BUG-031 | 生时校正发送错误内容后无法撤回修改

  • 状态: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 化生时校正上线前被旧模板组件断言阻断

  • 相关记录:BUG-018、BUG-028
  • 修复版本:待提交(本地可测)

BUG-032 | 连续事件补充回答丢失状态并重新进入日期模板

  • 状态: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 | 生时校正刷新后把真实对话重建成“已记录”模板

  • 影响面:生时校正多轮问答、事件证据账本、Agent 自然追问
  • 用户现象:用户先提供“2017年5月参加工作”,Agent 继续确认“正式工作还是实习/兼职”;用户回答“正式工作”后,系统却把这句话当成新事件,重新追问“具体是什么年月/只记得年份也可以”,既重复问题又丢失上一轮的年月。
  • 触发条件:Agent 的追问属于已有事件的日期补充或细节补充,但公开 turn 只保存问题文本,没有保存追问目标事件和问题类型。
  • 根因:编排器仅根据当前消息是否含日期决定分支;上一轮没有持久化 event_dateevent_detailnew_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
  • 最近更新: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 | 老案例摘要被误标成原始对话且第七条事件后无法继续

  • 影响面:首次创建生时校正 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
  • 最近更新: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 回答等待结束后一次性出现

  • 影响面:生时校正首页入口、未完成 case 恢复、重复 start 防护
  • 用户现象:用户点击进入生时校正时,接口发送新的 start,返回 409 action_conflict,页面无法进入校正会话。
  • 触发条件:前端账户快照没有包含已有未完成 case,或账户快照在出生声明变更后隐藏了旧 case,但数据库中仍存在该用户的未完成 V3 case。
  • 根因:前端只根据内存账户状态选择 start/resume;数据库会阻止同一用户创建第二个未完成 V3 case,旧快照因此被拒绝。
  • 修复:首次 start 收到 409 后刷新账户状态;如果刷新得到可恢复 case,自动使用最新 caseIdturnVersion 执行 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
  • 最近更新:2026-07-23
  • 影响面:生时校正首次进入、回答传输、Agent 生成状态、移动端对话可读性
  • 用户现象:首次进入时只显示“正在建立校正记录”,看不到后台生成的第一条 Agent 引导;用户发送经历后也只能看到“正在核对星盘信息”,待模型、校验和保存全部完成后,Agent 整段回答一次性出现,与普通 session 的逐段生成体验不一致。
  • 触发条件:任意生时校正 startresumeanswer 命令成功返回自然语言 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 活动状态与回答被错误渲染为互斥状态

  • 影响面:生时校正 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
  • 最近更新:2026-07-23
  • 影响面:普通 session 与生时校正共用的 Agent 消息行、流式回答反馈、完成态识别
  • 用户现象:Agent 尚未产出正文时只显示 orb 活动状态;开始出现正文后状态与答案的关系不连续,完成后活动状态直接消失,用户无法从同一消息位置判断回答仍在生成还是已经结束。
  • 触发条件:任意 assistant 消息在 thinkingstreamingsettled 三种视图状态之间切换。
  • 根因:共享 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 | 生时校正生成回答时消息列表不自动跟随到底部

  • 影响面:生时校正首条及后续普通新事件回答
  • 用户现象:case 与请求版本同为 0,提交首条经历仍返回 409 action_conflict
  • 根因:应用已为证据请求加入可选 followUp 状态,但当前数据库校验器仍只接受旧字段;显式的 new_event 与旧默认语义相同,却在保存边界被拒绝。
  • 修复:保存任何 turn 时都移除语义冗余的 followUp: new_event,兼容旧校验器;event_dateevent_detail 仍等待对应数据库迁移后持久化。
  • 验证:直接 RPC 探针确认旧格式为 true、带 followUpfalse;新增 store 回归测试覆盖首轮与后续 turn 的兼容投影。
  • 防复发:新增可选持久化字段时,默认语义必须能向旧数据库降级;数据库迁移未应用前不得把兼容字段直接写入 durable RPC。
  • 相关记录:BUG-032、BUG-034、BUG-035
  • 修复版本:待提交(本地可测)

BUG-037 | 非评分回答绕过 Agent 并显示固定澄清模板

  • 状态: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
  • 修复版本:待提交
  • 影响面:生时校正缺少年月、未来事件、换方向和非评分更正后的自然问答
  • 用户现象:用户用自然语言补充“化学专业”“后来换了工作”等内容后,页面直接显示“你提到……具体内容我已经记下了。它大致是什么年月?”,与前后 Agent 语气断裂,也可能忽略刚才已经建立的事件上下文。
  • 根因:编排器发现本轮暂时没有可评分事件后,直接进入同步 nonScoringTurn();该函数完全没有调用 narrative Agent,而是用固定字符串生成可见回复。Mastra 和模型没有获得生成这轮回答的机会。
  • 修复:非评分分支先用当前候选技术包、完整事件账本、最新用户原话和未决证据调用现有 narrative Agent;状态机只在后台固定本轮追问属于 event_dateevent_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 继续正常读取。迁移应用后自动使用完整查询。
  • 验证:服务角色直接查询确认修复前错误为 42703frontend/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 / UPDATEPostgREST 返回 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/accountPOST /api/consultPOST /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 --noEmitgit 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 持久化声明已经支持 placeIdplaceTypeprovidertimezoneIdtimezoneSource,但数据库函数 conversational_rectification_valid_declared_birth_input 仍只允许旧中国地点字段。合法全球地点声明在 create_conversational_rectification_case 内被判无效,并被 RPC 统一映射为 conversational_action_conflict
  • 修复:新增向前迁移同步数据库地点声明白名单,接受全球地点身份、IANA 时区来源及坐标字段;地点身份允许 citycityCodeplaceId 任一存在,同时保留旧中国地点兼容和数值边界校验。
  • 验证:数据库迁移单独执行成功;使用新的 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_conflictcase 的 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=answerresultCategory=successbillingState=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_idraw_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_dateevent_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=trueschemaValidated=falseissues=["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 秒成功结果还曾因旧引用正则误判被丢弃。
  • 修复:生产模型只生成 narrativeevidenceRequest,候选状态、时间、范围、分盘层、引用和领域依据由服务端从 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-conversationregenerate 与其他非最终叙事轮、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_evaluatedblocked 技法和确认权限,却没有继续提出下一个问题;回答像调试报告而不是连续的一问一答。
  • 根因:叙事上下文直接暴露事件的私有 domain、建议领域和完整 expert workflow 状态,模型因此围绕工程元数据组织回答;同时问题修复器只处理首轮或明显截断的句子,语法完整但没有问号的中间回答会原样通过。技法保持 not_evaluated 的直接原因则是当前有效、受支持且可评分的事件不足三条,路由尚未进入 scoreEvents(),并非 Mastra 无法调用技法。
  • 修复:新增叙事专用上下文,移除事件 domain、建议领域和内部评分;证据采集阶段只允许暴露已实际 usedpartial 的技法,不再展示未调用清单、阻塞项或确认权限。私有语义领域仍保留在服务端用于事件去重、跨领域路由和候选评分,但不得进入用户可见文案。所有非最终回答必须包含且只引导一个下一问题;模型正文漏问时追加同一次模型生成的 evidenceRequest.prompt,不使用固定业务模板。
  • 验证:新增回归覆盖事件正文可见但内部领域不可见、建议领域不进入叙事 packet、未调用技法不在采集轮展示,以及完整陈述漏问时补入模型自写问题;叙事、路由和编排聚焦测试通过,目标 ESLint、TypeScript --noEmitgit 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 --noEmitgit 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 文本的 startanswerregenerate 命令加入可选 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 原生 careerwealthmarriage 领域各选择一条最强事件;直接映射优先于代理映射,日级优先于月级和年级,同精度选择较新事件。月级和年级事件只取范围中点作为代表日,两个候选最多发起六次外部事件扫描;响应明确记录符合条件事件数、实际选中数和选择策略,不再暗示全部事件均已外部验证。通用 range scan 保持不变。
  • 验证:长对话回归断言十二条符合映射的事件只产生六次扫描,两个候选各覆盖事业、财富、关系三领域,所有扫描均为单日;相关 Python 测试 42/42 通过。使用真实 VedAstro key 重跑同一十二事件 fixture,首次完整命令约 9.7 秒、缓存后约 3.6 秒,官方响应状态为 official_verified。真实 SearchEvents05: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=falsevedastro_minute_sensitive_layers_not_discriminated。相关聚焦测试通过。
  • 防复发:任何最终分钟确认都必须区分“本地事件排序”“VedAstro 官方分钟技术身份验证”和“SearchEvents 背景验证”三种职责;两个官方快照有差异只证明候选技术状态不同,不得描述为 VedAstro 已独立证明本地首选分钟正确。
  • 相关记录:BUG-055、BUG-064、BUG-065
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-067 | 主分支同步后生时校正首轮流式、历史与重跑持久化回归

  • 状态:resolved
  • 首次发现:2026-07-24
  • 最近更新:2026-07-24
  • 影响面:生时校正首轮引导、NDJSON 流式回答、刷新后的交替历史、重跑持久化、Next.js 生产构建与迁移顺序
  • 用户现象:进入生时校正后首条 Agent 引导不显示或不流式;成功回答在页面上消失,刷新后 Agent 与用户消息错位;重跑后同一条用户消息被重复保存;同步主分支后开发测试可过但生产构建因 App Router 非法导出或缺失状态 setter 失败。
  • 根因:合并时删除了首轮 opening stream 与控制器 streamingAssistantText 的部分状态链,并把 NDJSON 客户端退回整包解析;持久化读取只恢复最终 turn,没有按 authored narrative 还原完整交替历史。重跑仍把上一条用户文本作为新 turn 保存,导致刷新后重复。两个迁移使用相同版本号;多个 route.ts 还导出了测试 helper,违反 Next.js App Router 生产构建的导出合同。最后,页面保留了 setRectificationOpeningAssistantText 调用但状态声明被删,只有完整生产 type-check 才暴露。
  • 修复:恢复服务端 NDJSON 增量事件和客户端逐块解析;恢复首轮 opening stream、控制器流式状态及持久化交替历史。重跑改为 assistant-only turn,数据库向前迁移允许 user_message = null,历史折叠时用新 Agent 回答替换上一轮回答而不复制用户气泡。迁移版本改为唯一顺序;将出生资料和全局地点 helper 移出 App Router route 文件,复杂生时校正实现移入 handler.ts,路由仅导出 HTTP handler 与受支持配置;补回首轮流式状态声明。
  • 验证:Next.js webpack 生产构建通过;除已知会使本机 Chrome 崩溃的真实 Chromium 组件测试外,前端测试 968/968 通过;VedAstro 适配器测试 24/24 通过;ESLint 0 error(保留 3 条既有 warning);git diff --check 通过。新增回归覆盖首轮流式增量、刷新后交替历史、assistant-only regenerate、迁移合同、全局出生地点 helper 与路由导出边界。
  • 防复发:发布门禁必须包含真实 next build --webpack,不能只跑单元测试或 tscApp Router route.ts 不得导出测试 helper;重跑必须以“替换上一轮 Agent 回答”为持久化语义;首轮消息、后续消息和刷新恢复必须共享同一对话历史合同。
  • 相关记录:BUG-052、BUG-053、BUG-055、BUG-058、BUG-060
  • 复发自:无
  • 修复版本:待提交(待生产验收)

BUG-068 | 生时校正确认词被当成新事件且重复追问同一日期

  • 状态:resolved
  • 首次发现:2026-07-25
  • 最近更新:2026-07-25
  • 影响面:生时校正连续问答、事件日期确认、刷新后继续会话
  • 用户现象:Agent 问某个事件是否发生在明确年月,用户回答“是的”后,下一轮仍重复询问相同年月;确认词还可能被保存成一条日期待补充的新事件。
  • 触发条件:用户使用确认词、否定词、代词或承接上一问的简短自然语言回答,而当前轮没有再次写出完整事件和绝对日期。
  • 根因:叙事模型原先每轮只收到最新用户文字和事件账本,缺少同一 case 的连续 Assistant/User 历史;即使补回历史,候选日期仍只存在于上一轮可见文案中,没有作为业务状态持久化,确定性提取器仍可能把确认词当成新事件。依赖从 Agent 文案正则反推日期也会随文案改写、语言变化或恢复路径失效。
  • 修复:复用现有 turn 与 event evidence 存储,每轮向 Agent 注入同一 case 最近 40 条连续问答;同时在共享 follow-up 合同和数据库 JSON 校验中持久化 promptanswerModeproposedDate。Orchestrator 在事件提取前确定性处理 yes/no:肯定时把候选日期作为 append-only correction 合并到目标 evidence,否定时不生成事件并把同一目标切换为开放式日期追问。模型成功返回时,其 follow-up 不再被提取器的自动澄清覆盖;Narrative Agent 拒绝缺少结构化候选、重复已解决问题或继续指向已完成 evidence 的输出。
  • 验证:回归覆盖“2020年10月吗?→是的”、单独“不是”、“不是,是2021年10月”、刷新/resume 后候选日期仍存在,以及同一 actionId 重放不重复写入;断言确认词不成为独立事件、修正 lineage 正确、否定后切换为 free_text,并断言叙事 prompt 末尾连续包含上一条 Assistant 问题和当前 User 回答。
  • 防复发:任何承接式回答必须以持久化会话历史为第一语境;确认或否认是否改变业务事实必须由持久化 follow-up 状态和确定性状态机执行,不得再从自然语言文案反推候选日期。
  • 相关记录:BUG-032、BUG-063、BUG-067
  • 复发自:无
  • 修复版本:待提交(本地可测)

BUG-069 | 生时校正最终候选被收集阶段约束退回并无限追问

  • 状态:resolved
  • 首次发现:2026-07-25
  • 最近更新:2026-07-25
  • 影响面:生时校正连续评分、候选收敛、VedAstro 外部验证与有限结果终止
  • 用户现象:用户连续提供多条真实经历后,本地候选已经缩小到单分钟或极窄范围,系统仍可能退回原始范围并继续追问;提问还可能停留在“为什么辞职、主动还是被动、造成什么影响”等不会改变当前评分的细节。
  • 触发条件:Technical Packet 收到单分钟候选或不足两个新建议领域;本地候选宽度已经适合外部验证但最终 margin 尚未达到确认阈值;事件达到旧的 3 条/2 领域门槛却未达到前端 4 条/3 领域门槛;候选连续多轮不再变化;或 family/other 背景事件被编排层计入评分覆盖。
  • 根因:收集阶段和最终确认阶段共用了错误门槛。Technical Packet 把“没有足够的新问题可问”当成候选无效,Orchestrator 又把窄候选失败静默替换为 baseRange;评分、技法合同和 Candidate Schema 分别使用 3/2 与 4/3 门槛,前两条事件不进入评分;plateau 只记录不终止,系统验证阻塞继续被转换成用户问题。外部验证入口还被误收紧为最终确认所需的 width <= 5margin >= 20%。同时 family/other 虽不进入 Python 评分器,却仍帮助编排层满足事件数和领域数;Agent 也继续追问不参与当前评分的原因、主动性和影响。原确定性校验只识别正确标注为 event_detail 的请求,模型可把同一详情问题错标成 new_event 绕过;最终轮也只靠提示词要求 evidenceRequest=null
  • 修复:建立前后端共享的收敛策略:第一条有效事件即评分,最终确认统一要求至少 4 条事件、3 个可评分领域、候选宽度不超过 5 分钟且 margin 不低于 20%。Technical Packet 接受单分钟和零建议领域;删除窄候选失败后退回 baseRange 的路径。候选在证据覆盖后连续两轮不变时结束为 completed + pending_validation,仅剩系统验证阻塞时直接结束有限结果,不再追问用户。外部验证单独使用最多 15 分钟的入口宽度,只要求唯一领先和必需层完整,不再提前要求最终 margin。编排层统一排除 family/other 的评分与确认计数,但继续持久化并展示为背景。Narrative Agent 的共享校验器禁止对已评分事件继续追问无计算价值详情,包括错标为 new_event 的详情语义;最终轮任何非空 evidenceRequest 都会触发重试,连续失败后才使用既有无追问 fallback。
  • 验证:前端聚焦测试覆盖结构化日期确认、单事件起评、单分钟候选、plateau/系统阻塞终止、家庭背景不进入评分计数、已评分事件详情错标重试,以及最终轮非空 evidenceRequest 重试。Python 聚焦测试覆盖长对话窄化后恢复 VedAstro 调用、4 条/3 领域统一门槛、官方分钟快照判别与 Technique Contract。
  • 防复发:必须区分“开始本地评分”“进入外部验证”“允许最终确认”三个阶段;没有新问题可问不得解释为候选无效。只有评分器实际支持的领域可以推进收敛计数,模型提问必须能改变日期、事件身份或评分领域,否则由确定性校验拒绝。
  • 相关记录:BUG-064、BUG-066、BUG-068
  • 修复版本:待提交(本地可测)

BUG-070 | 结构化日期确认迁移未接入生产迁移工作流

  • 状态:resolved
  • 首次发现:2026-07-25
  • 最近更新:2026-07-25
  • 影响面:生产环境生时校正 follow-up 持久化、日期候选确认与数据库约束
  • 用户现象:代码已写入 promptanswerModeproposedDate,但生产迁移工作流不会上传或执行对应 migration;生产数据库仍可能按旧 JSON 合同拒绝新 turn。
  • 触发条件:从 GitHub Actions 手动执行生产生时校正 migration 的 checkapply 操作。
  • 根因:新增 20260725010000_structured_conversational_date_confirmation.sql 后,没有同步加入工作流的 scp 上传列表和远端顺序执行列表。
  • 修复:将该 migration 同时加入上传与执行列表,继续复用现有 checksum、迁移账本和单事务执行保护。
  • 验证:静态回归断言 migration 文件名在生产工作流中恰好出现两次,分别覆盖上传和执行路径。
  • 防复发:新增需要生产执行的 migration 时,必须同时更新受审生产迁移清单并由测试覆盖上传和执行两处引用。
  • 相关记录:BUG-068、BUG-069
  • 修复版本:待提交(本地可测)

BUG-071 | 结构化日期校验器的冗余正则在 PostgreSQL 执行时报错

  • 状态:resolved
  • 首次发现:2026-07-25
  • 最近更新:2026-07-25
  • 影响面:生产环境生时校正首次建案、proposedDate 持久化与预留点数释放
  • 用户现象:开始生时校正时接口返回 409 action_conflict 和“请加载最新进度后再试”,预留点数随后自动释放且没有创建 case。
  • 触发条件:首轮 Agent 生成带 followUp.answerMode=yes_nofollowUp.proposedDate 的 evidence request,数据库开始执行结构化日期校验。
  • 根因:20260725010000_structured_conversational_date_confirmation.sql 同时保留了一个冗余的聚合日期正则;该正则括号不平衡,PostgreSQL 在函数运行时抛出 2201B invalid regular expression。下方按 year/month/day 精度分别校验的正则已经完整覆盖格式约束。
  • 修复:新增向前迁移 20260725020000_repair_structured_conversational_date_validator.sql,删除冗余聚合正则,只保留现有精度分支;同步加入生产迁移工作流。
  • 验证:生产事务回滚 dry-run 通过;以 2020-10 + month + yes_no 调用校验器返回 true;迁移账本、函数定义和生产健康状态均复核。
  • 防复发:数据库 JSON 合同的日期格式只维护一组精度分支;新增生产迁移时必须执行真实 PostgreSQL 函数调用,而不是只做 SQL 文本断言。
  • 相关记录:BUG-067、BUG-070
  • 修复版本:本次修复提交(生产已向前迁移)

BUG-072 | 生时校正首轮在零事件时执行全范围扫描并阻塞输入

  • 状态:resolved
  • 首次发现:2026-07-25
  • 最近更新:2026-07-25
  • 影响面:首页生时校正入口、start 编排、首次会话持久化与咨询问题 handoff
  • 用户现象:从首页进入生时校正后长时间停留在加载状态;未知时间或宽时段资料最明显,首问出现后输入框也可能继续等待会话同步。
  • 触发条件:新建生时校正 case,尤其声明时间为全天未知或宽时段;首页没有待转交咨询问题时也会读取 durable handoff。
  • 根因:start 在尚无人生事件时仍构建 Technical Packet,按声明范围逐分钟重排,随后再调用叙事模型生成固定性质的首问;前端收到首轮后又串行等待 persistSession(),并且直接首页入口无条件读取 durable handoff。
  • 修复:start 只按已声明时间范围创建确定性首轮和待验证候选,不做分钟扫描或模型生成;第一条有效事件回答后才进入原技术计算与叙事链。首页先展示首轮并开放输入,再后台同步会话;只有存在本地或显式待转交问题时才读取 durable handoff。
  • 验证:Orchestrator、入口、客户端、路由、持久化与同会话写入队列聚焦测试 144/144 通过;目标 ESLint 与 git diff --check 通过。覆盖首轮不扫描/不生成叙事、先展示后按序后台持久化,以及无 handoff 时跳过 durable 读取。
  • 防复发:零证据首轮不得执行分钟级技术计算或模型调用;非关键会话同步不得阻塞已持久化 case 的首轮交互;可选 handoff 读取必须由实际 handoff 状态触发。
  • 相关记录:BUG-055、BUG-065、BUG-067
  • 修复版本:待提交(本地可测)

BUG-073 | 咨询入口与持久化出生时间能力不一致

  • 状态:resolved
  • 首次发现:2026-07-25
  • 最近更新:2026-07-25
  • 影响面:首页状态文案、每日观察入口、推荐初始问题、/api/consult 出生时间模式判定
  • 用户现象:一类用户没有具体出生分钟,首页却仍诱导个人星盘问题;另一类用户已经保存准确到分钟的填报时间,旧标签页或旧客户端仍可能把咨询请求标为 general_no_birth_time,随后错误提示当前一般咨询模式不能生成个人星盘结论。
  • 触发条件:无分钟资料仍出现个人入口,或数据库已保存合法 family_exact / hospital_record / approximate 填报分钟,但客户端咨询模式停留在旧的 general_no_birth_time
  • 根因:首页没有复用出生时间路由结果来约束入口能力;同时 /api/consult 只在客户端请求星盘模式时加载 profile,收到 general_no_birth_time 时完全信任客户端状态,没有用持久化资料纠正陈旧模式。
  • 修复:首页复用 resolveBirthTimeConsultationRoute():无具体分钟时只显示一般知识文案和问题,有合法填报分钟时保留个人星盘入口。服务端对一般咨询请求也 best-effort 读取 profile;若持久化资料含合法未校正填报分钟则提升为 unverified_birth_time,若为 confirmed 且有合法 active time 则提升为 verified_chart,并继续由服务端 profile 构建星盘输入。period_only / unknown 仍保持一般咨询,不虚构分钟,也不把用户填报时间伪装成已校正时间。
  • 验证:聚焦测试覆盖无分钟首页入口、陈旧 general 请求被准确分钟 profile 提升为 unverified_birth_time,以及无具体分钟仍保持 general_no_birth_time;目标 ESLint、Webpack 构建与 git diff --check 通过。
  • 防复发:首页可点击入口必须与 resolveBirthTimeConsultationRoute() 的能力结果一致;服务端不得把客户端咨询模式当作出生资料真源,具体填报分钟必须进入 unverified_birth_time,只有 confirmed + active_birth_time 才能进入 verified_chart
  • 相关记录:BUG-009
  • 修复版本:待提交(本地可测)

BUG-074 | 生时校正首轮隐藏已知候选范围并让用户误以为系统没有读取资料

  • 状态:resolved
  • 首次发现:2026-07-25
  • 最近更新:2026-07-25
  • 影响面:生时校正新建 case 的确定性首轮引导
  • 用户现象:用户已经填写具体出生时间和误差范围,进入生时校正后却只看到泛化的重大事件提问,不知道系统实际正在核对哪个时间窗口。
  • 触发条件:新建 conversational-evidence-v3 case,首轮候选已经包含 rangeStart / rangeEnd,但可见 narrative 没有显示它们;旧生产版本还会调用模型生成同类泛化开场。
  • 根因:BUG-072 的快速首轮正确保留了候选范围,却只把范围写入结构化 candidate;首页不单独渲染该字段,固定 narrative 仍沿用了不含范围的旧提问。
  • 修复:复用首轮已经计算出的声明范围,在确定性 narrative 中明确显示当前待核对边界,并说明它不是已确认分钟;继续只问一件带年月的重要经历,不恢复零证据模型调用或分钟扫描。
  • 验证:Orchestrator 回归断言首轮正文包含真实 rangeStartrangeEnd、未确认边界和单事件提问;聚焦测试、目标 ESLint 与 git diff --check 通过。
  • 防复发:只要首轮已向用户展示生时校正状态,可见文案必须同时呈现结构化候选边界;不得只在隐藏状态中保存范围,也不得为了润色首问重新引入模型调用。
  • 相关记录:BUG-055、BUG-067、BUG-072
  • 修复版本:待提交(本地可测)

BUG-075 | 生时校正强制每轮提问并阻碍开放叙事

  • 状态:resolved
  • 首次发现:2026-07-26
  • 最近更新:2026-07-26
  • 影响面:生时校正 Narrative Agent、非评分轮续接、确定性首轮、聊天消息完成态
  • 用户现象:用户叙述一段或多段人生经历后,Agent 会机械追加问题、反复继承上一轮追问,并在普通资料交流结束后显示“回答已完成”,整体表现像问卷收集器而不是能理解人生轨迹的校时助手。
  • 触发条件:非最终轮模型只做自然回应但同时返回结构化追问,或本轮没有生成新追问却沿用上一轮 evidenceRequest;所有 settled Assistant 消息都会渲染统一完成标签。
  • 根因:Narrative Prompt 强制每个非最终回复恰好以一个问题结束;服务端 repair 会把隐藏的 evidenceRequest.prompt 自动追加到正文;非评分轮会自动生成缺失信息追问并继承旧 evidenceRequest;通用消息组件把 settled 状态映射成“回答已完成”。
  • 修复:允许用户一次叙述一件或多件经历并自由连续表达,问题改为 Agent 可选行为;删除自动补问和已评分事件的聊天限制,普通自然回复使用 evidenceRequest: null;首轮只开放邀请叙述,非评分轮不再自动规划或继承问题,仅在明确的结构化纠正承接中保留必要状态;settled 消息不再渲染活动状态。事件抽取、保存、技术评分、候选收敛和分钟确认安全门保持不变。
  • 验证:Narrative Agent、Orchestrator 与聊天布局聚焦测试 101/101 通过;目标 ESLint 无错误或警告;Next.js 16 Webpack 生产构建与 git diff --check 通过。
  • 防复发:代码不得要求每轮必须提问,也不得把隐藏 prompt 自动拼接到 Agent 正文;没有可见短答问题时 evidenceRequest 应为 null,用户叙述节奏由 Agent 与用户共同决定,代码只维护记录、抽取、评分、收敛与确认边界。
  • 相关记录:BUG-070、BUG-072、BUG-074
  • 修复版本:待提交(本地可测)

BUG-076 | 完整出生资料已保存但权威档案状态为空

  • 状态:resolved
  • 首次发现:2026-07-26
  • 最近更新:2026-07-26
  • 影响面:账户出生资料保存、星盘库同步、POST /api/consult 服务端资料校验
  • 用户现象:出生日期、家人确认的具体时间、地点、坐标和时区都已填写,星盘库副本也显示 birthTimeStatus: reported,但咨询仍返回“暂时无法核对完整出生资料”。
  • 触发条件:首次创建 profiles 行,或既有合法出生声明的 birth_time_status 为空且用户原样重新保存。
  • 根因:分钟校正主链合并时把 BUG-018 的首次档案派生状态分支退回为 {};共享 helper 又只在声明字段发生变化时写 reported,导致空状态行原样重存也无法自愈。chart_profiles.profile 只是星盘库 JSON 副本,不是咨询接口采用的权威出生资料。
  • 修复:账户路由统一调用共享状态派生 helper;新档案和合法声明空状态均原子写入 reported,确认分钟继续禁止被普通资料编辑覆盖;增加前向迁移,仅回填没有 active/candidate/case 应用结果且声明满足来源规则的空状态行。
  • 验证:账户 helper 回归覆盖新档案、空状态原样重存、候选失效和 confirmed 保护;迁移契约锁定安全过滤条件,并要求生产迁移工作流上传和应用各一次。
  • 防复发:服务端权威 profiles 的声明和状态必须同写;星盘库 JSON 不得作为咨询真值,历史空状态只能通过受约束前向迁移修复。
  • 相关记录:BUG-018、BUG-073
  • 复发自:BUG-018
  • 修复版本:待提交(本地可测)

BUG-077 | Python 诊断门状态导致历史事件评分误报 503

  • 状态:resolved
  • 首次发现:2026-07-26
  • 最近更新:2026-07-26
  • 影响面:生时校正历史事件评分、Python 技术合同到 Web 候选结果的适配边界
  • 用户现象:用户提交第一条可评分历史事件后,请求返回 503,日志显示 stage=score_events error=ZodError,开放叙事无法继续收敛。
  • 触发条件:Python technique_contract.gates 返回诊断专用状态 diagnostic_fail
  • 根因:Python 引擎合法使用 diagnostic_fail 表示诊断未通过但不构成技术异常;Web wire schema 只接受 pass | fail | blocked | not_evaluated,因此在业务判断前拒绝整份候选结果。
  • 修复:Web wire schema 接受 diagnostic_fail,并在共享适配边界将其规范化为内部既有的 fail;不扩大持久化合同,不修改 Python 引擎。
  • 验证:适配器回归覆盖含 diagnostic_fail 的技术合同并断言内部状态为 fail;聚焦测试、目标 ESLint 与 git diff --check 通过。
  • 防复发:跨语言 wire schema 必须覆盖 Python 真源可返回的枚举;诊断状态在适配边界归一化,内部领域模型继续保持最小稳定集合。
  • 相关记录:BUG-067、BUG-075
  • 修复版本:本次修复提交

BUG-078 | 叙事模型超时导致已完成的事件评分整轮返回 503

  • 状态:resolved
  • 首次发现:2026-07-26
  • 最近更新:2026-07-26
  • 影响面:生时校正开放叙事、历史事件保存、候选收敛、Narrative Agent 降级路径
  • 用户现象:历史事件已被成功抽取和技术评分,但主叙事模型与备用模型超时后,整轮返回 503,用户必须重试且无法看到已记录结果。
  • 触发条件:非最终轮的两次叙事生成均超时、报错或未通过安全校验。
  • 根因:安全的 packet 驱动兜底已经存在,却只允许最终轮使用;首轮和中间轮在两次生成失败后直接抛出 RectificationNarrativeUnavailable,把非关键的文案依赖变成了事件记录与收敛的硬依赖。
  • 修复:所有阶段在两次生成失败后统一使用既有的确定性安全兜底;保留已完成的事件记录、技术评分与候选推进,evidenceRequest 为 null,不机械追问,也不展示模型未验证内容。
  • 验证:回归覆盖首轮超时、首轮非法问卷、首轮非法技术层和中间轮虚构引用,均返回安全兜底且不泄漏被拒内容;Narrative Agent 聚焦测试、目标 ESLint、生产构建与 git diff --check 通过。
  • 防复发:叙事模型只能影响自然语言表达,不能成为事件持久化和确定性收敛的单点故障;降级输出必须来自已验证 packet。
  • 相关记录:BUG-067、BUG-075、BUG-077
  • 修复版本:本次修复提交

BUG-079 | 无可用路由领域时 Agent 追问导致 paused 会话返回 503

  • 状态:resolved
  • 首次发现:2026-07-26
  • 最近更新:2026-07-26
  • 影响面:生时校正开放叙事、暂停后恢复、历史事件保存、Narrative Agent 到公开 turn 的适配边界
  • 用户现象:完整 synthetic smoke 在暂停并恢复后提交下一条历史事件,技术评分和模型叙事都成功,但接口返回 service_unavailable,事件无法保存。
  • 触发条件:技术 packet 的 suggestedDomains 为空,而模型自然提出了一个可继续回答的问题。
  • 根因:Narrative Agent 会把模型领域替换成服务端允许的领域;允许集合为空时产生 evidenceRequest.domains=[]。叙事校验未拒绝空数组,但公开 turn 合同要求至少一个领域,导致 turnFromNarrative() 在持久化前把整轮转换为 503。
  • 修复:在 Narrative Agent 的共享适配边界统一处理 authored 与 legacy 输出;没有任何服务端可验证路由领域时保留自然语言正文,只把可选 evidenceRequest 归一化为 null。不放宽公开持久化 schema,也不信任模型自报领域。
  • 验证:回归覆盖无路由领域的 authored 输出,并覆盖 pause → resume → answer 完整编排路径;确认正文保留、evidenceRequest=null、事件正常保存且不返回 503。
  • 防复发:模型自然语言不得成为事件保存的硬依赖;私有路由元数据为空时应省略可选状态,而不是生成违反持久化合同的半合法对象。
  • 相关记录:BUG-075、BUG-078
  • 修复版本:本次修复提交

BUG-080 | 开放叙事仍被技术上下文塑造成流程播报

  • 状态:resolved
  • 首次发现:2026-07-26
  • 最近更新:2026-07-26
  • 影响面:生时校正普通叙事轮、自然对话、追问状态持久化
  • 用户现象:用户叙述“2016 年离家去外地上大学”后,Agent 仍回答“先记为、对校时有价值、参与候选核对、不会确认某个分钟”,像记录员而不是自然交谈。
  • 触发条件:普通 intermediate 轮生成叙事时仍向模型提供 Technical Packet、候选分钟、分盘、事件台账和未决证据。
  • 根因:此前开放叙事只取消了“每轮必须提问”,没有隔离普通聊天与技术收敛上下文;模型继续模仿内部流程措辞。叙事没有可见问题时,模型返回的隐藏 evidenceRequest 还会进入下一轮状态。普通提示不再暴露事件 ID 后,旧 authored schema 又仍强制模型提交已有 evidenceId,形成合同矛盾。
  • 修复:普通叙事轮只向模型提供最近真实对话和本轮用户原话,禁止主动播报记录、校时价值、候选范围、分盘、评分与收敛;只有用户主动询问技术结果时才提供受约束技术包。没有可见问题的隐藏 evidenceRequest 统一清空;自然追问只由模型表达问题意图,目标事件 ID 由服务器依据活动事件和未决证据绑定。首轮、最终轮及主动技术询问仍保留技术边界。
  • 验证:Narrative Agent 与 Orchestrator 联合回归 99/99 通过,覆盖普通 prompt 不含 packet/eventLedger、隐藏追问清空、服务器绑定 follow-up、相对日期承接、fallback、暂停恢复和并发幂等。
  • 防复发:普通聊天的可见措辞不得由技术 packet 或事件台账驱动;内部收敛状态只能记录和计算,不能成为 Agent 每轮必须复述的脚本。
  • 相关记录:BUG-074、BUG-075、BUG-078、BUG-079
  • 修复版本:本次修复提交

BUG-081 | 开放叙事只共情不继续引导

  • 状态:resolved
  • 首次发现:2026-07-26
  • 最近更新:2026-07-26
  • 影响面:生时校正开放叙事的中间轮回复
  • 用户现象:用户讲完一段包含明确行动与转折的经历后,Agent 只分析感受和性格,没有自然询问后续经历,对话停在原地。
  • 根因:为消除问卷式强制追问,中间轮 prompt 将提问设为完全可选,且校验器只检查已有问题是否可见,没有要求事件轮承担继续引导叙事的职责。
  • 修复:普通中间轮 prompt 要求在本轮新事件后以一个贴着上下文的开放问题收尾;优先询问事件之后的下一步,继续禁止机械补年月、结果或转折。若模型仍只共情不引导,服务端直接补一句开放问题,不增加第二次模型调用;无模型可用时的安全兜底也保留自然后续问题。
  • 验证:回归覆盖“研究院实习后因价值观冲突辞职”场景,确认只共情不引导的回复会保留原文并补上后续问题,且不触发模型重试。
  • 防复发:开放叙事不等于被动倾听;事件轮默认负责自然推进,但不得恢复固定问卷模板。
  • 相关记录:BUG-075、BUG-080
  • 修复版本:本次修复提交

BUG-082 | 生时校正缺少独立证据状态机并把分钟峰值误导为产品答案

  • 状态:resolved
  • 首次发现:2026-07-26
  • 最近更新:2026-07-26
  • 影响面:生时校正建案、事件采集、日期修订、候选评分、暂停恢复、范围保存、原咨询问题 handoff 与扣费幂等
  • 用户现象:专业 Agent 可以持续收集跨领域事件、逐步补充日期并做稳定性复核,但 Web 流程把对话、抽取、评分和叙事绑在单次请求中;容易重复追问同一事件、出现等待状态无下一题、在低置信度下突出单个峰值分钟,并且刷新或多设备继续时缺少独立业务真源。
  • 根因:旧 conversational-evidence-v3 同时承担聊天体验和校时状态机;人生事件没有稳定的 eventId + revision 修订链,问题没有持久化目标事件,分钟扫描结果也没有强制经过范围聚类、日期敏感性、逐项排除和邻近分钟门控。聊天会话、计算任务和咨询 handoff 的生命周期耦合,无法独立恢复和仲裁并发。
  • 修复:新增 rectification-evidence-v4 独立领域模型、Case/Turn/Event Revision/Snapshot/Job/Handoff 持久化状态机和后台 Worker。先完成教育、迁移、关系、事业、财务、健康压力、家庭七领域覆盖,再对非日精度事件按 targetEventId 追加同一事件 revision;已追问或跳过的事件不循环。候选分钟先聚类,再通过事件数、领域数、范围宽度、邻近分钟、leave-one-out、日期敏感性和计算口径 hash 门;公开合同固定 canConfirmExactMinute=false,只允许用户主动保存候选范围,不更新 profiles.active_birth_time。Handoff 和扣费由 PostgreSQL lease、幂等 action 与 settlement receipt 保护。
  • 验证:前端 V4/domain/service/migration/replay/handoff/consultation continuation 33 个测试通过;Python 2 个测试通过;目标 ESLint 与 Webpack 生产构建通过。隔离 PostgreSQL 12 项不变量通过;真实 PostgreSQL + Python E2E 完成七领域与定向日期修订后进入 range_ready,主要范围为 05:2605:30,无空问题死状态,原 active_birth_time 不变且未主动保存前 acceptedRange 为空。静态浏览器验收确认日期修订输入框、范围保存按钮、非确认分钟文案和 390px 无横向溢出;真实登录态 UI 因隔离服务未连接认证配置,本轮未声称已验收。
  • 防复发:生时校正业务真源必须是 V4 Case 和 append-only 事件台账,不得从聊天正文反向恢复;任何公开结果只能显示通过稳定性门的范围,单分钟峰值不得成为可保存结果;证据不足必须生成下一题或明确暂停,不能进入 awaiting_answer + currentQuestion=nullV4 路径不得写 profiles.active_birth_time
  • 相关记录:BUG-067、BUG-069、BUG-072、BUG-074、BUG-075、BUG-080、BUG-081
  • 修复版本:待提交(本地可测)

BUG-083 | staging 质量门使用无可用 Linux sandbox 的浏览器运行时导致验收失败

  • 状态:resolved
  • 首次发现:2026-07-27
  • 最近更新:2026-07-27
  • 影响面:生时校正 V4 的 390px 真实浏览器验收、staging 镜像发布与后续部署
  • 用户现象:PR 质量门已通过且相同提交的其余 1031 个测试成功,但推送到 staging 后唯一的真实 Chromium 测试未能取得 DevTools 端口,导致不可变镜像未发布、部署未继续。
  • 触发条件:backend-quality-gate.yml 未显式供应带系统 sandbox 的浏览器,或安装 Playwright 默认的 chromium-headless-shell 后直接用原始二进制启动。
  • 根因:真实浏览器测试刻意保留 Chromium sandbox,但 Playwright 默认下载的 chromium-headless-shell 是原始构建;当前 GitHub Linux Runner 无法为该原始二进制建立所需 sandbox,进程以 SIGTRAP 退出。仅依赖 Runner 偶然预装的 Chrome 也不具备可重复性。
  • 修复:通过 Playwright 官方 chrome 安装通道供应系统级 stable Chrome 及其依赖,再执行真实浏览器合同;避免选择无系统 sandbox 包装的 headless shell,不跳过验收,也不增加 --no-sandbox
  • 验证:workflow 合同回归断言 Playwright stable Chrome 必须被安装;聚焦部署合同测试、完整前端测试、目标 ESLint、生产构建与 git diff --check 通过;staging 质量门和部署结果另行记录。
  • 防复发:保留浏览器 sandbox 的 CI 必须使用具备系统 sandbox 包装的浏览器安装通道,不能直接启动无相应宿主配置的原始 headless shell,也不能依赖 hosted runner 的偶然预装状态。
  • 相关记录:BUG-082
  • 修复版本:本次修复提交

BUG-084 | staging V4 Worker 与候选引擎位于不同 Docker 网络

  • 状态:resolved
  • 首次发现:2026-07-27
  • 最近更新:2026-07-27
  • 影响面:生时校正 V4 后台评分、第三条及后续可评分事件、staging 登录态验收
  • 用户现象:Case 创建和前两条事件成功,第三条事件触发候选引擎后 Worker Job 失败并返回 fetch failed
  • 触发条件:staging Worker 同时需要访问 PostgreSQL app 网络和 API 所在的 Compose default 网络。
  • 根因:rectification-v4-worker 只加入 app 网络;api 只在 default 网络。Worker 虽正确配置 JYOTISH_API_BASE=http://api:5200,但 Docker DNS 无法跨网络解析和连接 api
  • 修复:让 staging Worker 同时加入 defaultapp 网络,不改变 production compose,也不暴露 API 端口。
  • 验证:部署合同测试固定 Worker 的内部 API 地址和双网络配置;聚焦测试、Compose 渲染、目标 ESLint、git diff --check 及 staging 登录态 smoke 另行记录。
  • 防复发:需要同时访问内部 API 与 staging PostgreSQL 的服务必须在 Compose 合同中显式加入两个现有私有网络。
  • 相关记录:BUG-082
  • 修复版本:本次修复提交

BUG-085 | 生时校正回退为独立面板和固定领域问卷

  • 状态:investigating
  • 首次发现:2026-07-27
  • 最近更新:2026-07-28
  • 影响面:生时校正聊天 Surface、事件语义、后台 Job、候选计算、诊断、Reasoner、Renderer 与持久化主链
  • 用户现象:进入生时校正后看到独立的校正面板、证据区域和固定问题;交互不像普通 session,领域也不再根据用户刚讲的经历动态选择。
  • 触发条件:旧 V4 既在界面层使用独立校正结构,又让 question-planner.tsquestion-author.ts 直接决定领域顺序与问题文案;模型只负责写下一问,后台没有形成完整 Agent 决策闭环。
  • 根因:产品状态被压缩成“下一问字符串”,事件语义、候选特征、诊断结果、问题机会、模型决策和公开消息之间没有受约束的 durable contract;因此即使替换提示词,系统仍会沿用问卷式控制流,且无法审计模型为何选题或安全重放已完成 Job。
  • 修复:删除旧 question-planner.tsquestion-author.ts,将回答处理重构为完整 V5 主链:保存回答并创建后台 Job → Evidence Reconciliation → Candidate Engine / Feature Snapshot → Diagnostics → Opportunity Builder → Bounded Reasoner → Decision Validator → Renderer → Atomic Job Completion。可见层继续复用普通 session 聊天 SurfaceReasoner 只能选择服务端生成的 opportunity 或受约束动作,不能注入分钟、分数、事件或任意问题;Renderer 只表达已验证决定,候选范围不得表述为已确认出生分钟。Agent Run、Public Message、Diagnostics、Feature Snapshot、Pending Evidence 和事件修订均作为一等产物持久化。
  • 验证:67 个 TypeScript 聚焦合同全部通过,覆盖普通 session UI、完整 V5 artifact chain、Reasoner 单次诊断预算、Opportunity 选择、shadow/legacy 隔离和 range-only 输出;7 个 Python 服务合同通过。真实 PostgreSQL 14 已按 V4 → V5 顺序完成 migration dry-run,并跑通 processing → reasoning → rendering → complete、五类 artifact 各一条落库和 completed Job 幂等重放。tsc --noEmit 未出现 V5 新错误,只剩 birth-time-journey-engineidentity-auth-integrationonboarding-route 三处无关基线错误。当前完成边界为本地可测,尚未提交、推送、迁移 staging 或执行登录态 smoke。
  • 防复发:生时校正不得再次把模型降级为“问题文案生成器”;所有可见动作必须来自 server-owned opportunity,经 bounded reasoner、decision validator 和 renderer 后原子持久化。测试必须同时锁定 legacy/shadow 隔离、artifact 完整性、候选范围边界和 completed-job replay 指纹。
  • 相关记录:BUG-020、BUG-075、BUG-080、BUG-081、BUG-082、BUG-083、BUG-084、BUG-086
  • 修复版本:本地 V5 重构,待提交与 staging 验收

BUG-086 | 模型下一问可绕过当前事件而跳成领域问卷

  • 状态:investigating
  • 首次发现:2026-07-27
  • 最近更新:2026-07-28
  • 影响面:生时校正 V5 的当前事件延续、问题机会构建、诊断工具预算、模型决策验证和 Job replay
  • 用户现象:用户回答“2016 年离家去外地上大学”后,下一问直接变成“请说一次影响较大的搬家或长期迁居”,看起来仍按“升学 → 搬家”模板轮询,而没有承接刚才的具体经历。
  • 触发条件:当前事件仍缺必要精度,但旧 Worker 只校验模型返回结构;只要模型输出一个格式合法的新领域问题,就可以绕过当前事件和服务端已知证据缺口。
  • 根因:旧方案把“required continuation”作为给模型的提示,而不是服务器拥有的候选动作和最终决策约束;诊断结果也没有独立工具预算、持久化产物和可回放选择依据,无法阻止合法 JSON 携带错误业务路由。
  • 修复:Opportunity Builder 将未解决的当前目标设为独占路由,并只发布带稳定 ID、目标事件、效用分解和隐私成本的问题机会;Bounded Reasoner 最多执行一次只读诊断,最终只能选择活动 opportunity 或受限状态动作;Decision Validator 拒绝不存在、跨 Case、非活动或越权的机会,也禁止模型直接写问题、分钟、分数和事件。Reasoner 不可用、返回非最终诊断或耗尽预算时走同一确定性 fallback policyRenderer 根据 validated decision 生成自然语言承接,Worker 再通过单一 completion RPC 原子保存全部产物。
  • 验证:对抗合同覆盖“当前目标独占下一问”“只能选择服务端活动 opportunity”“诊断预算耗尽 fail closed”“模型不得注入问题/分钟/事件/分数”和“Reasoner/Renderer 不可用时确定性降级”。真实 PostgreSQL completed-job replay 已验证:相同完整 payload 指纹返回既有 Case;任一 artifact 改变且指纹不同会抛出 rectification_v5_replay_payload_mismatch,不会二次写入或接受漂移结果。当前仅完成本地验证,staging 行为仍待发布后验收。
  • 防复发:当前事件延续必须是服务端 opportunity 所有权规则,而不是 prompt 建议;模型输出即使结构合法,也必须经过 bounded tool budget、active-opportunity lookup、decision validation 和 completion payload hash 四层门控。
  • 相关记录:BUG-075、BUG-085
  • 修复版本:本地 V5 重构,待提交与 staging 验收

BUG-087 | 语义机会仍退化为固定文案并在拒绝后重复追问

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:生时校正 Agent 的证据协调、问题机会、Reasoner 上下文、Renderer、候选范围提示与 Skill 合同
  • 用户现象:对话会把月份已经明确的经历继续机械追问具体日期,按固定领域顺序轮询;用户表示不知道、跳过或换方向后仍可能回到同一事件,Renderer 还会重复套话和未变化的候选范围。
  • 触发条件:Question Opportunity 直接持久化最终 prompt,Renderer 再用服务端问题覆盖自然生成结果;缺失领域按固定数组选择,日期策略把所有非日精度事件视为未完成,且证据协调未完整区分拒绝、未知、换向和回答了另一事件。
  • 根因:机会合同混合了“为什么问、要补什么字段”和“最终怎么说”,导致 Reasoner 看不到最近对话语义、Renderer 无法安全自然表达;同时 target disposition 和重复追问预算不完整,固定领域与日期控制流绕过了信息增益、隐私成本和稳定性诊断。
  • 修复:升级为兼容旧 promptsemantic-question-v2,由 Builder 同时生成并按 utility 排序最多五个活动机会;补齐 resolved / unknown / declined / direction_change / answered_other_event / unresolved / not_applicable,限制同一目标连续追问和回答其他事件后的温和补问次数;月份默认足够,仅在日期敏感诊断不稳定时细化。Reasoner 获得脱敏的最近 Turn、事件、目标和机会语义;Renderer 改为验证自然问题并在失败时使用锚定 fallback,同时只在公开门首次通过或范围实际变化时播报范围,相同范围即使再次计算或收到 offer 决策也不重复播报。受限模型辅助提取仅补充确定性解析缺口,服务端继续校验原文子串和日期。
  • 验证:V6 语义合同、日期敏感性、拒绝/换向、回答新事件不覆盖旧事件、单次补问、Renderer 锚点/单问题/分钟注入/内部信息拒绝、候选范围去重、Reasoner 上下文、模型辅助提取和旧 Opportunity 兼容测试通过;四轮端到端测试覆盖月份职业事件、外地入学、无日期搬家换向和后续职业事件,并保留 exact-minute、Profile 写入、legacy/shadow、V5 候选引擎与 completed-job replay 边界。完整前端测试、lint、TypeScript 与 Python 结果见本任务交付记录。
  • 防复发:问题机会只表达服务器拥有的语义目标和约束,最终文案必须通过 realization validator;日期追问必须有诊断依据,拒绝/换向必须关闭目标,领域选择必须由 utility 和上下文驱动。Skill 明确禁止固定问卷、重复范围、唯一分钟、D60 驱动和公开内部评分/技术轨迹。
  • 相关记录:BUG-068、BUG-075、BUG-080、BUG-081、BUG-082、BUG-085、BUG-086
  • 复发自:BUG-085、BUG-086
  • 修复版本:birth-time-rectification-v6 / rectification-agent-v6-1,本地验证完成,待提交与 staging 发布

BUG-088 | TypeScript 全量检查被过期测试夹具阻塞

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:npx tsc --noEmit 本地发布前质量门
  • 用户现象:生产构建通过,但全量 TypeScript 检查在三个测试文件报错:事件评分输入仍传入服务端固定的 high_rigor、身份全局缓存清理被控制流误收窄为 never、onboarding 测试未归一化 PostgreSQL Date 联合类型。
  • 根因:测试夹具落后于现有生产合同;这些报错不来自 V6 Agent 运行时,但会让显式 TypeScript 验证失败。
  • 修复:删除客户端不应拥有的 high_rigor 输入;在异步请求结束后从 globalThis 重新读取身份缓存;按生产边界把 Date 归一化为日期字符串后再构建 onboarding cache identity。
  • 验证:npx tsc --noEmit、相关前端测试和生产构建通过。
  • 防复发:测试输入只使用公开类型拥有的字段;异步初始化的全局缓存不要依赖删除前的局部控制流;数据库日期联合类型在进入纯字符串合同前必须归一化。

BUG-089 | Staging CI 自动升级 MCP 2.0 导致旧 FastMCP 导入失败

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:Staging Backend Quality Gate 的 Python quick quality gate 与 staging 发布
  • 用户现象:本地完整验证通过,但 staging push 的质量门在导入 mcp_server.py 时失败,报 ModuleNotFoundError: No module named 'mcp.server.fastmcp'
  • 根因:requirements.txtpyproject.toml 仅声明 mcp>=1.0CI 在 2026-07-29 安装了不兼容的 mcp 2.0.0,而仓库当前服务端仍使用 MCP 1.x 的 mcp.server.fastmcp.FastMCP 导入合同。本地环境保留 mcp 1.25.0,因此未复现依赖漂移。
  • 修复:两个发布依赖入口统一限制为 mcp>=1.0,<2,继续使用已验证的 MCP 1.x API,不在本次发布中混入 MCP 2.0 迁移。
  • 验证:新增依赖合同测试同时读取 requirements.txtpyproject.toml,防止任一入口再次放宽到 MCP 2.xPython quick quality gate 与构建重新执行。
  • 防复发:运行时依赖的主版本兼容边界必须在全部安装入口保持一致;升级 MCP 2.x 必须作为独立迁移处理并先替换导入/API 合同。
  • 相关记录:BUG-087、BUG-088
  • 修复版本:待本次 staging 修复提交与部署验收

BUG-090 | V6 审查发现用户可见分钟注入、家庭事件越权和最新事件排序错误

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:生时纠正 Renderer、模型辅助事件提取、Opportunity 与 Reasoner 最近事件上下文
  • 用户现象:模型可能在 acknowledgement 或 limitation 中声称唯一出生分钟;家庭健康事件可能被矛盾模型字段伪装成本人可评分事件;UUID 排序与创建时间相反时,下一问可能承接较早经历。
  • 根因:分钟安全验证只覆盖 question;辅助提取校验未拒绝 subject=self 与非空家庭 relatedPerson 的矛盾组合;账本的稳定 UUID 排序被误当作会话时间顺序。
  • 修复:全部用户可见 Renderer 字段统一执行出生分钟和内部信息安全校验并回落到服务器确定性文案;辅助提取在服务器拒绝主体/亲属矛盾并保持家庭事件 context_only 边界;Builder 与 Reasoner 显式按 createdAteventId、revision 稳定排序最近事件,不改变证据哈希使用的账本排序。
  • 验证:新增 acknowledgement/limitation 分钟注入、家庭 ICU 事件越权、UUID 与创建时间逆序的回归测试,并重新运行前端完整测试、lint、TypeScript 与构建。
  • 防复发:所有模型可写用户文案共享同一安全边界;模型提取不能决定评分主体;用于哈希的稳定顺序不得被复用为会话时序。
  • 相关记录:BUG-075、BUG-086、BUG-087
  • 修复版本:待本次 staging 修复提交与部署验收

BUG-091 | Staging Case rollout 未启用 V5 Agent 导致 V6 继续输出固定模板

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:staging 生时纠正新 Case 与 V6 未完成 Case 的用户可见回复
  • 用户现象:用户提交“2016 年 9 月离家去外地上大学”后,回复仍固定为“我记下了这段经历。接下来请继续讲另一件……”,没有进入 Semantic Question Renderer。
  • 根因:Case rollout 只写入 V3 创建门和 smoke 状态,没有写入 RECTIFICATION_AGENT_V5_ENABLEDRECTIFICATION_AGENT_V5_SHADOWRECTIFICATION_AGENT_V5_CANARY_PERCENT;因此 selectRectificationDeploymentMode() 把新 Case 持久化为 v4_legacyOrchestrator 必然调用 Legacy Projector。
  • 修复:受控 staging rollout 现在原子写入 V5 Agent 开关,public 与 smoke rollout 使用 v5_agent、100% canary,并重建 web/workerstaging 中唯一满足 V6 版本、未完成、无 open Job 条件的错误 Case 已原位升级为 rectification-evidence-v5 / v5_agent,历史 Turn、Event、Job 与 Agent Run 保持不变。
  • 验证:rollout 脚本测试断言三项 V5 选择器只写一次,并在成功前核对 web/worker 容器实际读取的 enabledshadowcanarystaging 活跃 Case 聚合只剩 v5_agent;健康检查保持 exact SHA、public、ready。
  • 防复发:公开 Case rollout 必须同时控制创建门与 Agent deployment mode,并验证运行容器的实际环境;仅有 readyForNewCases=true 不再视为新对话 Renderer 已启用的充分证据。
  • 相关记录:BUG-085、BUG-086、BUG-087
  • 修复版本:birth-time-rectification-v6 / rectification-agent-v6-1

BUG-092 | Semantic Renderer 成功后仍被静默替换成固定领域模板

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:V6 ask_new_event Opportunity 排序、问题验证、Renderer telemetry 与 staging 用户可见下一问
  • 用户现象:用户提交“2020 年 4 月去石油化工研究院实习做研究员”后,系统仍显示“承接……请再说一件……哪次搬家、离乡或长期迁居……”,像固定问卷。
  • 根因:Builder 将最近六轮答案拼接为当前主题,使较早教育事件中的“离家/外地”再次提升 relocation,同时未覆盖领域奖励在已经满足最小领域数后仍占主导;Renderer 对自然 new_dated_event 问法使用过窄词面校验,校验失败后静默替换为 Builder 固定 fallbacktelemetry 仍记为 renderer succeeded
  • 修复:当前主题只读取最新回答或最新事件;达到两个可评分领域后显著降低纯领域覆盖收益并提高最新主题连续性;移除“承接……请再说一件……”拼接,fallback 改为锚定当前经历的单句问题;放宽自然新事件词面但继续执行单问题、锚点、内部信息和出生分钟安全校验;validator 回退单独记录 renderer rejected 与脱敏错误码。
  • 验证:真实两事件重放断言 career 机会优先于旧 relocation 关键词、月份不被细化、旧固定模板被拒绝、锚定研究院实习的自然问题被保留且不等于 fallback;完整前端、lint、TypeScript、Python V5 与 staging smoke 随发布记录执行。
  • 防复发:模型调用成功、Schema 成功和问题被接受必须分开观测;Opportunity utility 不得把历史关键词与“未覆盖领域”组合成伪装的固定轮询。
  • 相关记录:BUG-086、BUG-090、BUG-091
  • 修复版本:birth-time-rectification-v6 / rectification-agent-v6-1

BUG-093 | 首次候选评分因跨语言计算规格哈希不一致而失败

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:V5 Candidate Engine 首次达到三事件评分门后的 Feature Snapshot 原子持久化
  • 用户现象:第三件可评分事件已经保存在 Turn,但 Worker 最终显示“这次比较没有完成,回答已经保留,请再试一次”,Case 恢复上一问题且没有写入新事件。
  • 根因:服务器创建 Case 时由 TypeScript 对整数时区 8 计算规格哈希;Python 请求归一化把它变成浮点数 8.0,而 Python JSON 序列化保留 .0。两个运行时对语义相同的规格得到不同哈希,完成事务因此拒绝 Feature Snapshot 并抛出 rectification_v5_feature_snapshot_mismatch
  • 修复:Python 生成跨服务 Calculation Spec 时将整数值的经纬度和时区规范化为整数,使其 JSON 数字表示与 TypeScript JSON.stringify 一致;评分算法和候选矩阵不变。
  • 验证:新增已知 TypeScript 哈希向量测试,修复前稳定失败、修复后通过;staging Case e2d3e1d2-efb0-461d-9914-f890bc2b8569 的 PostgreSQL 日志确认原始异常,使用同一规格重放确认 Python Feature Snapshot 哈希恢复为 Case 哈希。
  • 防复发:跨语言持久化指纹必须使用已知向量验证 JSON 数字规范化,不能只在各自语言内断言自洽。
  • 相关记录:BUG-087、BUG-092
  • 修复版本:rectification-v5-matrix-scoring-1(仅修复输入规范化,算法版本不变)

BUG-094 | V5 Agent 消息操作栏在 V4 面板切换后消失

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:生时校正 V5 Agent 对话、助手消息反馈与当前问题重新生成
  • 用户现象:助手消息气泡下不再显示点赞、点踩、复制和重新生成操作。
  • 触发条件:生时校正页面使用 RectificationV4Panel 渲染 V5 Agent 会话。
  • 根因:旧对话组件中的消息操作栏没有迁入 V4/V5 共用面板,同时新 V4 API 没有与当前语义问题绑定的重新生成命令。
  • 修复:复用现有消息操作栏样式,仅为 v5_agent 的稳定助手消息恢复操作;重新生成只重写当前已验证 Semantic Question Opportunity 的自然语言实现,并通过用户、Case 版本、当前目标和 action ID 原子校验,不重跑事件提取、候选评分、诊断或 Job。
  • 验证:组件资格与反馈互斥测试、V4 service 幂等重放与数据不变量测试、Renderer 安全回落测试、迁移契约测试,以及 staging 精确 SHA 部署和浏览器验收。
  • 防复发:测试锁定 V5 Agent 操作栏、仅当前问题可重跑、legacy/shadow 不启用新 Renderer、重跑不改变 turns/events/snapshots/profile 且 completed Job 不被改写。
  • 相关记录:BUG-090、BUG-091、BUG-092、BUG-093
  • 复发自:无
  • 修复版本:birth-time-rectification-v6 / rectification-agent-v6-1

BUG-095 | 生时校正“思考中”无法证明实际执行步骤且刷新后不可溯源

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:V4/V5 生时校正运行状态、历史助手消息、候选计算与诊断的测试可观测性
  • 用户现象:页面只显示“正在核对星盘信息……”动画,无法判断本轮实际执行了哪些阶段、工具和技法;任务结束或刷新后也无法回看。
  • 根因:Job 只保存一个会被后续步骤覆盖的当前 phase,不能还原阶段历史;已持久化的 Public Message 与 Agent Run 工具记录没有被 Case API 投影到对应 TurnUI 的 thinking 状态只是客户端进度动画,不是模型 reasoning,也不是服务端执行收据。
  • 修复:将“分析过程”定义为与 Turn 关联、可持久化和刷新后可恢复的服务端执行收据;只投影实际发生的阶段、工具调用和 allowlist 技法。供应商显式返回的 reasoning 内容只有通过服务端来源校验与安全过滤后才可作为可选摘要,缺失或不安全时直接省略,不伪造且不读取 hidden chain-of-thought。
  • 安全边界:不公开分数、权重、贡献矩阵、内部 ID/字段、候选分钟、工具参数或原始结果、Prompt、模型内部信息和用户敏感原文;D60 不展示且不驱动结论;未执行、不可用或仅供参考的技法不得显示为已执行。
  • 兼容边界:历史无收据记录继续读取;v4_legacyv5_shadow 保持原有用户可见回复,shadow 仅持久化新产物而不展示分析轨迹;completed-job replay、原子 completion、canConfirmExactMinute === false 和禁止自动写入 profiles.active_birth_time 保持不变。
  • 防复发:API 与组件测试必须锁定 Turn 关联、刷新恢复、运行中真实 phase、仅展示实际工具/技法、无安全 reasoning 时不补写摘要,以及旧记录、legacy、shadow 的兼容行为。
  • 相关记录:BUG-011、BUG-086、BUG-087、BUG-094
  • 修复版本:birth-time-rectification-v6 / rectification-agent-v6-1

BUG-096 | PostgreSQL 日期对象导致生时纠正 Case 创建误报资料格式错误

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:staging 自托管 PostgreSQL 出生资料读取、历史时区偏移补全与生时纠正 Case 创建
  • 用户现象:已完成出生资料的账号启动生时纠正时返回 400,并显示“出生资料或提交内容格式不正确”。
  • 触发条件:数据库驱动将 birth_date 返回为 JavaScript Date,且历史资料的 timezone_offset 为空、需要根据日期和时区 ID 补全。
  • 根因:共享 resolveMissingBirthTimezoneOffset() 只把非空字符串识别为出生日期;合法 Date 被当成缺失值,补全提前返回,随后严格出生资料解析以 Historical timezone offset must be resolved before parsing 拒绝请求。
  • 修复:共享时区补全 resolver 在读取出生日期时同时接受有效 Date 与既有字符串,并统一归一化为 YYYY-MM-DD;不放宽后续 Zod 校验,也不改变生时纠正计算规格。
  • 验证:路由回归夹具改为 PostgreSQL 实际返回形态的 Date;聚焦测试、前端全量测试、lint、TypeScript、生产构建和 Python V5 服务测试通过,staging 精确 SHA smoke 随本次发布执行。
  • 防复发:所有从 PostgreSQL 进入纯日期字符串合同的共享边界必须先覆盖 string | Date 驱动返回类型;Case 创建继续要求历史时区偏移在严格解析前完成。
  • 相关记录:BUG-076、BUG-091、BUG-095
  • 复发自:无
  • 修复版本:待本次 staging 修复提交与部署验收

BUG-097 | 生时纠正分析状态停滞且教育事件被换词重问为迁居

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:V5 Agent 生时纠正运行状态、历史消息布局、Semantic Question Opportunity 排序与 Renderer 安全回退
  • 用户现象:任务实际已经经过“生成语义问题机会、选择下一步动作、生成安全回复”,页面运行中却一直显示“正在整理你刚才提到的经历”;完成后的“分析过程”显示在 Agent 正文下方;用户已经说明离家去外地上大学的年月后,下一问仍把同一经历换词问成搬到新城市或长期离乡。
  • 触发条件:Job 轮询返回新的 phase,但聊天消息继续读取首次 Case 响应中的旧 data.job;同时教育事件原文中的“离家、外地”命中迁居领域关键词并抬高 relocation 机会,Renderer 失败回退后直接使用带“以当前事件为时间参照”的模板。
  • 根因:Hook 同时维护 data.job 与独立 Job state,轮询只更新后者;恢复 processing Case 时 API 没有返回 active Job,刷新后无法继续轮询;setInterval 允许并发请求,较旧响应可能覆盖较新 phase;机会排序把零散地点词当成迁居主题信号,没有优先采用最新账本事件的已确认领域;迁居 fallback 和问题验证器没有禁止把当前事件改写成另一件事件继续追问。
  • 修复:轮询结果原子合并回 data.job,消息状态直接跟随服务端 extracting_evidence / planning_question / reasoning / renderingCase Store 和 API 为 processing Case 恢复最新 pending/processing Job;轮询改为单飞 setTimeout 并拒绝较旧响应;把持久化分析收据移动到 Agent 正文上方并保留正文下方操作栏;迁居关键词收窄为明确搬迁动作,最新事件主题加权以账本领域为准;所有新事件 fallback 明确询问另一件独立事件,Renderer 拒绝旧跨事件模板、同义重复和与所选机会领域不匹配的问题。
  • 验证:组件测试锁定分析过程、正文、操作栏顺序、连续 Job phase 更新及乱序响应不倒退;服务测试锁定 processing 刷新恢复 active Job 和 shadow 年精度 legacy 细化;Agent 测试锁定外地上大学不会提升 relocation、事件数组顺序不改变排序、旧模板与跨领域实现触发安全 fallback;完整前端测试、lint、TypeScript、staging 构建、Python V5 服务测试和 staging 精确 SHA smoke 随本次发布执行。
  • 防复发:UI 只允许一个 Job 真源,恢复 processing Case 必须携带 active Job,轮询必须单飞且单调更新;新事件机会不得把 anchor 当作待细化目标;主题信号优先来自已验证事件领域,地点词不能单独代表迁居;Renderer 必须同时验证“另一件事件”、所选领域语义与安全边界。
  • 相关记录:BUG-090、BUG-093、BUG-094、BUG-095
  • 复发自:BUG-093、BUG-095
  • 修复版本:待本次 staging 修复提交与部署验收

BUG-098 | V6 公开候选门禁未明确 LODO、技法层级与独立 holdout 边界

  • 状态:resolved/partial
  • 首次发现:2026-07-30
  • 最近更新:2026-07-30
  • 影响面:V6 Python 候选扫描、Candidate Snapshot 公开范围门禁、生时纠正 Skill 与参考 Skill 能力声明
  • 用户现象:现有说明容易把“支持某技法”、同一 Case 内的留一诊断和参考 Skill 方法论误读为已完成独立验证;缺失 KP_cusps 或 D60 也可能被错误当成公开范围的统一硬阻塞。
  • 根因:文档没有把真实实现链、LODO public gate、active-domain required/optional/reference-only 技法策略和参考 Skill 的未实现能力分开;LOEO/LODO 使用同一事件贡献矩阵做事后减项,不能替代 prospective independent holdout。
  • 修复:记录 V6 的真实链路为跨午夜兼容的逐分钟 Python 扫描、事件贡献矩阵、Snapshot、LOEO/LODO、date sensitivity、neighbor stability 与 candidate split;公开范围新增 LODO 稳定性门禁,并按活跃可评分领域判定 required layerKP_cusps 为 optional、D60 为 reference-only、未知层失败关闭;事件 provenance 仅用于审计且不参与加权。
  • 验证:本工作树的聚焦合同覆盖跨午夜分钟枚举、LODO 低于 0.8 拒绝公开范围、required layer 缺失阻塞、KP_cusps/D60 缺失不阻塞,以及旧 Snapshot 兼容;本记录不把这些回归误写成独立 holdout 验证。
  • Partial / deferredper-Case independent holdout 因缺少 prospective sticky partition 与 calibration contract 延期。当前 LOEO/LODO 只证明同一 Case 内的敏感性,不得伪称 prospective、independent 或 calibrated validation 已完成。
  • 安全边界:继续禁止手工 supports/conflicts 伪评分、任意外部仓动态加载、唯一分钟结论和自动写入 profiles.active_birth_time;参考 Skill 只作方法与审计参照,不成为第二评分真源。
  • 防复发:公开候选必须同时通过事件/领域覆盖、范围宽度、邻近分钟、LOEO、LODO、日期敏感性、计算规格和 required-technique 门禁;任何 holdout 完成声明必须先有稳定分区持久化与校准验收证据。
  • 相关记录:BUG-082、BUG-090、BUG-093、BUG-095
  • 修复版本:birth-time-rectification-v6 / rectification-agent-v6-1,独立 holdout 延期

BUG-099 | 新事件追问可能预设事件存在、换词重复且 Renderer 回退不可追溯

  • 状态:resolved
  • 首次发现:2026-07-30
  • 最近更新:2026-07-30
  • 影响面:Semantic Question Opportunity 生成与排序、跨领域事件去重、Renderer 问题校验、持久化分析历史
  • 用户现象:新事件问题可能直接询问“哪次”经历而暗示该事件必然发生;提示缺少便于回忆但不限定答案的具体线索;与最新事件语义相同的内容可能换一个领域名称再次追问;Renderer 模型问题被接受、被拒绝或改由服务器 fallback 后,历史分析收据无法明确区分实际路径。
  • 触发条件:生成 ask_new_event 时仅依赖领域模板或缺失领域;跨领域候选与最新事件共享同一人物、行动或生活转折但没有语义重叠降权;Renderer 不可用、调用失败或问题未通过校验而进入 deterministic fallback。
  • 根因:问题政策尚未明确存在性询问、非穷举回忆线索数量和禁止虚构年龄/日期窗口;utility 合同没有钉死跨领域语义重叠 penalty;分析历史没有要求持久化 Renderer 校验结果与服务器 fallback 来源。
  • 修复要求:所有新事件问题先询问相关经历是否存在,不得预设发生;提供 2–5 个明确标注为示例而非穷举的回忆线索,并允许用户回答其他经历或表示没有;不得发明年龄、人生阶段或日期窗口。若候选与最新事件跨领域但语义重叠,必须在排序前降低 utility,足以判定为同一事件换词时不得再次提问。每次 Renderer 尝试必须在分析历史中留下安全的分类收据,区分模型问题通过校验、模型问题被拒绝,以及服务器 deterministic fallback,并记录不含原始 Prompt 或用户敏感文本的原因类别。
  • 验证:rectification-agent-v6.test.ts 覆盖存在性问题、具体回忆线索、退出方式、禁止虚构年龄/日期窗口和跨领域同事件降权;rectification-analysis-trace.test.ts 覆盖持久化分析历史中的模型安全校验与服务器 fallback 分类;完整前端测试 1123/1123 通过。
  • 安全边界:分析历史只记录阶段、校验结果、fallback 布尔值或安全原因类别,不记录模型 Prompt、原始候选文本、隐藏推理、内部评分、事件原文或出生资料。
  • 防复发:新事件问题和 Renderer 分析收据必须作为服务端合同测试,而不是只测试最终展示文案;领域名称不同不得绕过同一事件的语义去重。
  • 相关记录:BUG-087、BUG-095、BUG-097
  • 修复版本:birth-time-rectification-v6 / rectification-agent-v6-1

BUG-100 | Staging migration 账本中的遗失历史文件阻塞安全发布

  • 状态:resolved
  • 首次发现:2026-07-30
  • 最近更新:2026-07-30
  • 影响面:staging migration check、exact-SHA 应用发布
  • 用户现象:生时纠正 V6 修复已经通过质量门并合入 main,但 staging 在切换镜像前报 migration file missing: 20260727010000_admin_users.sql,因此仍运行旧版本。
  • 根因:staging 的 append-only migration 账本包含 5 条已不在任何受审仓库历史中的支付/管理 migration 记录;现有 runner 要求每一条账本记录都对应当前文件,因此按顺序安全停止。
  • 修复:在 migration runner 中加入 5 条静态 retired migration 记录,逐条固定校验从 staging 只读账本核对到的 SHA-256;只有文件名和 checksum 同时完全匹配时才允许继续。checksum 漂移、其他遗失 migration 或非法文件名仍然失败关闭;不删除账本记录,也不动态信任数据库返回值。
  • 验证:新增无数据库单元测试覆盖全部 5 条 retired migration 正确 checksum 通过、任一错误 checksum 拒绝、未声明遗失 migration 继续拒绝;staging 发布仍须通过原 migration check、exact digest 和 exact SHA 门禁。
  • 防复发:历史 migration 文件不得从受审仓库删除;若必须兼容已遗失记录,只能用代码审查过的静态 filename + checksum,并保留 fail-closed 测试。
  • 相关记录:BUG-099
  • 修复版本:staging migration integrity compatibility

BUG-101 | 正常访谈被 Opportunity 模板与 Renderer 回退主导,事件种类在 Python bridge 丢失

  • 状态:resolved
  • 首次发现:2026-07-30
  • 最近更新:2026-07-30
  • 影响面:V5 Agent 生时纠正常访谈、事件修订暂存、公开问题生成、Python 候选评分语义
  • 用户现象:模型虽然参与推理,但下一步焦点、问题类别和公开回复仍主要由服务器硬编码的 Builder、Opportunity 分类与正则 Renderer 决定;同一轮难以自然识别多件事件,关系开始、结束或变化进入 Python 评分后又退化为通用 relationship 领域。
  • 触发条件:正常 v5_agent 路径依次调用 buildQuestionOpportunities()runBoundedReasoner()renderPublicTurn();事件通过 TypeScript/Python bridge 时只传 domain,没有保留 canonical event_kind
  • 根因:Agent 只在服务器预先枚举的机会中选择,无法基于完整 Case Dossier 自主理解当前访谈焦点并生成下一句;公开回复失败时继续由领域正则模板接管。与此同时 Python legacy request 把领域值当作事件种类,抹平关系事件的 start/end/change 语义。
  • 修复:新增 Director 两阶段合同:服务器提供完整 Case DossierDirector 可在一轮提出多个 create/revise evidence proposal;服务器验证原文、日期、目标和 opaque ID 后生成 revisions 并重算评分/诊断,Director 再自主选择焦点并直接生成自然问题与公开回复。服务器继续控制 status、phase、snapshot/range gate、内部 ID、精确分钟与单问题安全边界;输出失败只允许同一 Director 做一次安全 repair,再进入通用 fallback。v5_shadow 保留 legacy 可见投影与确定性证据行为,只持久化 Director artifacts。Python bridge 与评分 trace 继续传递 canonical event_kind,并为 relationship start/end/change 保留可验证的最小区分。
  • 验证:TypeScript 相关套件 114/114 通过,其中 Director 专项 7/7TypeScript tsc --noEmit 与修改文件 ESLint 通过;Python tests/test_active_rectification_events.py 14/14 通过。
  • 安全边界:模型不得公开内部 ID、分数、贡献矩阵、工具信息或精确分钟;proposal 必须引用最新回答中的原文与日期文本,所有持久化 revision、候选重算、门禁判断和原子提交仍由服务器拥有。
  • 防复发:正常 Agent 路径不得重新依赖 Opportunity 枚举或领域正则决定访谈内容;Director 合同测试必须覆盖多事件提议、修订目标验证、拒绝/不知道后的换焦点、range gate、内部信息泄露与一次 repair;Python 测试必须断言 event_kind 从输入穿透到规则 trace。
  • 相关记录:BUG-095、BUG-097、BUG-099
  • 修复版本:staging

BUG-102 | Director Dossier 丢失早期语境、历史 Pending Evidence 与拒答领域

  • 状态:resolved
  • 首次发现:2026-07-30
  • 最近更新:2026-07-30
  • 影响面:V5 Agent Director 的长期会话理解、拒答保护、未解析证据续接与候选诊断循环
  • 用户现象:Director 已经替代正常路径的 Opportunity/Renderer,但超过十二轮的会话只收到“更早还有 N 轮”,declinedDomains 固定为空,历史未解决 Pending Evidence 未进入本轮 Dossier;一次只读诊断不足时无法继续观察后再决定下一问。
  • 触发条件:Case 超过十二轮、用户曾拒绝某个问题领域、前序 Turn 留下未解决证据,或 Director 连续需要两类候选诊断。
  • 根因:Dossier Builder 使用计数占位代替早期问答摘要,并未从 Turn 账本派生拒答领域;Job claim 合同也没有携带未解决 Pending Evidence。Director 的诊断处理使用单次 if,与 Dossier 声明的一次预算绑定。
  • 修复:Dossier 现在保留最近十二轮原文,并把更早问答压缩为有内容的受限摘要;从历史 Turn 派生拒答领域;Memory/Supabase Job claim 加载未解决 Pending Evidence 并与本轮新增项一并交给 Director。只读诊断改为最多两次的有界循环,公开回复、数据库状态、候选范围门禁与原子提交仍由服务器控制。Prompt 版本升级为 rectification-director-v2
  • 验证:Director 回归测试覆盖早期语境、拒答领域、Pending Evidence 和两次诊断循环;TypeScript 类型检查、相关 ESLint 与目标测试通过。
  • 安全边界:Pending Evidence 仅作为私有 Dossier 输入,公开文本仍经过内部信息、精确分钟、单问题和候选范围门禁校验;工具循环保持只读且最多两次。
  • 防复发:Dossier 测试必须断言早期语境不是计数占位、拒答领域和未解决证据可见;诊断测试必须断言循环有上限且最终返回非诊断动作。
  • 相关记录:BUG-099、BUG-101
  • 修复版本:local follow-up

BUG-103 | Director revise 可跨事件覆盖既有 Event ID

  • 状态:resolved
  • 首次发现:2026-07-30
  • 最近更新:2026-07-30
  • 影响面:V5 Agent 事件修订暂存、事件账本身份连续性与后续候选评分
  • 用户现象:模型可把“大学入学”的既有 Event ID 修订成“搬家”或其他无关事件,并把未受原文约束的 proposedSummary 写入账本。
  • 触发条件:revise proposal 引用真实 Target ID 和回答中的日期/Span,但声明了不同 Domain、Kind、Subject、Related Person,或 Span 与原事件语义锚点不连续。
  • 根因:服务器只验证 Target ID 存在、Span/日期来自最新回答,没有验证 Revision 的事件身份连续性;持久化 Summary 直接采用模型提议文本。
  • 修复:stageAgentEvidenceProposals() 仅接受 Domain、Kind、Subject、Related Person 与 Target 一致,且最新原文事件摘要仍命中 Target 摘要或原始文本的修订;不连续的提议转为 Pending Evidence,不覆盖原 Event ID。合法修订的 Summary 改用服务器从已验证 Source Span 提取的事件摘要,身份字段与 Scoreability 继续沿用 Target。
  • 验证:新增回归测试证明合法日期修订保留 Event ID 并使用原文摘要,跨领域且含虚构 Summary 的 revise 不产生 Revision、只产生 Pending Evidence。
  • 安全边界:Agent 仍可创建新事件;跨事件内容必须走 create,不能借 revise 篡改既有账本身份。
  • 防复发:Revision 测试必须同时覆盖合法日期更正和跨事件覆盖拒绝,不能只断言 Target ID 存在。
  • 相关记录:BUG-101、BUG-102
  • 修复版本:local follow-up

BUG-104 | Director 可覆盖拒答、日期简答失效、Pending 误关闭与旧候选快照越权

  • 状态:resolved
  • 首次发现:2026-07-30
  • 最近更新:2026-07-30
  • 影响面:V5 Agent Director/Orchestrator、事件 Revision、Pending Evidence 生命周期、Event Kind 评分边界、公开候选范围与回复安全校验
  • 用户现象:明确的“不想说/记不清/换一个”可能被模型覆盖回未解决并继续追问;仅回答月份、日期或时间段无法修订当前事件;自然纠正事件类型或人物会失败;历史 Pending Evidence 可能永久残留或被无关 Revision 错误关闭;旧 relationship_end 评分快照仍可能公开或接受;技术层名称可能出现在公开回复。
  • 根因:模型处置优先级高于服务器确定性关闭状态;日期 Revision 复用了创建事件的完整语义要求;修订合同没有区分日期修订与重新分类;Pending completion 没有原子关闭合同,也未按缺口类型验证修订;relationship_end 缺少独立评分规则却保留旧 scoreable Snapshot;公开文本过滤只覆盖部分技术层。
  • 修复:服务器关闭状态不可被 Director 覆盖,且关闭后禁止用空 Target ID 的澄清/冲突 Focus 重开原事件;Evidence operation 拆为 create/revise_date/reclassify/ignore,日期简答继承 Target 身份与缺失年份,显式纠正追加同 Event ID Revision,人物变化进入 pending_reviewV5 completion 增加 ownership/target/replay 安全的 Pending resolution,并且 date_unresolved 只在日期确实变化后关闭;relationship_end 强制 pending_review,迁移清除由旧 scoreable 关系结束事件支持的最新 SnapshotDossier 与 acceptRange 双重拒绝旧快照;公开回复禁止全部 D-number 技术层、KP、Vimshottari、Narayana、Shadbala 与 Ashtakavarga,只允许公开已批准 Snapshot 的首个范围 Cluster。
  • 验证:Director、V6 Agent、V4 Domain/Service/Migration 与 Python Event Engine 回归覆盖明确拒答、空 Target ID 绕过、日期简答、事件重新分类、Pending 原子关闭与原因匹配、旧快照失效、技术层泄漏、多事件提取和 Event Kind trace;真实 PostgreSQL migration 应用测试通过。
  • 安全边界:服务器继续拥有事实验证、事件身份、Scoreability、候选范围和持久化权限;Agent 只提出结构化计划。禁止公开内部 ID、评分、技术 trace、代表分钟或第二 Cluster,禁止自动写入 Profile 出生时间。
  • 防复发:拒答保护必须有 Orchestrator/Director 级回归;Pending resolution 必须验证缺口已被对应 Revision 补齐;评分政策变化必须同时处理历史 Snapshot;公开技术层过滤按完整技术命名空间测试。
  • 相关记录:BUG-099、BUG-102、BUG-103
  • 修复版本:birth-time-rectification-v6 / rectification-director-v2

BUG-105 | 候选差异未驱动下一问、Event Kind 未进入评分且不可回答 Case 被错误复用

  • 状态:resolved
  • 首次发现:2026-07-31
  • 最近更新:2026-07-31
  • 影响面:V6 Agent 问题排序、V5 Python 贡献矩阵、V4 Case 创建/复用、Staging 新建校正后的首次回答
  • 用户现象:诊断已经显示候选簇在特定技术层存在差异时,访谈仍可能继续做通用领域轮询;关系确立与关系变化在评分中缺少语义差异;新建校正后首次提交回答可能返回“当前没有待回答的问题,请刷新后重试。”;已知主体的非评分事件还可能被错误追问“发生在谁身上”。
  • 触发条件:候选分歧只携带技术层而没有可行动的缺失证据;评分仅按 Domain 汇总;创建 Case 时复用 paused、没有 Active Job 的 processing、或没有 currentQuestionawaiting_answer Casepending_review 被等同于主体不明确。
  • 根因:Director Dossier 缺少 Candidate Contrast Packet,问题排序对同领域历史提问施加通用重复惩罚;共享评分入口没有按 Event Kind 和真实命中 Rule ID 调整贡献;Service、Memory Store 与 Supabase RPC 的可恢复 Case 条件不一致且没有算法版本隔离;主体澄清条件错误地依赖整个 Scoreability 状态。
  • 修复:共享诊断层按主候选与次候选的静态特征差异计算区分层、按事件贡献差值计算相关事件,再生成不暴露候选分钟的 Candidate Contrast Packet,用 Cluster Rank、区分层、相关事件和缺失 Event Kind 驱动问题机会;候选驱动的不同 Event Kind 不受通用同领域重复惩罚,并优先于无诊断依据的领域轮询,非评分事件不作为 Candidate Split Target;共享 Python 贡献矩阵按真实 Rule ID 对 relationship_startrelationship_change 应用不同 Profile,零 Activation 不凭空加分,relationship_end 继续 Fail Closed;算法版本升级到 rectification-v5-matrix-scoring-2;三层 Case 复用统一为仅恢复可回答 Case 或拥有 Active Job 的 Processing Case,且必须匹配算法版本;主体澄清仅针对 subject=other
  • 数据库:新增向前迁移,更新新 Case 默认算法版本、保留 range-scoring v1 与 matrix-scoring v1 历史 Candidate Snapshot 解析兼容、废弃未完成的旧算法 Case、标记其活跃 Job 为 Stale,并重建带可恢复状态和算法版本检查的 Case 创建 RPC;不写入 Profile 出生时间。
  • 验证:真实用户回放覆盖复读、大学入学并离家、开始工作、分手和负债,断言关系结束保持 pending_review、不会把大学入学换词重问为迁居、D9 内部差异转为关系确立/状态变化的自然存在性问题、公开问题不泄露技术层或候选分钟,并允许“没有、不知道、不想回答、换方向”;Service 回归覆盖 Paused、孤儿 Processing、空问题 Awaiting Answer、新 Case 首次回答与旧算法 Case 替换;Python 回归覆盖关系 Event Kind 反转候选排序及零 Activation。
  • 安全边界:Candidate Contrast 只在服务器内部使用,不向用户公开技术层、分数、代表分钟或第二候选簇;Agent 不能把 relationship_end 变为可评分事件,不能确认精确出生分钟,也不能自动写入 Profile。
  • 防复发:完整回放测试必须同时覆盖事件账本、问题排序、退出方式和公开文本;评分版本变化必须同步 Case 复用、数据库默认值和历史快照兼容;Case 创建测试必须随后真实调用一次 Answer。
  • 相关记录:BUG-101、BUG-102、BUG-104
  • 修复版本:local follow-up / rectification-v5-matrix-scoring-2

BUG-106 | Director 缺少统一工具 Observation 与可更新 Dossier

  • 状态:resolved
  • 首次发现:2026-07-31
  • 最近更新:2026-07-31
  • 影响面:V8 生时纠正 Director Runtime、Skill 决策策略、Agent Run 诊断轨迹和未完成 Case 版本元数据
  • 用户现象:Director 虽然能够请求只读诊断,但 Case、候选扫描和证据缺口仍由固定流程一次性塞入 prompt;取得结果后最多再读两次诊断,Dossier 本身不随观察更新,前端处理中也只显示单行状态。
  • 触发条件:最终决策需要依次读取案件、候选差异、证据缺口和某项稳定性诊断才能决定下一问;或模型重复请求本轮已经读取过的静态工具。
  • 根因:Director 合同只有 request_diagnosticDossier 没有 revision、Observation 和候选假设;Runtime 只累计旁路诊断数组,前端没有把既有 Job phase 投影成稳定的分析步骤。
  • 修复:新增服务器拥有的 case_readcandidate_scanevidence_gapdiagnostic_read 统一只读工具;最终规划首轮只提供 Runtime 与工具可用性,不再预载这些工具拥有的完整数据。每次调用按需暴露当前权威服务器投影、生成结构化 Observation、递增 in-run Dossier revision,并把更新后的 Dossier 交回同一 Director。循环最多 10 轮,静态工具按工具+诊断去重,越界后只允许一次强制收敛;前端复用现有 Job phase 显示“整理事件 / 比较候选 / 确定验证方向”三步进度。Skill/Prompt 升级为 birth-time-rectification-v8 / rectification-director-v4,向前迁移只推进未完成的 Agent Case,不改历史完成结果、评分算法或 Profile 出生时间。
  • 验证:Director 回归覆盖 case_read → candidate_scan → evidence_gap → diagnostic_read → final、每轮 Dossier revision/Observation 回灌、重复工具不重复执行、一次强制收敛和单问题最终动作;前端回归覆盖三个公开分析步骤;V8 迁移合同覆盖默认版本、未完成 Agent Case 范围和禁止写入 active_birth_time
  • 安全边界:服务器继续拥有事件事实、工具结果、候选 Snapshot、公开范围门禁、持久化与最终输出验证;Observation 只存在于本次 Agent Run 的内存 Dossier,不成为第二业务真源,也不向前端公开原始结果、内部技术层、候选分钟或评分。
  • 防复发:不得把每次诊断后的 prompt 改回“必须立即结束”;增加新工具时必须定义 observation 是否会在同一 Turn 变化、重复调用策略和总轮次上限。
  • 相关记录:BUG-101、BUG-102、BUG-105
  • 修复版本:birth-time-rectification-v8 / rectification-director-v4

BUG-107 | 开放问题收集年份事件后先切换新事件、下一轮再回访旧事件

  • 状态:resolvedstaging pending deployment
  • 首次发现:2026-07-31
  • 最近更新:2026-07-31
  • 影响面:V8 Director 最终规划、确定性 fallback、年份/季度精度事件的访谈连续性
  • 用户现象:开放问题收到一件只有年份的本人事件后,系统先用固定句式要求另一件经历;收到第二件月份明确的经历后,又突然回头追问第一件事件的月份,表现为事件焦点来回跳转。
  • 触发条件:上一问没有 questionTargetEventId,本轮新建的可评分事件只有 yearquarter 精度,同时 Director 调用失败、超时或计划被拒绝而进入 fallback;即使 Agent 正常返回,旧校验也未禁止它在未闭合宽日期事件时切换目标。
  • 根因:Orchestrator 只把上一问的 questionTargetEventId 传入最终 Dossier,没有把本轮新建且日期仍宽的事件提升为当前目标;Director 因而看见 targetDisposition=not_applicable。确定性 fallback 随即输出通用“再说一件经历”模板,后续 Director 才从完整账本重新发现旧事件缺月份。最终计划校验也只保护拒答目标,没有保护 unresolved / answered_other_event 目标不被放弃。
  • 修复:最终规划优先保留服务器 reconciliation 的未回答目标;否则把本轮新建的 year / quarter 可评分事件设为 unresolved 当前目标。计划校验拒绝在该目标未闭合时切换到无关新事件;fallback 按目标真实日期精度追问月份或时间段,并绑定真实 Event ID,而不是继续输出通用新事件模板。
  • 验证:Director 回归断言未闭合宽日期目标不能被新事件问题放弃,强制 fallback 会锚定该事件并请求 monthOrchestrator 回放断言开放问题收到年份事件后,下一问立即补月份且 targetEventId 指向新事件。相关 Director/分析轨迹测试、TypeScript 和 ESLint 均通过;完整前端测试 1157 项通过。
  • 防复发:事件连续性由服务器的 currentTargetEventId + targetDisposition 约束,不能只靠 Agent prompt;新事件的必要日期精度应在切换话题前闭合,用户明确“不知道/跳过/换方向”时才解除目标。
  • 相关记录:BUG-092、BUG-097、BUG-106
  • 修复版本:local / pending release

BUG-108 | V8 丢失事件承接说明且服务器领域模板覆盖 Agent 自主选题

  • 状态:resolvedstaging pending deployment
  • 首次发现:2026-07-31
  • 最近更新:2026-07-31
  • 影响面:V8 Director 最终回复、Candidate Contrast、确定性 fallback、前端可见问题历史与处理中动画
  • 用户现象:用户提供具体经历后,界面只显示下一问,没有显示 Agent 对新线索的承接和公开安全的价值说明;后续问题又容易落入预写的教育、迁居、关系、事业、财务、健康模板,甚至把验收案例中的“大学、实习、搬家”等措辞当成产品脚本。处理中还显示固定三步 checklist,而不是随真实 phase 变化。
  • 触发条件:v5_agent 最终规划生成了 publicReply,但持久化出口只保存裸问题;同时 Opportunity Builder 通过 domainPolicy、关键词、人工 recallEase/privacyCost 和领域 fallbackPrompt 预先决定候选问题,Renderer 再用领域词表校验模型输出,模型异常时 Director fallback 直接采用人工排序结果。
  • 根因:公开消息和可见问题使用了两个出口;更关键的是服务器同时承担了事实约束和访谈选题,形成“Builder 人工选题 → Director 采用排名 → Renderer 关键词裁决”的双重控制,Agent 实际只能改写模板。
  • 修复:持久化完整的 acknowledgement、公开安全的证据价值说明、limitation 与唯一问题;删除固定领域 domainPolicy、领域 recall cues、领域关键词匹配和 V8 Opportunity 排名传参。Candidate Contrast 仅提供完整、顺序稳定的事实观察,Director 根据完整账本、拒答记录、候选差异和只读工具自主决定方向与措辞。无当前目标且模型失败时使用 domain:null 的领域中立恢复问题;有未闭合目标时服务器只保护目标连续性并询问必要事实。保留拒答/隐私保护、事实来源验证、单问题、范围门、目标锚点和技术信息过滤。前端移除固定分析 checklist,并把真实 Job phase 映射到 thinking-orbs 状态。
  • 验证:回归覆盖 Builder 不再生成六领域问题、Candidate Contrast 返回全部可用缺口而不替 Agent 选题、Renderer 接受不在测试样例中的自然方向、Agent 自主问题不被领域 fallback 替换、拒答领域不能被重新打开、大学与研究院实习回放在模型失败时保持领域中立。npx tsc --noEmit、98 项聚焦测试、1160 项完整前端测试、touched-file ESLint 与 git diff --check 均通过。
  • 防复发:服务器只提供事实、能力和禁止项;正常 V8 选题与措辞归 Agent。测试案例只能验证不变量和复现回归,不能通过词表、权重或预写问题进入生产决策链。
  • 相关记录:BUG-105、BUG-106、BUG-107
  • 修复版本:staging branch / deployment manifest records exact SHA

BUG-109 | 生时校正模型文案可越过公开事实边界

  • 状态:resolved
  • 首次发现:2026-07-31
  • 最近更新:2026-07-31
  • 影响面:V5 Director 公开回复、下一问生成与手动重新生成
  • 用户现象:模型可能直接追问、遗漏服务器承接说明,或把未经计算的候选优劣写入公开问题。
  • 触发条件:Director 返回自由文案,或历史 selectedOpportunity 进入问题重新生成路径。
  • 根因:公开回复和问题 wording 曾由模型直接提供;历史兼容分支还可绕过新 focus 合同调用 renderer。
  • 修复:Agent 只选择结构化 focus;服务器根据事件账本、能力矩阵和 focus 生成承接、方法说明及单一问题;历史 selectedOpportunity 先转换为 focus,所有重新生成路径共用同一服务器 renderer。
  • 验证:Director、analysis trace、V4 service 聚焦测试 52/52TypeScript、ESLint 通过;前端全量测试 1164/1164。
  • 防复发:回归测试覆盖模型伪造候选结论、手动 regenerate 以及历史 selectedOpportunity 持久化路径。
  • 相关记录:无
  • 复发自:无
  • 修复版本:待发布

BUG-110 | 月份简答未继承目标事件年份导致重复追问并暂停

  • 状态:resolvedlocal
  • 首次发现:2026-07-31
  • 最近更新:2026-07-31
  • 影响面:V5 生时校正的目标事件日期补充、Director fallback 与下一问生成
  • 用户现象:已有“2016 年离家去外地上大学”事件时,用户回答“9 月”后没有生成同一事件的新 revision;系统仍把目标视为 unresolved,重复月份问题,随后因 question_repeated 进入暂停。
  • 触发条件:当前问题绑定一个仅有年份或季度精度的事件,用户只回答月份、月日、半年或月份区间,且 Agent 不可用或返回重复的临时问题。
  • 根因:确定性 reconciliation 只接受答案中重新出现完整年份的日期;Evidence 阶段又提前校验临时公开问题,导致有效证据提议可能被重复问题校验一并拒绝。
  • 修复:复用目标事件已有年份补全局部日期回答,成功后追加同一 Event ID 的 revision 并关闭目标;Evidence 阶段只验证证据和 target disposition,公开问题只在 final 阶段验证;fallback reason 同时保留原始异常和二次拒绝原因。
  • 验证:回归覆盖“2016 年事件 + 9 月 + Agent 强制不可用”的完整 Orchestrator 流程,确认生成 2016-09 revision、无 pending、下一问不再绑定原事件且状态保持 awaiting_answerDirector 测试确认记录 fallback_rejected:question_repeated
  • 防复发:单元测试分别锁定确定性日期继承、Evidence 阶段边界、fallback 原因和完整两轮回放。
  • 相关记录:BUG-104、BUG-107、BUG-109
  • 复发自:无
  • 修复版本:local / pending release

BUG-111 | Director 校验器覆盖 Agent 公开回复且复合事件缺少公开语义

  • 状态:resolvedlocal
  • 首次发现:2026-07-31
  • 最近更新:2026-07-31
  • 影响面:V5 Director 公开承接、方法说明、下一问、手动重新生成与复合事件语义
  • 用户现象:Agent 已生成自然承接和下一问时,服务器仍会替换为固定模板;“离家去外地上大学”只暴露教育语义,公开解释无法同时说明教育与迁居层面。
  • 触发条件:最新事件同时包含多个可核对维度,或 Director / 手动 regenerate 返回合规的自然文案。
  • 根因:最终计划校验器同时承担验证和重写职责;事件账本只暴露单一主评分领域;手动 regenerate 路径完全忽略模型输出并直接调用服务器问题模板。
  • 修复:事件账本为同一事件增加只读 publicSignals,保留一个主评分身份并补充公开 secondary signals,不新增 Event、不重复计分;最终校验器只验证 grounding、技术边界、候选结论、隐私、单问题、重复问题和目标连续性,不再覆盖合规 Agent 文案;手动 regenerate 改为 Agent 生成、服务器两轮安全校验,不合规后才使用确定性 fallback。
  • 验证:回归覆盖“离家去外地上大学”只保留一条事件但公开 education + relocation、D24 + D4 合法 grounding、家人健康不投射为本人 D30、合规 Agent 文案原样保留、未 grounding 技法与候选结论被拒绝、手动 regenerate 的 repair 与 fallback。
  • 防复发:公开语义只解释用户原话中已存在的复合信号;服务器继续拥有事件身份、评分、事实 grounding 和安全门,正常措辞与提问归 Agent。
  • 相关记录:BUG-107、BUG-108、BUG-109、BUG-110
  • 复发自:BUG-109
  • 修复版本:local / pending release

BUG-112 | 活动旧 Case 静默阻止新版 Agentic 生时校正

  • 状态:resolvedstaging pending deployment
  • 首次发现:2026-08-01
  • 最近更新:2026-08-01
  • 影响面:生时校正前端入口、V4 面板、V4 Reasoner 模型选择与运行信息
  • 用户现象:账户存在未结束的旧 Case 时,页面始终进入 /api/rectification/v4/cases/*;没有切换新版 Agent 的入口,模型选择器也不影响本轮 Reasoner,fallback 原因与部署版本不可见。
  • 触发条件:打开生时校正时 loadActiveRectificationV4() 返回活动 Case。
  • 根因:入口用 existing ? "v4" : "agentic" 静默分流;UI 未调用已有 abandon()Reasoner 只读取 Case 固定模型;API 未返回最新 Agent Run 的安全运行摘要。
  • 修复:活动旧 Case 改为显式二选一;进入新版前先结束旧 Case;V4 面板增加同一切换操作;本轮 Turn 模型优先传给 Reasoner并记录实际模型;Case API 只公开最新运行的 mode、model、skill、deployment SHA 与 fallback code。
  • 验证:frontend/tests/conversational-rectification-component.test.tsfrontend/tests/rectification-v4-service.test.ts
  • 防复发:入口合同禁止恢复静默 V4 分流;服务测试锁定本轮模型优先级与 runtime trace。
  • 相关记录:BUG-085、BUG-086、BUG-111
  • 修复版本:local / staging pending deployment

BUG-113 | 新版 Agentic 生时校正进入会话后不自动生成首次引导

  • 状态:resolved
  • 首次发现:2026-08-02
  • 最近更新:2026-08-02
  • 影响面:生时校正首页入口、Agentic 对话首次挂载、首次可见引导
  • 用户现象:进入“生时校正”后只创建普通 birth_time_rectification Session,页面保持空白;网络中没有 POST /api/rectification/agent,必须由用户先输入内容才会触发 Agent。
  • 触发条件:账户没有需要继续的旧 V4 Case,入口直接选择新版 Agentic 生时校正。
  • 根因:Agentic MVP 只实现了用户提交消息后的 send(),没有迁移旧 V4 在页面挂载时自动启动首次 Agent Turn 的交互契约;后续入口改为默认进入 Agentic 后,这个遗漏被直接暴露。
  • 修复:Agentic 对话首次挂载时只发送一次隐藏的内部启动指令,复用现有 /api/rectification/agent 流式路径;界面立即显示 assistant thinking,首条可见说明与问题继续由 Agent 生成,内部指令不渲染为用户消息。
  • 验证:组件回归测试锁定一次性挂载启动、隐藏内部指令和 Agent endpoint 调用入口;前端测试与 lint 覆盖修改文件。
  • 防复发:任何替换生时校正入口或会话实现的改动,都必须保留“用户无需先发消息即可收到 Agent 首次引导”的挂载契约。
  • 相关记录:BUG-085、BUG-112
  • 修复版本:Agentic web opening auto-start

BUG-114 | Agentic 生时校正启动指令伪装成用户消息且未知时间被误判为资料缺失

  • 状态:resolved
  • 首次发现:2026-08-03
  • 最近更新:2026-08-03
  • 影响面:首页生时校正入口、/api/rectification/agent 首次启动合同、出生资料门、候选范围与确认写入安全门
  • 用户现象:进入生时校正后浏览器发送一段“用户刚进入生时校正会话……”的隐藏 message;服务端随后返回“出生日期、时间或出生地点资料不完整”。同时产品入口仍可能恢复或创建 V4 Case,与当前 Agentic 工具链并存。
  • 触发条件:用户从首页进入生时校正;资料使用合法的“只知道时段”或“完全不知道时间”声明,或客户端与服务端出生资料状态不同步。
  • 根因:BUG-113 用伪装成用户消息的字符串补上自动启动,没有建立服务端拥有的 opening operation;产品 wrapper 仍保留 V4 active-case 分流;Agentic profile loader 又把没有具体分钟一律当成缺失,因此合法的不确定时间无法进入最新流程。
  • 修复:产品 wrapper 只挂载 AgenticRectificationChat,不再调用或恢复 V4 Case;首次请求改为 action: "opening",服务端把 opening context 注入 Agent Turn,客户端不再发送或渲染隐藏用户指令;首页在创建 Session 前复用 onboarding 资料门,服务端以 profile_incomplete 明确回退;period_only 使用已声明时段,unknown 使用 00:0023:59,跨午夜范围保持原样;gate 返回服务端候选范围,score、diagnostics、features、confirm 拒绝 Agent 自行发明范围,全天宽范围 scan 延后而不伪造中午出生时间。
  • 验证:Agentic 入口、Session、工具、首页入口与组件合同测试覆盖无 V4 产品分流、opening operation、资料回退、时段/未知时间、跨午夜、宽范围降级、候选范围一致性和确认写入门;前端完整测试、lint、build 与 staging 真实 smoke 随本次发布执行。
  • 数据边界:仅废弃 V4 产品入口,历史 V4 代码与数据暂时保留,不在本次发布中做破坏性删除或迁移。
  • 防复发:自动首轮必须是服务端明确 operation,不得伪装成用户文本;“不知道具体分钟”是合法资料状态,不得等同于资料缺失;所有评分与确认工具只能使用服务端候选范围。
  • 相关记录:BUG-112、BUG-113
  • 复发自:BUG-113
  • 修复版本:待本次 staging 修复提交与部署验收

BUG-115 | ISO 出生日期被 Agentic 资料门误判并触发 opening 重试循环

  • 状态:resolvedlocal
  • 首次发现: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 和首页 readProfile() 都把持久化日期直接交给只接受纯 YYYY-MM-DD 的资料完整性校验;首轮只兼容了账户 JSON 中的 ISO 字符串,但 staging 自托管 PostgreSQL 数据层在 Agent loader 内实际返回 JavaScript Date,因此服务端仍误判 missing_birth_date。失败回调清空 rectificationSessionId 却未设置现有的自动恢复暂停状态,resume effect 又会立即重新挂载聊天并再次发送 opening。
  • 修复:共享持久化日期规范化函数统一接受合法 Date、ISO 字符串和纯日期字符串,由 Agent loader 与首页账户重新加载共同复用;服务端资料失败时先设置 rectificationError 暂停自动恢复,资料成功保存后再清除暂停状态。
  • 验证:staging 只读诊断确认目标账户的 birth_date 在服务端为 Date,时间、地点和误差字段类型均有效;回归覆盖 PostgreSQL Date、数据库 ISO 日期、非法日期,以及 profile failure 在清空 Session 前设置自动恢复暂停状态;完整前端测试 1200/1200、lint 0 error、production build 通过。
  • 防复发:数据库日期边界不得假设唯一 JavaScript 序列化形态;所有从账户持久化资料进入完整性校验的路径必须先走同一规范化函数;任何自动挂载请求的失败回调都必须先阻断对应的自动恢复条件。
  • 相关记录:BUG-016、BUG-114
  • 复发自:BUG-114
  • 修复版本:待本次 staging 修复提交与部署验收

BUG-116 | Agent 工具步骤耗尽后静默完成且校正对话刷新即丢失

  • 状态:resolved(local,空流修复已先部署)
  • 首次发现:2026-08-03
  • 最近更新:2026-08-03
  • 影响面:POST /api/rectification/agent、Agentic 生时校正消息持久化、首次 opening、余额显示与刷新恢复
  • 用户现象:提交新的人生事件后接口只返回 {"type":"done","emitted":false},页面没有 Agent 回复;刷新页面后此前校正对话全部消失,并再次自动发送 opening、再次预扣咨询点数;页面余额可能保持旧值,让一次请求看起来像多次扣费。
  • 触发条件:Agent 在默认步骤上限内连续调用 rectification-* 工具但没有剩余步骤生成公开文本;或 Agentic 校正组件卸载/刷新,而 chat_sessions.messages 仍为空。
  • 根因:Mastra 默认步骤上限不足,路由又把空 textStream 当作正常完成;新版组件只把消息保存在 React 本地状态,没有复用现有 chat_sessions 持久化边界,自动 opening 也只检查本次组件实例的 ref;新 Session 还可能在数据库创建完成前挂载 Agent;请求完成后没有刷新账户余额。
  • 修复:Agent 步骤上限提升为 8,解析后仍无可见文本时返回明确 error 并退款,不再发送 done false;请求绑定当前用户的 birth_time_rectification Session,成功回复先原子更新完整消息再发送 done 并完成扣费;已有持久化消息拒绝重复 opening;客户端从 Session 初始化、成功后同步首页状态并刷新一次账户余额,未收到持久化成功的 done 时移除临时 Assistant;新 Session 先创建成功再挂载 Agent,并按 Session key 重建本地对话状态。
  • 验证:frontend/tests/rectification-agentic-entry.test.ts 覆盖多步公开回复、空流退款合同、Session 归属与写回、刷新抑制 opening、失败流清理、新 Session 创建顺序;完整测试、lint、build 与 staging 真实刷新/扣费 smoke 随本次发布执行。
  • 数据边界:复用现有 chat_sessions.messages,不新增平行对话存储;不从用户粘贴内容擅自回填旧 Session;不修改身份、credits 历史或出生资料。
  • 防复发:公开回复必须同时满足“可见文本 + Session 持久化成功”才能发送完成事件;自动 opening 必须以服务端 Session 历史为准,不能只依赖组件内存。
  • 相关记录:BUG-113、BUG-114、BUG-115
  • 修复版本:空流修复 e65c8eeda2ff5916f88f18dd345c02beff045e8b / Session 持久化待本次 staging 发布

BUG-117 | 用户采纳最强候选后无法保存为平台排盘时间

  • 状态:resolvedlocal
  • 首次发现:2026-08-04
  • 最近更新:2026-08-04
  • 影响面:Agentic 生时校正候选结果、个人资料出生时间、后续咨询排盘时间
  • 用户现象:04:55 已是最强候选,用户多次明确表示“就用 04:55”,但 Agent 因唯一分钟确认门未通过而拒绝保存,个人资料和后续排盘仍未使用该时间。
  • 根因:系统把引擎候选、用户采纳和引擎唯一确认压缩成单一 confirmed 状态;没有可持久化的候选身份和用户采纳边界。
  • 修复:引入 candidate / accepted / confirmed 三态;服务端持久化候选身份、相对支持度、Session 所有权和 Profile 基线;新增 service-role 原子采纳 RPC;前端展示候选卡并允许用户采用;accepted 接入个人资料和全平台排盘。
  • 数据边界:相对支持度仅表示本次候选间的归一化比较,不是统计概率;保留 reported_birth_timeaccepted 不冒充引擎唯一确认。
  • 安全边界:RPC 校验用户、Session、结果身份、有效期、候选成员、最新结果和 Profile 基线;出生申报资料变化使旧候选失效;采纳不计费。
  • 验证:TypeScript 通过;聚焦测试 90/90;完整测试 1221/1221lint 0 error、3 个既有 warningproduction build 通过;本地 PostgreSQL 验证 04:55 写为 accepted、保留 05:00 reported time、重复采纳幂等,并验证出生申报时间变化会使结果失效且拒绝再次采纳。
  • 相关记录:BUG-113、BUG-114、BUG-115、BUG-116
  • 修复版本:待提交与发布

BUG-118 | 候选已落库但确认卡不显示,VedAstro 未执行被误述为未通过

  • 状态:resolvedlocalpending deployment
  • 首次发现:2026-08-04
  • 最近更新:2026-08-04
  • 影响面:Agentic 生时校正候选 SSE、self-hosted PostgreSQL 查询兼容层、确认门公开语义
  • 用户现象:rectification-confirm 已返回并持久化 selection_allowed=true 的候选时间,但页面只显示 Agent 文本,不显示候选确认卡;Agent 同时把 VedAstro 未执行、邻近分钟诊断和留一事件诊断混写成确认门未通过。
  • 根因:候选恢复查询调用 .gt("expires_at", now),而 staging 使用的 LocalPostgresQueryBuilder 未实现 gt,路由捕获读取异常后仍发送完成事件;外部验证本身只有在本地候选满足事件数、领域数、窄区间、唯一领先和必需层完整时才执行,missing_mandatory_layers 会使其保持 not_evaluated,并非 VedAstro 调用失败。邻近分钟与留一事件在 technique contract 中仅为诊断项。
  • 修复:在共享本地 PostgreSQL query builder 中实现参数化 gt 过滤;保留现有候选卡与 SSE 协议不另起状态;确认工具显式返回外部验证是否已调用、状态和原因,并要求 Agent 区分 not_evaluatedfail,不得把诊断项描述为硬阻塞。
  • 验证:真实 local PostgreSQL business client 回归覆盖未过期候选读取;Agentic 工具回归覆盖 not_evaluated 映射为 external_validation_invoked=falseSession、entry、candidate persistence 聚焦测试通过。
  • 防复发:self-hosted query builder 新增 Supabase/PostgREST 链式操作时必须由真实 PostgreSQL fixture 覆盖;公开文案必须按 external_engines.status 区分未执行、失败和通过。
  • 相关记录:BUG-116、BUG-117
  • 修复版本:待本次 staging 修复提交与部署验收

BUG-119 | 候选采用覆盖兼容出生时间且个人资料不显示双时间记录

  • 状态:resolvedlocalpending deployment
  • 首次发现:2026-08-04
  • 最近更新:2026-08-04
  • 影响面:Agentic 生时校正候选采用、个人资料出生时间展示、账户资料刷新、后续排盘时间
  • 用户现象:采用候选 05:06 后,账户虽然返回 active_birth_time=05:06,但个人资料仍只展示初始化填写的 05:00;数据库兼容字段 birth_time 同时被改成 05:06,导致“原始填报”与“校正采用”语义混在一起。
  • 根因:候选采用 RPC 主动把 active_birth_time 和兼容字段 birth_time 同时写为候选时间,旧 guard_birth_time_journey() 触发器还会双向镜像这两个字段;客户端 refreshAccount() 只刷新账户对象,没有同步个人资料展示使用的独立 profile state。
  • 修复:新增向前迁移解除 birth_time / active_birth_time 双向镜像,候选采用只写服务端拥有的 active_birth_time,并修复既有 Agentic 采用记录;保留 reported_birth_time 作为用户原始填报。账户刷新同步 profile 但不覆盖正在编辑的 draft;个人资料同时展示“当前排盘时间”和“原始填报时间”,候选卡明确采用边界并移除易被误解为概率的进度条。
  • 数据边界:reported_birth_time 是原始填报,active_birth_time 是平台当前排盘时间,birth_time 仅保留旧系统兼容用途;accepted 是用户采用,不等于引擎唯一确认。后续咨询继续读取 active_birth_time
  • 验证:聚焦账户、迁移、Agentic UI 合同测试 38/38;本地 PostgreSQL 完整迁移与业务测试通过,验证采用后 active_birth_time=04:55birth_time_status=acceptedreported_birth_time=05:00birth_time=null
  • 相关记录:BUG-117、BUG-118
  • 修复版本:待提交与发布

BUG-120 | Agent 仍在追问事件时过早显示候选采用卡且采用后无法改选

  • 状态:resolvedlocalpending deployment
  • 首次发现:2026-08-04
  • 最近更新:2026-08-04
  • 影响面:Agentic 生时校正确认门、候选卡展示时机、候选采用交互与数据库原子写入
  • 用户现象:Agent 回复仍在要求补充事件或确认日期时,页面已经显示三项候选并可立即采用;候选采用后所有选项被禁用,无法在同一批有效候选中改选。
  • 触发条件:确认工具取得至少三条事件、覆盖两个领域且返回候选,但引擎仍为 continue_rectification;或用户已经采用当前结果中的一个候选。
  • 根因:确认工具仅用事件数、领域数和候选存在性推导 selection_allowed,没有区分“继续收集证据”与“结束收集并邀请选择”;Agent 合同未禁止同轮追问和提供采用;前端与 RPC 又把首次采用误当成不可变终态。
  • 修复:rectification-confirm 新增显式 offer_selection,继续追问时必须为 false,仅在用户要求现在选择或本轮唯一下一步是选择候选时为 true;引擎真正通过唯一分钟确认门时仍自动允许确认。候选卡改为桌面端一行三列、移动端横向滚动,说明候选来自当前事件、可继续补充事件并重新计算;采用后保留其他候选可点击。向前迁移允许在候选结果仍有效且 Profile 基线未漂移时原子改选。
  • 数据边界:继续补事件不会把当前候选冒充最终结果;相对支持度不是统计概率;改选只更新 active_birth_time 和采用记录,不覆盖 reported_birth_time,也不写兼容字段 birth_time
  • 验证:Agent 工具与入口合同聚焦测试 37/37;真实本地 PostgreSQL 业务测试通过 04:55 -> 05:07 改选并保持 reported_birth_time=05:00birth_time=nullTypeScript、聚焦 ESLint 与 production build 通过;桌面端三列和移动端横向滚动截图已完成视觉检查。
  • 防复发:任何非唯一候选卡必须由显式选择阶段开启;同一 Agent 回复不得既索取新证据又提供采用操作;候选采用测试必须覆盖幂等、改选、过期结果和 Profile 基线漂移。
  • 相关记录:BUG-117、BUG-118、BUG-119
  • 修复版本:待提交与发布

BUG-121 | 月份与区间事件在确认工具中被序列化成错误日期格式并耗尽 Agent 步骤

  • 状态:resolved
  • 首次发现:2026-08-04
  • 最近更新:2026-08-04
  • 影响面:Agentic 生时校正结束收集、V5 评分/诊断、旧确认门适配、无回复退款兜底
  • 用户现象:用户明确表示不再补充事件后,接口返回“生时校正没有生成有效回复,本次不会扣除点数,请重新发送”,没有展示最终候选或后续选择。
  • 触发条件:历史证据同时包含 yearmonthrange 精度;Agent 在结束收集时调用 rectification-confirm。月份或年份事件被发送成完整日期,区间事件又可能使用 /to 等自然分隔形式。
  • 根因:共享事件 schema 只检查字符串长度;V5 转换只识别 .. 区间;toV3Event() 又把标准化后的 YYYY-MM-DDmonth/year 精度一起发送给只接受 YYYY-MM/YYYY 的旧确认端点。引擎持续返回 event date does not match its precision,Agent 在 8 个工具步骤内反复修正和重试,最终没有剩余步骤生成公开文本。该问题是 BUG-116 的输入契约残余变体,提高步骤数只能延后失败。
  • 修复:事件日期统一复用严格日历范围转换;V5 保留年月日和区间的 date_start/date_end,并兼容 ../to、中文范围符和紧凑年月范围;旧确认端点按精度发送严格的 YYYYYYYY-MMYYYY-MM-DD,区间按旧端点能力降级为年份证据且摘要仍保留原区间语义。工具 schema 同时明确推荐日期格式。
  • 验证:新增 V5 区间归一化和旧确认精度序列化回归;Agentic 工具/入口/会话合同测试 51/51,通过针对性 ESLint、tsc --noEmit 和 production webpack build。使用脱敏后的原始长对话本地重放,Agent 在 5 次工具调用内完成 gate -> score -> diagnostics -> confirm,工具错误 0,生成 610 字可见候选回复,不再触发空回复退款。
  • 防复发:任何送往旧事件引擎的日期必须由精度契约测试断言;新增日期表示必须先走共享日历校验,不能在调用端自行拼接或仅增加 Agent 重试步数。
  • 相关记录:BUG-116、BUG-118、BUG-120
  • 修复版本:本记录所在 staging 发布提交

BUG-122 | self-hosted staging 管理员看不到独立后台入口

  • 状态:superseded by BUG-123
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:self-hosted staging 账户菜单、GET /api/account、独立后台入口;不影响后台独立登录与 requireAdminSession
  • 用户现象:身份库已持久化 adminviewer 角色的用户登录主站后,账户菜单不显示后台入口;即使显示旧入口,主站 /admin 路径也会返回 404。
  • 触发条件:AUTH_PROVIDER=self-hosted,后台部署在与主站不同的 AUTH_ADMIN_ORIGIN,用户角色以逗号分隔形式持久化在 identity.users.role
  • 根因:主站 isAdminUser 对 self-hosted 模式直接返回 false,没有读取持久化角色;侧栏又把入口写死为主站相对路径 /admin/codes。既有后台鉴权已按持久化角色执行,但主站入口发现逻辑没有复用同一授权事实,独立域名部署合同也没有进入账户响应。
  • 修复:self-hosted 分支通过现有 ADMIN_DATABASE_URL 管理只读连接查询当前用户的 identity.users.role,仅 adminviewer 可见入口,且不使用 ADMIN_EMAILS 替代角色授权;GET /api/account 在服务端解析身份配置并返回 AUTH_ADMIN_ORIGIN + /admin/codesSupabase 模式继续返回 /admin/codes;账户与侧栏类型透传该 URL,并将文案改为“后台管理”。后台独立登录和 requireAdminSession 保持不变。
  • 验证:frontend/tests/admin-contracts.test.tsfrontend/tests/admin-users-contract.test.tsfrontend/tests/account-api.test.tsfrontend/tests/sidebar-contract.test.ts 锁定持久化角色、独立后台 URL、服务端环境边界和后台写权限门禁;目标 TypeScript、构建与 staging 登录态 smoke 结果另行记录。
  • 防复发:self-hosted 主站入口发现必须以 identity.users.role 为授权事实,不能退回邮箱 allowlist;客户端不得读取后台 origin 环境变量或硬编码主站 /admin 路径;后台 API 必须继续独立执行 requireAdminSession,入口可见性不得被当作授权。
  • 相关记录:BUG-010、BUG-083、BUG-084、BUG-123
  • 复发自:BUG-010
  • 修复版本:已由 BUG-123 的同域单会话架构取代

BUG-123 | self-hosted staging 双域后台与主站会话模型冲突

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-29
  • 影响面:staging Better Auth 配置、后台页面与 API、登录、账户入口、Caddy、部署校验和 smoke;生产配置不变。
  • 用户现象:管理员需要第二套后台域名和浏览器会话才能进入后台,主站登录态不能直接使用;viewer 还被当作后台只读角色,与仅数据库 admin 可进入的产品合同冲突。
  • 触发条件:self-hosted staging 同时配置用户与后台 origin/secret、Caddy 拆分两个站点,并按 Host 选择 Better Auth 实例。
  • 根因:早期隔离设计把后台浏览器 surface 当成第二套身份系统,导致入口发现、登录、Cookie、部署变量和授权策略重复;同时把入口可见性与 API 权限错误扩展到 viewer
  • 架构决策:后台复用主站 Better Auth user sessionidentity.users.role 的持久化 admin 是唯一后台授权事实。Better Auth 插件的 /api/auth/admin endpoint 继续在主站 fail-closed 404,未知 Host 继续 421
  • 修复:删除活动运行时后台 origin/secret 与 services.admin,服务端数据 client 和 requireAdminSession 统一读取 user session;后台 layout 增加服务端 gate,匿名转 /login、非 admin 不渲染;所有后台 API 保留独立 guardpayments/packages 改用 requireAdminSessionisAdminUser、账户入口和 Refine policy 收敛为 admin-only;登录取消 Host 分流;staging Caddy、Compose、环境校验、部署脚本、工作流和 smoke 收敛为同域。
  • 验证:身份 config/host/auth、admin policy/contracts、account/sidebar/login、部署/工作流与 admin layout/API guard 合同更新;针对性测试、TypeScript、Next build 与 git diff --check 结果记录在本次交付报告。生产部署未执行。
  • 防复发:活动运行配置和测试不得重新引入独立后台域名、AUTH_ADMIN_ORIGINBETTER_AUTH_ADMIN_SECRET 或浏览器 admin auth serviceviewer 对后台入口、页面、读 API 和写 API 均必须为 403;入口可见性不能替代 route guard。
  • 相关记录:BUG-010、BUG-083、BUG-084、BUG-122
  • 复发自:BUG-122
  • 修复版本:435e628806390e7ae138363491e7bae63ee801d4staging 已验收

BUG-124 | 后台支付入口分散且界面风格不一致

  • 状态:resolved
  • 首次发现:2026-07-29
  • 最近更新:2026-07-30
  • 影响面:后台 Refine 侧栏、/admin/payments/admin/packages、易支付配置与对话页充值入口。
  • 用户现象:支付记录与支付配置占用两个导航项,页面仍使用主站 standalone-page/admin-header/admin-section 样式;套餐新增表单常驻页面,后台默认退出入口还会触发登出,管理员难以直接返回对话;对话页支付入口缺少安全默认关闭和服务端创建订单硬门禁。2026-07-29 复发时,Z-Pay 配置不能折叠且占据长页面,后台受全局 html/body overflow:hidden 限制无法纵向滚动,套餐 API 与易支付配置 API 仍调用 self-hosted adapter 不支持的 Supabase builder/RPC。2026-07-30 部署 dd8e2ad9c7e76d0152b4563c43a45b1e26137035 后,GET /api/admin/payments 与套餐管理仍返回 500。
  • 触发条件:进入同域 /admin 后管理支付记录或套餐,或点击 Refine 侧栏底部默认 Logout;复发条件为进入支付管理、展开长配置或调用套餐 CRUD / 易支付配置读写。2026-07-30 的数据库权限复发在 admin_runtime 通过 ADMIN_DATABASE_URL 查询支付表时稳定触发。
  • 根因:首轮支付后台实现依赖 Supabase 专用关联 select、分页、计数和 Admin Auth 查询;self-hosted staging 的本地 PostgreSQL adapter 不支持这些 builder 能力,支付记录因此统一降级为“支付记录服务暂时不可用”。同页套餐设计也不符合最新后台信息架构,易支付配置响应漏投影 chat_enabled,chat 创建订单又依赖服务端提交网关后猜测跳转地址,不兼容标准易支付收银台表单页。复发遗漏源于上轮只把支付记录切换到 PostgreSQL,套餐与配置契约测试没有锁定 self-hosted 数据链,且未覆盖聊天全局滚动边界下的后台专用滚动容器。2026-07-30 的直接根因是 20260727020000_epay_packages_orders.sql 只向 Supabase 的 service_role / authenticated 授权,未向 self-hosted 后台实际使用的 admin_runtime 授予 payment_packagespayment_orders 权限,也未添加对应 RLS 策略;因此数据库健康且新 SHA 已部署,后台 SQL 仍被 PostgreSQL权限门禁拒绝。同日还确认 staging 迁移与部署工作流错误地要求目标 SHA 属于 main 历史,使完全独立的测试分支被生产分支阻塞;该控制面耦合导致为恢复 staging 而误合并生产 main。
  • 修复:支付记录改为通过 queryAdminRows 执行参数化 SQL,联表 public.payment_orderspublic.payment_packagesidentity.users,以窗口计数保留分页合同并用独立聚合 SQL输出统计;不再使用 Supabase builder 或 Admin Auth。后台在支付管理之后新增独立“套餐管理”资源和页面,套餐新增、编辑、停用、错误重试及原字段保持完整,支付页只保留概览、Z-Pay(易支付)渠道配置和支付记录。配置读取补回 chat_enabledchatEnabled。创建订单完成登录、开关、配置、SSRF、套餐和订单校验后,直接返回带 sign/sign_type 的标准 submit.php 收银台 URL,不服务端请求网关、不返回商户密钥;对话页用浏览器打开该 URL,套餐加载异常显示安全错误,正常 enabled=false 仍静默隐藏。复发修复将 Z-Pay 配置改为默认收起的 Ant Design Collapse,展开后才显示表单和操作;为 AdminApp 增加 admin-app-shell100dvh 独立纵向滚动边界而不改聊天全局规则;套餐 CRUD 全部改用 queryAdminRows 参数化 SQL、UUID 校验、returning 与 404;易支付读取仅在 PostgreSQL 42P01 时回退环境变量,保存直接参数化调用 public.admin_save_epay_settings 并使用函数返回行,保留原子审计和脱敏响应。2026-07-30 新增前向迁移 20260730010000_admin_payment_permissions.sql,向 admin_runtime 最小授予套餐读写、订单只读、易支付配置读取及保存函数执行权限,并为启用 RLS 的支付表补齐角色策略;不授予订单写入或删除权限。Gitea 与 GitHub 的 staging 迁移、部署和测试环境运维工作流统一 checkout staging,删除 staging SHA 属于 main 历史的要求;生产工作流保持不变。误合入 main 的 PR #1 已由 PR #2 的 revert 恢复,恢复后 main 内容树与合并前提交 43581ac0f75e7f157032503475e878bd53ad161d 完全一致。针对 run 1309,Gitea 两条远端工作流的 previous-SHA 探测与 registry login/logout 保持 sudo -n docker;脚本只接受受控的 dockersudo -n docker 数组分支并拒绝其他值,不使用 eval。run 1313 证明 sudo Docker login 已成功,但 run-staging-migration.sh 第 37 行无法写入 root-owned 部署树下的 /opt/jyotisha-staging/.state/mutation.lock。因此迁移与部署工作流改为通过 sudo -n env 传入受控环境并以 root 启动整个脚本,脚本内固定 DOCKER_BIN=dockerDOCKER_CONFIG 仍指向 incoming 的 .dockercleanup 使用 sudo -n rm -rf 删除脚本可能创建的 root-owned incoming 内容,Docker logout 仍使用 sudo。
  • 验证:frontend/tests/admin-contracts.test.ts 锁定支付、套餐资源顺序;frontend/tests/admin-payments-contract.test.ts 锁定本地参数化 SQL、identity.users 联表、套餐 SQL CRUD/UUID/404、独立套餐页面、默认折叠和后台专用滚动容器;frontend/tests/epay-settings.test.ts 锁定 chatEnabled 回显、queryAdminRows 读取、参数化 admin_save_epay_settings、不依赖 Supabase builder/RPC、默认折叠和不泄露 key。2026-07-29 运行三份契约测试共 27 项全部通过;ESLint、TypeScript 与 git diff --check 结果记录在本次交付报告。2026-07-30 线上健康响应证明部署 SHA 为 dd8e2ad9c7e76d0152b4563c43a45b1e26137035 且本地业务库、身份库均健康;静态权限审计确认支付迁移缺少 admin_runtime grant/RLS。新增权限迁移契约后,支付、套餐、配置三组 21 项回归全部通过。首次独立 staging 迁移 run 1300 在镜像校验阶段暴露 docker manifest inspect --verbose 对 ACR 返回单元素数组,而解析器只接受对象,触发 AttributeError: 'list' object has no attribute 'get'Gitea staging 迁移与部署已兼容单平台数组并增加聚焦契约测试。run 1309 进一步确认远端 deploy 用户对 /var/run/docker.sock 无权限;run 1313 的 sudo Docker login 已成功,随后在迁移脚本第 37 行因 deploy 用户不能写 root-owned /opt/jyotisha-staging/.state/mutation.lock 而终止,证明仅提升 Docker 命令不足以覆盖部署树写入。工作流回归现锁定整个脚本由 sudo -n env 启动、脚本内 DOCKER_BIN=docker、incoming Docker 配置不变、root-owned cleanup 使用 sudo,并继续保留 runner 对两种固定 Docker 命令形式的契约。staging 最终部署 SHA 为 1f44892a2cf210797e7dc74f49721a8f10c8849d;迁移台账确认 20260730010000_admin_payment_permissions.sql 于 2026-07-30 05:58:38 UTC 应用,数据库 ACL/RLS 与 admin_save_epay_settingsadmin_runtime 执行权限均已生效,web/api/postgres 容器健康,三个未登录管理 API 正确返回 401,部署后日志无相关 500。支付、套餐与易支付配置 21 项针对性回归通过,管理员随后确认 /admin/payments/admin/packages 已恢复。
  • 防复发:self-hosted staging 后台查询不得依赖 LocalPostgresDataClient 未实现的 Supabase builder、RPC 或 Admin Auth 能力;支付与套餐必须保持独立资源顺序。套餐与易支付配置契约必须显式拒绝 Supabase builder/RPC 并锁定参数化 SQL、404、原子函数写入和安全错误响应;支付配置必须默认折叠,后台必须拥有独立滚动容器且不得放宽聊天的全局 overflow:hidden。易支付配置读写测试必须同时覆盖数据库列和公开字段;创建订单只生成经公网 SSRF 校验的签名收银台 URL,商户密钥只能参与服务端签名,不得进入 URL、响应、日志或审计。对话支付默认关闭,UI 与创建订单 API 必须共享服务端开关;可用性测试不得提交伪订单或返回 URL、PID、密钥、headers/body。
  • 相关记录:BUG-122、BUG-123
  • 修复版本:d44a414(权限迁移),staging 部署 1f44892a2cf210797e7dc74f49721a8f10c8849d

BUG-125 | 个人报告入口对不可用出生时间状态错误开放

  • 状态:resolvedlocalpending staging deployment
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:首页个人报告 CTA、POST /api/reports 出生时间门槛
  • 用户现象:资料流程已经完成、但出生时间仍为 reportedcandidate 的用户会看到“生成个人报告”,点击后服务端必然返回 422 birth_time_not_usable
  • 触发条件:用户有咨询会话和消息,profileComplete=true,但当前排盘时间尚未被用户采用或引擎确认。
  • 根因:首页只用资料完整度判断入口可见性,没有镜像报告 API 的 accepted/confirmed + 有效 active time 门槛;UI 与服务端各自正确但组合后形成误导入口。
  • 修复:首页复用既有 isBirthTimeReadyForConsultation(profile),只有 acceptedconfirmed 且当前排盘时间有效时才显示个人报告入口;服务端门槛保持不变,不把候选范围或填报时间伪装成已采用时间。
  • 验证:frontend/tests/personal-report-entry.test.ts 15/15 通过,新增回归直接覆盖 reported=falsecandidate=falseaccepted=trueconfirmed=true 及缺失 active time 为 false;目标 TypeScript、ESLint 和 git diff --check 通过。
  • 防复发:任何报告出生时间状态扩展必须同时更新服务端事实门槛和客户端可见性测试;客户端不得仅以资料表单完成度推导报告可生成。
  • 相关记录:BUG-117、BUG-119
  • 修复版本:本次个人报告 staging 发布提交

BUG-126 | 正式报告页进入能力审计后质量门禁仍断言旧路由集合

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:tests/test_api_server_security.py、Gitea Staging Backend Quality Gate、正式报告页面可发现性审计
  • 用户现象:PR quality gate run 1453 中 290 项 Python 检查通过,但 test_capability_audit_scans_registry_and_local_sources 因扫描结果新增 reports/[reportId] 而失败。
  • 触发条件:新增 frontend/src/app/reports/[reportId]/page.tsx 后运行 API 安全 quick quality gate。
  • 根因:能力审计会动态扫描前端页面,新增报告 reader 被正确识别;精确路由集合测试仍锁定新增前的六个页面,且本地个人报告聚焦矩阵没有包含该跨层能力审计测试。这是 BUG-014 的同类契约更新遗漏。
  • 修复:将 reports/[reportId] 明确纳入能力审计预期路由集合,不隐藏或排除真实产品入口;将该测试纳入本轮修复后的本地和远端门禁复验。
  • 验证:tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources 本地聚焦通过;Gitea quality gate run 1459 在完整 runner 中通过,Python quick gate 291 passed / 1 skipped。
  • 防复发:新增或删除 Next.js 页面时必须运行能力审计安全测试;报告前端验收矩阵增加跨层 _scan_app_routes 契约,不能只运行 frontend/tests/personal-report-*
  • 相关记录:BUG-014、BUG-125
  • 复发自:BUG-014
  • 修复版本:本次个人报告 staging 发布提交

BUG-127 | 个人报告迁移成功后 self-hosted 精确表清单仍是旧值

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:frontend/tests/database-local-business.test.ts、self-hosted PostgreSQL 全迁移验收、Gitea Staging Backend Quality Gate
  • 用户现象:quality gate run 1456 的 1409 项 frontend 测试中 1408 项通过;真实 PostgreSQL fixture 成功创建 public.personal_reports 后,精确表集合断言因预期值缺少该表而失败。
  • 触发条件:在完整 Docker/PostgreSQL runner 中应用全部迁移并枚举 public schema 表。
  • 根因:个人报告迁移契约覆盖了双迁移语义、RLS、权限和版本唯一性,但既有 self-hosted 全库精确表清单没有同步新增 personal_reports;本机缺少 Docker CLI,无法执行该 fixture,问题由远端完整 runner 捕获。
  • 修复:在 self-hosted 全迁移测试中显式断言 20260806000000_personal_reports.sql 被应用,并将 personal_reports 按字典序加入精确表清单;不删除真实表、不放宽集合比较。
  • 验证:静态 personal-report migration tests 9/9 和迁移版本测试通过;Gitea quality gate run 1459 的真实 PostgreSQL fixture 与完整 frontend suite 通过,frontend 1409/1409。
  • 防复发:新增 self-hosted 业务表时必须同时更新全迁移 applied ledger 和精确 public 表集合;本地没有 Docker 时必须依赖并等待完整远端数据库门禁,不能仅凭迁移文本测试宣称数据库全绿。
  • 相关记录:BUG-126
  • 复发自:无
  • 修复版本:本次个人报告 staging 发布提交

BUG-128 | staging deploy 泄漏多行 SSH secret 且 env owner 契约互相冲突

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:Gitea/GitHub staging deploy 与 migration workflow、staging SSH 凭据、.env.staging* owner、加密备份和发布门禁;production 未受影响。
  • 用户现象:exact-SHA 自动 deploy run 1464 在应用切换前失败;Gitea job 日志把多行 staging SSH 私钥逐行显示,同时远端数据库 env validator 报 owner 不匹配。公网仍运行旧 SHA。
  • 触发条件:Gitea workflow 将多行 OpenSSH key 直接放入 step envroot 控制脚本验证一个由 deploy 持有的 mode-0600 env;此前 root rollout 临时文件又通过 mv 把 env owner 改成 root。
  • 根因:Gitea runner 不能可靠遮蔽多行 secret 的每一行;控制面同时混用了“当前脚本用户”和“部署树 owner”作为 env ownership 事实,rollout 覆盖文件时未保留原 owner/gid。
  • 修复:立即停止发布,生成并验证新 staging ED25519 key,精确撤销旧 authorized key,证明旧 key 无法登录,删除本地旧 key,更新 Gitea/GitHub staging secrets,并删除 28 个可能含旧 key 的 Gitea deploy/migration runs。STAGING_SSH_PRIVATE_KEY 改为单行 base64;所有 staging workflow 解码到 0600 临时文件并用 ssh-keygen 验证。deploy/migration 以部署树 UID 校验两个 envbackup helper 继续以 deploy 运行;rollout 临时文件显式保留部署树 owner/gid。
  • 验证:新 key 严格主机校验登录成功,旧 key 登录失败;新 Gitea/GitHub secrets 已更新;泄漏 run 1464 已删除;本地 workflow contracts 31/31、personal-report 142/142、owner regression、shell/YAML、TypeScript、ESLint、governance 和 pre-work 通过;Gitea quality gate run 1465 在完整 Docker/PostgreSQL runner 中成功。新 exact-SHA migration/deploy 仍按发布流程单独验收。
  • 防复发:禁止 staging workflow 直接注入多行私钥或打印 decoded secret 变量;env owner 必须由部署树身份决定,root 受控脚本不得用 root 临时文件改变持久 env owner。任何凭据日志暴露先轮换/撤销/清理,再修代码和重跑。
  • 相关记录:BUG-124、BUG-127、ERR-092、ERR-093、ERR-094
  • 复发自:无
  • 修复版本:f7a615a5bf11ed95b3a6c7e6d28dfe8150a825efstaging migration/deploy 与安全验收完成

BUG-129 | staging trusted-main checkout 无界 fetch 导致自动部署长期占用 mutation queue

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:Gitea staging deploy/migration 控制器的 trusted-main checkoutproduction 与 staging 应用数据面未受影响。
  • 用户现象:exact-SHA quality gate run 1473 成功后,自动 deploy run 1474git fetch --no-tags origin main "$DEPLOY_SHA" 长时间没有日志进展;fetch 后续自行恢复,run 最终于 18 分钟成功部署 02cc483b7c303e6cc0f26fb31462c50adb007f12。第一轮 bounded-retry 修复合入后,run 1480 的 3 次 120 秒 fetch 全部在服务端压缩 16,093 个对象时耗尽并 fail closedSSH/远端 mutation 未开始,公网/state 继续健康运行 02cc483b7c303e6cc0f26fb31462c50adb007f12
  • 触发条件:空仓库命令 git fetch --no-tags origin main "$DEPLOY_SHA" 同时请求分支和目标 SHA,导致 Gitea 为每次尝试枚举/压缩完整历史对象;runner 与服务端之间的传输无法在 120 秒内完成。
  • 根因:原控制器既没有命令级 timeout,也错误地为正常前向发布抓取 full-history dual ref。第一轮修复只增加 bounded retry,解决了无界占用,但旧回归测试只断言 timeout/attempt/ancestry,未限制传输对象范围,因而未拦住连续三次重新打包完整历史。
  • 修复:不再让 mutation runner 做任何 Git object fetch。成功 staging gate 从其已验证的 exact SHA 生成仅含 tracked deploy/ 与严格 manifest validator 的 controller.tar,将 tar SHA-256 写入四字段 manifest,并与 immutable image digests 一起上传。deploy/migration 从 exact successful gate artifact 下载 bundle,强制校验 controller SHA、tar hash、路径、重复项、类型和 2 MiB 上限后才解包;正常发布使用当前 main == staging controller,手工旧版 rollback 也不得执行旧 controller。refs 与 forward/rollback 关系通过有界 Gitea API 和完整 commit-DAG 路径证明,字段缺失、分页不完整、头不一致或证据冲突均 fail closed。
  • 验证:第一轮 bounded retry 的本地 workflow contracts 31/31、PR gates 1475/1477 与 staging gate 1479 成功;run 1480 证明 3 次 120 秒耗尽后无半部署。bundle 修复本地 manifest/workflow contracts 35/35、三份 YAML、解析后所有 shell/Python heredoc、真实 25-entry/122,880-byte controller tar hash/ZIP+TAR 安全检查、mutation Git-object-op=0、live Gitea commit-DAG、mandatory pre-work、ESLint、privacy 和 diff 检查通过;完整 PR gate 1481、staging gate 1483 成功。自动 deploy 1484 在 1 分钟内成功部署 e59f15d352787f3d05425ba8c459d092e9801a20,日志 mutation git fetch=0、controller hash check 存在、SSH secret 遮蔽且无私钥材料;main/staging/public/state 精确一致,5 个容器 restart count 均为 0health、Swiss Ephemeris、未登录 401、personal_reports、RLS、2 条 owner policies、精确 migration ledger、authenticated SELECT/DELETE-only 与 service-role CRUD 均通过。
  • 防复发:所有 release-controller 网络调用必须有命令级上限和失败闭合;mutation workflow 禁止 git fetch/ls-remote/cat-file/merge-base/checkout/init。控制器必须来自 exact successful gate 的 hash-bound artifact,正常与 rollback 均使用当前 reviewed controller;测试必须覆盖 artifact identity、tar safety、commit-DAG proof 和旧 Git object 路径为零。
  • 相关记录:BUG-128、ERR-094、ERR-095
  • 复发自:BUG-129 第一轮修复未覆盖对象范围
  • 修复版本:e59f15d352787f3d05425ba8c459d092e9801a20gate-attested controller bundle 已完成 staging exact-SHA 验收

BUG-130 | self-hosted 计费查询与订单领域调整缺少并发和审计边界

  • 状态:resolvedlocal
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:self-hosted LocalPostgresDataClient、支付商品与订单创建、账户会员查询、生时校正 reservation 查询、订单后台、兑换码后台及 20260806030000_settle_order_usage_authorization.sql
  • 用户现象:self-hosted runtime 无法执行 nested PostgREST select,部分时间和前缀筛选也缺少参数化 builder;一次性商品并发支付可能重复发放权益;订单后台只有读取能力,失败权益、人工补偿和账务退款缺少受控领域动作;兑换码写操作无法强制记录操作原因。
  • 触发条件:通过本地 PostgreSQL adapter 查询商品及权益、读取有效会员或 reservation 前缀;同一用户并发结算任意 oneTimePerUser 商品;管理员重试失败发放、人工补偿、登记线下退款,或创建、修改、撤销兑换码。
  • 根因:支付调用方依赖 self-hosted adapter 不支持的关联 selectadapter 又缺少 lte/like 参数化能力;一次性限制没有由不可变订单快照和数据库唯一约束共同承担;订单状态和余额缺少统一的管理员领域函数、幂等请求、乐观版本及审计合同;旧兑换码 RPC 不接收 reason。
  • 修复:商品及权益改为两次简单查询后在服务端组装,adapter 增加参数化 lte/like;订单创建写入不可变 oneTimePerUser 商品快照,结算与人工补偿统一通过 (user_id, product_code) 唯一兑换记录原子阻止重复发放;新增 admin_adjust_order,只允许 retry_grantcompensaterecord_refund,强制 billing.adjustments.write、二次认证、reason、request ID 幂等、expected version 和审计,账务退款明确不调用外部支付网关;兑换码 create/update/revoke 新 RPC 均强制 reason,旧无 reason 签名撤销 runtime 执行权限。未直接修改余额或绕过领域函数改订单状态。
  • 验证:真实 PostgreSQL tests/database-billing-adjustments.test.ts 通过,覆盖一次性商品并发仅一次成功、快照不可变、重试/补偿/账务退款、权限、reason、幂等、版本冲突、审计及旧 RPC 权限撤销;综合 tests/database-billing-admin.test.tstests/database-local-business.test.ts 通过;计费 route/reauth/contract 测试 38/38 通过;目标 ESLint 与补丁检查通过。全仓 TypeScript 当前被共享工作树中非本任务的 tests/identity-auth-integration.test.ts:412 类型错误阻断。
  • 防复发:self-hosted 支付查询不得重新引入 nested PostgREST selectLIKE/范围条件必须参数化;一次性权益必须同时依赖不可变快照和数据库唯一约束;订单与兑换码后台不得直接更新余额或订单状态,所有写入必须经过带 reason、权限、幂等和审计的领域 RPC。
  • 相关记录:BUG-124
  • 复发自:BUG-124
  • 修复版本:待提交(本地可测)

BUG-131 | staging quality gate exact-SHA checkout 因过严低速阈值单次失败

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:Gitea Staging Backend Quality Gate validate/publish 的 exact-SHA checkoutstaging mutation controller、应用数据面与 production 未受影响。
  • 用户现象:docs-attestation staging gate run 1485 在 validate 的首步失败;Gitea 已枚举/压缩 3,246/2,895 个 shallow objects,但客户端传输降速后触发 curl 28 Operation too slowearly EOF。publish 被依赖关系跳过,自动 deploy 未触发;公网继续健康运行 e59f15d352787f3d05425ba8c459d092e9801a20
  • 触发条件:quality gate 的 exact-SHA --depth=1 fetch 只有单次调用,并把低速失败设为连续 30 秒低于 1024 B/s;当前 Gitea 链路在约 20 KiB/s 波动后短时低于阈值。
  • 根因:BUG-129 消除了 mutation workflow 的 Git object fetch,但 quality gate 自身仍必须取得待测源码;其 checkout 没有 bounded retry,且低速阈值对当前受限链路过严。旧测试只断言 exact SHA/clean tree,没有覆盖 checkout retry 与低速边界。
  • 修复:validate/publish 两处 exact-SHA checkout 均改为最多 3 次、每次 hard timeout 300 秒;保留 connect timeout 15 秒,将低速失败收紧为连续 60 秒低于 1 B/s。每次仍只抓 --depth=1 --no-tags origin "$GITEA_SHA",耗尽后明确 fail closed,不复用旧 artifact、不放宽 exact-SHA 或 clean-tree 校验。
  • 验证:过期基线 PR gate 1488、最新 controller PR gates 1519/1523、staging push gate 1525 均成功;最终 gate 对 exact SHA 6c1dcbe857006ec6ae7463b57b2b7d5947da4851 完成 validate/publishartifact ID 12 成功上传,deploy 1526 成功。
  • 防复发:质量门禁和 mutation controller 的网络边界分别测试;quality gate checkout 必须覆盖 attempt 数、hard timeout、低速阈值、exact-SHA refspec、最终错误和 clean-tree identity。
  • 相关记录:BUG-129、ERR-095、ERR-096
  • 复发自:无;属于同一 Gitea 链路在 quality-gate 阶段的独立缺口
  • 修复版本:6c1dcbe857006ec6ae7463b57b2b7d5947da4851(最终 staging 验收)

BUG-132 | 新增后台页面未同步能力审计精确路由集合

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources、Gitea staging quality gate;新增后台页面实现本身未由本记录改动。
  • 用户现象:并发主线 staging gate run 1489 的 Python quick gate 为 290 passed、1 skipped、1 failed;能力审计已扫描到 14 个新增后台页面,但测试仍精确断言旧的 7 路由集合,publish 被跳过,自动 deploy 未触发。
  • 触发条件:新增 administrators、audit logs、consultations、credit transactions、customers、feature flags、model releases、models、orders、products、roles、security、subscriptions、usage 页面后执行 capability audit 精确集合回归。
  • 根因:并发后台功能更新了真实 App Router 页面,却未同步能力审计的完整预期集合;这是 BUG-126 同类防复发模式在后台模块复发,说明新增页面的同变更门禁仍未统一执行。
  • 修复:保留精确集合比较,将实际新增的 14 个后台页面按排序加入预期列表;不删除既有个人报告页,不改成子集或数量下限,不修改并发后台业务实现。
  • 验证:本地 staging controller/contracts 35/35、完整 Gitea PR gates 1503/1505/1519/1523、最终 staging push gate 1525 与 deploy 1526 均成功。
  • 防复发:任何 frontend/src/app/**/page.tsx 新增或删除必须在同一提交更新 capability audit 精确路由集合,且 quality gate 失败不得通过放宽断言绕过。
  • 相关记录:BUG-126、BUG-131
  • 复发自:BUG-126
  • 修复版本:6c1dcbe857006ec6ae7463b57b2b7d5947da4851(最终 staging 验收)

BUG-133 | admin users Route Handler 重导出 runtime 导致 production build 失败

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:frontend/src/app/api/admin/users/route.ts、Next.js production build、Gitea staging quality gatecustomer handler 权限和业务逻辑未受改动。
  • 用户现象:PR gate run 1493 的 Python 路由回归和 frontend 1472/1472 均通过,但 next buildNext.js can't recognize the exported runtime field in route. It mustn't be reexportedpublish 被跳过,自动 deploy 未触发。
  • 触发条件:legacy /api/admin/users Route Handler 通过 export { ..., runtime } from "../customers/route" 同时重导出 handlers 和 route segment config。
  • 根因:Next.js 要求 runtime 等 route segment config 在当前 route 文件中可被静态解析,不允许从另一 Route Handler 重导出;既有精确合同测试反而固化了非法 re-export,且并发功能本地 production build 未闭环。
  • 修复:在 users route 本文件静态声明 export const runtime = "nodejs",只重导出 DELETE/GET/PATCH/POST/PUT handlers;不复制 handler、不修改权限或客户数据逻辑。合同测试改为强制本地 runtime 常量并拒绝 runtime re-export。
  • 验证:admin users 目标合同、tsc --noEmit、默认 Turbopack next buildnext build --webpack、全 route segment config re-export 扫描与 git diff --check 已通过;Gitea PR gate 1499 及最终 PR/push gates 1523/1525 均完成 production builddeploy 1526 成功。
  • 防复发:Route Handler 的 runtimedynamicrevalidate 等 segment config 必须本地静态声明;handler 可复用,但 segment config 不得 re-export。新增 alias route 必须经过 production build,而不只运行文本合同测试。
  • 相关记录:BUG-131、BUG-132
  • 复发自:无
  • 修复版本:6c1dcbe857006ec6ae7463b57b2b7d5947da4851(最终 staging 验收)

BUG-134 | staging admin origin selector 未配置导致 exact-SHA 自动部署失败

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:staging .env.staging 的公开 identity selector、自动 deploy run 1502;应用容器、业务数据库和 production 未被修改。
  • 用户现象:staging gate 1500 已成功验证并发布 SHA 9a3d0d440f43deab66c1f8a4a08cdbfc6f9d73eb 的 immutable artifact,但自动 deploy 1502 在远端 env 校验时报 invalid staging selector: ADMIN_USER_ORIGIN 并 fail closed;公网继续健康运行旧 SHA e59f15d352787f3d05425ba8c459d092e9801a20
  • 触发条件:包含双 host self-hosted identity validator 的 controller 部署到现有 staging host,而 .env.staging 尚未包含精确且唯一的 ADMIN_USER_ORIGIN=https://admin.staging.jyotisha.chat
  • 根因:并发 admin rollout 将 admin host origin 加入应用和 validator 合同,但 staging host-managed env 未在发布前同步新增的非密钥 selectorquality gate 验证仓库合同,不读取主机 secret/env,因此直到 mutation 前远端校验才暴露漂移。
  • 修复:已在共享 staging mutation lock 下,仅向原文件原子补入公开 ADMIN_USER_ORIGIN selector,保留全部既有内容、deploy:deploy owner 和 0600 mode;未输出、复制或重写其他 secret 值。随后完整 validator 暴露独立的 service runtime 漂移,转由 BUG-135 处理;仍待 exact-SHA artifact 重新部署。
  • 验证:脱敏只读检查先确认 .env.stagingdeploy:deploy 0600AUTH_USER_ORIGIN 精确且唯一、ADMIN_USER_ORIGIN 计数为 0;原子修复后 ADMIN_USER_ORIGIN 精确且唯一,正式 validators 与 deploy 1526 通过。最终 user host admin paths 为 404admin host /admin 为 307 /login、session API 为 401;容器 restart count 均为 0。
  • 防复发:任何新增 staging host-managed selector 必须在同一 rollout runbook 中包含 deploy 前 presence/exact-value 检查;quality gate 成功不能替代 host env validation。env 修复必须共享 mutation lock、原子替换并保持 owner/mode,严禁打印 raw env。
  • 相关记录:BUG-128、BUG-133、ERR-093、ERR-097
  • 复发自:无
  • 修复版本:6c1dcbe857006ec6ae7463b57b2b7d5947da4851staging host selector 与双 host 验收)

BUG-135 | staging service runtime 缺失且 admin runtime 错误继承 BYPASSRLS 角色

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:staging 私有 PostgreSQL runtime roles、.env.staging.env.staging.database、个人报告 service client 与后台最小权限;production 未受影响。
  • 用户现象:补齐 ADMIN_USER_ORIGIN 后,正式 validator 继续报 invalid staging identity setting: SERVICE_DATABASE_URL。脱敏检查确认应用 env 缺少 SERVICE_DATABASE_URL、数据库 env 缺少 SERVICE_RUNTIME_PASSWORD、PostgreSQL 缺少 service_runtime login role;同时旧 admin_runtime 意外继承了带 BYPASSRLS 的 service_role。当前旧 web 容器同样没有 service URL,因此无法安全恢复旧明文。
  • 触发条件:在早期初始化的 staging 数据卷上部署依赖独立 service client 和最新 admin RBAC 的应用;bootstrap 脚本只在空数据卷初始化时执行,现有 host env/roles 未随 reviewed compatibility contract 对齐。
  • 根因:staging host bootstrap 漂移。数据库保留了 service_role、identity/app/admin runtime roles,但没有后来合同要求的 service_runtime;旧 admin runtime membership 又违反当前 bootstrap 和 RBAC 的明确 revoke 边界。quality gate 不读取 host env 或运行时 role catalog,因此直到远端部署前校验与现场权限审计才暴露。
  • 修复:在共享 mutation lock 下生成独立 staging-only 随机凭据,通过 PostgreSQL stdin 创建/设置 service_runtime,授予 service_role membership 和数据库 CONNECT;将 raw password 仅原子写入 .env.staging.databasepercent-encoded URL 仅原子写入 .env.staging,两文件保持 deploy:deploy 0600。随后撤销 admin_runtimeservice_role membership;未重启容器、未输出凭据、未改 production。已应用的 20260806000000_personal_reports.sql checksum 与仓库一致,保持历史迁移不可变。
  • 验证:两个正式 env validator 均通过;service_runtime 真实密码登录、service_role membership 和 CONNECT 均通过;最终审计再次确认 service_membership=trueservice_connect=trueadmin_service_membership=falseadmin_bypassrls=falseservice_can_login=true。两份 env 均为 deploy:deploy 0600migration 1516、push gate 1525 和 deploy 1526 成功。
  • 防复发:非空数据卷不能依赖 /docker-entrypoint-initdb.d 自动重放;每次新增 runtime role 或 host-managed URL 都必须有兼容性 role repair、脱敏 pre-deploy presence 检查和真实登录/role-membership smoke。admin_runtime 永不得继承 service_roleservice writes 只能使用独立 SERVICE_DATABASE_URL。已应用迁移不得为修正文案而改 checksum。
  • 相关记录:BUG-128、BUG-134、ERR-093、ERR-098
  • 复发自:无
  • 修复版本:6c1dcbe857006ec6ae7463b57b2b7d5947da4851staging role/env 与 exact-SHA 验收)

BUG-136 | staging quality gate frontend build 无界卡住并耗尽 45 分钟 job

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:Gitea Staging Backend Quality Gate validate job、staging artifact publication;应用代码、staging host 和 production 未被本次失败修改。
  • 用户现象:push gate 1507 对 reviewed SHA 6fd22921197715e065d0d137fbd7ea5a82a188e4 完成 frontend 1472/1472、ESLint 0 errorNext.js 输出 Compiled successfully in 38.2s 后约 44 分钟无 further output45 分钟 job 超时,publish 被跳过;公网继续运行旧健康 SHA e59f15d352787f3d05425ba8c459d092e9801a20
  • 触发条件:质量门禁执行 npm run build --prefix frontend 没有命令级 bounded timeoutTurbopack 在编译后静态生成/收尾阶段无输出卡住时只能等待 job-level timeout。
  • 根因:quality gate 只有 45 分钟 job 上限,缺少针对生产构建步骤的 fail-closed deadline;此前 PR gate 1503/1505 同一代码完整 build 通过,说明本次是 runner/build hang,不是已观测的业务编译错误。
  • 修复:在 Gitea validate 中将 frontend production build 包在 timeout 600 内,超时输出明确事实并以非零状态失败;不跳过 build、不降低测试、不发布旧 artifact。新增 workflow contract 锁定该 bounded timeout。
  • 验证:本地 workflow contracts 35/35PR gates 1511/1519/1523 和 staging push gates 1514/1521/1525 均在 600 秒 command deadline 内完成 production build;最终 gate 1525 publish 与 deploy 1526 成功。
  • 防复发:所有可能长时间静默的编译、镜像构建和外部网络步骤都必须有命令级 deadline,且 deadline 失败必须 fail closed;保留 job-level timeout 作为第二层上限,不把 timeout 当成功。
  • 相关记录:BUG-129、BUG-131、ERR-096、ERR-099
  • 复发自:无
  • 修复版本:8dc61e3135d8afb96b6683714f41a1716761164e;最终验收 6c1dcbe857006ec6ae7463b57b2b7d5947da4851

BUG-137 | staging deploy 在公网 upstream 尚未收敛时用旧 SHA 立即判失败

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:Gitea Deploy staging 最终公网验证、自动回滚;数据库迁移已成功,production 未受影响。
  • 用户现象:exact-SHA gate 1514 与 migration 1516 成功后,deploy 15171518 均启动目标 web/API image 并达到容器 healthy,却在约 3 秒后的公网 verification 返回非零,随后成功恢复旧 web/worker;公网和 .state/deployed-revision 均保持旧 SHA e59f15d352787f3d05425ba8c459d092e9801a20
  • 触发条件:Compose 切换到目标容器后,旧 Caddy upstream 在短暂收敛窗口仍可让 /login 返回 200;脚本只轮询 /login,然后对公网 health SHA 和其余 predicate 仅检查一次,读取旧 SHA 时立即触发回滚。
  • 根因:发布验证把“login 可达”和“公网已路由到 exact SHA”拆成了不对称检查;容器健康与代理 upstream 收敛不是同一时刻,单次 SHA 检查形成确定性 race。目标 image 隔离 probe 已确认注入的 GITHUB_SHA 为目标 SHA。
  • 修复:在原 60 秒总预算内,每 5 秒原子重查 login、admin 未登录重定向、admin API/account 401、公网 health exact SHA、私有 API health 和 Swiss Ephemeris;仅当所有 predicate 同轮满足才成功。预算耗尽仍 fail closed 并只输出状态码、observed SHA、health 状态等脱敏摘要,不输出正文、env 或凭据。
  • 验证:合同测试 35/35deploy 1522 的脱敏摘要证明 bounded verifier 等待到目标 public SHA 后才报告独立 admin-host 问题;后续 PR gate 1523、push gate 1525、artifact ID 12 和 deploy 1526 均成功,最终公网/host state/main/staging 完整 SHA 一致。
  • 防复发:发布验证必须等待最终外部路由 identity,而不能把单个 readiness endpoint 当作代理收敛证明;所有重试必须有总上限,失败记录仅含非敏感 predicate 状态并保持自动回滚。
  • 相关记录:BUG-129、BUG-136、ERR-099、ERR-100
  • 复发自:无
  • 修复版本:28f4207f295276968427686a21971fd53abb42ec;最终验收 6c1dcbe857006ec6ae7463b57b2b7d5947da4851

BUG-138 | staging Caddy 保留旧单文件 bind inode 且 admin 验证误走用户域名

  • 状态:resolved
  • 首次发现:2026-08-06
  • 最近更新:2026-08-06
  • 影响面:staging Caddy 双 host 路由、admin TLS、Deploy staging 未登录边界验证;production 未受影响。
  • 用户现象:修复公网 SHA 收敛后,deploy 1522 明确观测目标 SHA、私有 API 和 Swiss Ephemeris 均正常,但在用户域名上得到 /admin -> 307 //api/admin/session -> 403;同时 admin.staging.jyotisha.chat TLS 握手失败。workflow 在 60 秒后自动恢复旧应用,未写入新 deployed-revision。
  • 触发条件:controller 通过原子目录同步替换 deploy/Caddyfile.staging,但长期运行的 Caddy 容器仍持有旧单文件 bind mount inode;随后 checker 又把 admin 页面/API 请求错误发送到 STAGING_URL 而不是 ADMIN_USER_ORIGIN
  • 根因:host 文件与 Caddy 容器 mount inode 漂移。只读现场证据显示 host Caddyfile 含 admin host、运行容器内文件不含,inode/size/mtime 均不同;staging VPS 从两台权威 nameserver 查询 admin A 记录均为 118.26.111.127,排除 DNS 缺失。用户域名按新 Caddy 合同本应对 admin paths 404,因此旧 checker 的 307/401 期待也违反双 host 边界。
  • 修复:应用 Compose 切换后显式 --force-recreate --no-deps caddy,使其重新挂载 gate-attested Caddyfile;完整 convergence 同轮要求用户域名 admin page/API 均 404、admin origin page 307 到 /login、admin API 401,并继续要求 exact public SHA、account 401、私有 API/Swiss 健康。失败仍 bounded、fail closed 并自动恢复旧应用。
  • 验证:shell/workflow contracts 35/35、PR gate 1523、push gate 1525、artifact ID 12 和 deploy 1526 成功。Caddy 被 force-recreate,容器内配置包含 admin host;公网 user host /admin/api/admin/session 均 404admin host / 为 308 /admin/admin 为 307 /login、session API 为 401Caddy restart count 为 0。
  • 防复发:原子替换单文件 bind mount 后必须 recreate/reload 长期运行服务;部署 smoke 必须分别使用各自主机 origin,不能在 user host 上测试 admin host 合同。保留 authority DNS、mount inode 和 TLS 检查作为脱敏现场证据。
  • 相关记录:BUG-134、BUG-137、ERR-097、ERR-100、ERR-101
  • 复发自:无
  • 修复版本:6c1dcbe857006ec6ae7463b57b2b7d5947da4851

BUG-139 | staging 后台缺失初始 Owner 且拒绝重定向与 Caddy 形成无限循环

  • 状态:resolvedlocal candidatepending review/deployment
  • 首次发现:2026-08-07
  • 最近更新:2026-08-07
  • 影响面:admin.staging.jyotisha.chat 后台入口、self-hosted admin RBAC 初始 Owner 恢复;production 未受影响。
  • 用户现象:用户完成后台域名登录后访问 /,浏览器报 ERR_TOO_MANY_REDIRECTS;未认证公开链仍正常表现为 / 308 到 /admin、再 307 到 /login、最终 200。
  • 触发条件:已认证 self-hosted 用户通过身份 session,但 admin_permission_keys 没有返回 admin.access,后台 gate 产生 403;历史同域 fallback 将所有非 401 授权错误重定向到 /,而独立后台 Caddy 又将 / 永久重定向到 /admin
  • 根因:第一层是双 host 发布后仍保留 BUG-123 的同域 403/503 -> / 行为,与 BUG-138 的后台根路径 308 -> /admin 组合成确定性循环。第二层是 20260806010000_admin_rbac.sql 的一次性 bootstrap 只捕获迁移执行当时已经是 identity admin 的用户;staging 脱敏聚合显示 identity_admins=1auth_users=1bootstrap_eligible_admins=1,但 active_admin_users=0owner_assignments=0identity_admins_missing_rbac=1,因此当前唯一 active identity admin 没有 RBAC Owner,真实授权结果为 403,而不是 cookie/host 隔离或数据库不可用。
  • 修复:后台 layout 对 401 仍转 /login403 使用 Next.js forbidden interrupt 返回明确 403 页面;503 以 307 转到 admin layout 外的独立 /admin-unavailable route,由该 route 最终返回 503 和 cache-control: no-store。后台根 route 对 403/503 直接返回对应状态和 no-store 文本响应,所有拒绝路径都不再导向 /。授权边界现将读取 self-hosted 配置、Host 判定、Better Auth/session、identity DB 与 RBAC DB 查询置于同一异常边界:既有 AdminAuthorizationError 原样保留,identity 401/403 继续映射为脱敏 401/403,Host 不匹配仍为 403,其余未知基础设施异常统一转换为 后台服务暂时不可用 503API adminErrorResponse 保留该最终状态。Owner 恢复 migration 保留在 frontend/db/migrations,但在任何表查询前用 to_regclass/to_regprocedure 检查 identity/auth 表、RBAC 表与关键函数;identity-only MIGRATIONS_DIRECTORY=db/migrations 缺少 RBAC 时安全 no-op 并正常记账,full staging 合并两目录后按文件名排序,在 20260806010000_admin_rbac.sql 之后执行恢复,而 Supabase-only/production migration 集合不包含该文件,不污染 production Supabase ledger。恢复语义仍为:已有 active Owner no-op;真正空库 no-op;只有恰好一个当前可登录(未封禁,或封禁截止时间已过)、已同步 auth.users、持久 identity role 包含 admin,且 admin_users 不存在或尚未 revoked 的候选才恢复 Owner。历史 revoked admin 明确排除,admin_users 冲突使用 do nothing,不得清除 revoked_at/revoked_by;零个或多个候选均以约束错误 fail closed。运行时授权继续只依赖数据库 RBAC,不读取 ADMIN_EMAILS,也不批量授权所有 identity admin。
  • 验证:重定向合同覆盖 401 -> /login、403 forbidden、嵌套页面 503 只转 /admin-unavailable 以及 Caddy / -> /admin 不成环;直接执行独立 route handler 验证最终响应为 503、no-store、无 Location。可执行授权单元测试覆盖配置 reader、Better Auth/identity reader 与 RBAC query 未知故障均脱敏为 503,既有授权异常与 identity 401/403 不变,并直接验证 adminErrorResponse 最终返回 503。真实 PostgreSQL fixture 验证 db-only identity migration 在 RBAC 缺失时安全 no-op、full 两目录流程执行恢复,以及空库 no-op、已过期封禁和带空格角色的单一同步候选可恢复、未同步或仍封禁账号不可恢复、已有 Owner 时第二个 identity admin 不获授权、revoked 历史管理员不会复活且撤销字段保持不变、两个候选和零候选均 fail closed。聚焦与完整测试、lint、TypeScript、构建结果见本次候选提交验证记录。
  • 防复发:独立后台 host 的拒绝路径不得使用相对 / 作为逃生路由;401、403、503 必须分别保留认证、授权和服务故障语义,layout 不能把 503 吞成 500,授权依赖的未知配置/provider/数据库错误也不得泄露或退化为 500。跨 identity 与 RBAC 的恢复 migration 必须留在 DB migration ledger,并以显式 schema/function 前置检查兼容 identity-only no-op;不能放入 production 使用的 Supabase-only ledger。一次性 RBAC bootstrap 后新增的初始管理员必须通过受约束向前 migration 或显式角色管理进入权限图;历史撤销是安全边界,禁止自动清除,也禁止用邮箱 allowlist 或“所有 identity admin”兜底。
  • 相关记录:BUG-123、BUG-134、BUG-138
  • 复发自:BUG-123
  • 修复版本:本次 staging admin redirect/RBAC recovery 候选提交

BUG-140 | 首页入口卡片点击后持续显示灰色交互态

  • 状态:resolved
  • 首次发现:2026-08-07
  • 最近更新:2026-08-07
  • 影响面:首页 starter-theme-carddaily-starlanguage-card 与共享 product-entrypoint-card 交互反馈
  • 用户现象:用户点击首页主题或产品入口卡片后,卡片背景持续处于灰色/选中样式,没有在松开指针后恢复。
  • 触发条件:在触摸设备点击卡片,或在桌面端点击后让指针继续停留在卡片上。
  • 根因:卡片把铺满表面的背景反馈绑定到无设备能力边界的 :hover;移动浏览器可能在点击后保留模拟 hover,桌面指针停留也会让一次点击看起来像永久选中。hover、短暂按压和真实 disabled 三种状态因此在视觉上混在一起。
  • 修复:将铺灰背景收敛到 :active,只在按压期间显示并于释放后恢复;桌面 @media (hover: hover) 只保留轻量边框、光泽和箭头位移,不再改变卡片底色;键盘继续使用现有 focus-visible 轮廓,真实 disabled 透明度规则保持不变。
  • 验证:frontend/tests/consultation-entrypoint.test.ts 新增 sticky-hover 回归;与 frontend/tests/starter-questions.test.ts 精确运行 40/40 通过;目标 ESLint 与 git diff --check 通过。全量前端尝试中 1457 项通过,16 项因本机缺少 Docker 或 python 可执行命令而失败,与本次 CSS 修改无关。
  • 防复发:首页可点击卡片的表面底色只能由短暂 :active 或真实业务 disabled 状态改变;hover 视觉必须限制在支持 hover 的设备,且回归测试禁止重新为这些卡片的 hover surface 添加背景色。
  • 相关记录:无
  • 复发自:无
  • 修复版本:待提交

BUG-141 | self-hosted PostgreSQL DATE 行导致 consultation 503

  • 状态:resolvedlocal candidate
  • 首次发现:2026-08-07
  • 影响面:self-hosted staging 的 profile/consultation 读取;production 未受影响。
  • 根因:本地 PostgreSQL adapter 将 DATE 查询结果保留为 JavaScript Date,下游业务合同要求无时区的 YYYY-MM-DD
  • 修复:按列类型将查询结果中的 DATE 归一化为本地日历字符串,timestamp 仍保持 Date;同步删除 legacy rectification 后的 account stale test。
  • 验证:focused 回归 37/37、完整前端 1017/1017、local quick quality gate 291 passed/1 skippedTypeScript、lint0 error)、production build 与 git diff --check 通过。
  • 修复版本:本次 staging-only 集成候选

BUG-142 | Gitea staging publish job Run 1540 post-job timeout

  • 状态:resolvedlocal candidate
  • 首次发现:2026-08-07
  • 最近更新:2026-08-07
  • 影响面:Gitea Staging Backend Quality Gate publish jobvalidate、exact-SHA 与安全校验未修改,production 未涉及。
  • 用户现象:Run 1540 在发布步骤完成后进入 post-job timeout,质量门禁未能正常收口。
  • 触发条件:publish job 的 45 分钟 job timeout 不足以覆盖发布后的收尾阶段。
  • 根因:publish job 与 validate job 共用 45 分钟上限,未为发布收尾保留独立余量。
  • 修复:仅将 .gitea/workflows/backend-quality-gate.yml 的 publish timeout 从 45 分钟调整为 60 分钟;validate 仍为 45 分钟,安全检查与 exact-SHA 行为保持不变。
  • 验证:新增合同断言区分 validate=45 与 publish=60;聚焦 staging workflow test、YAML 解析和 git diff --check 通过;未 push、deploy 或触碰 production。
  • 防复发:合同测试必须同时锁定 validate 与 publish 的 job timeout,避免发布收尾预算被误改。
  • 相关记录:BUG-136
  • 复发自:无
  • 修复版本:本次提交(staging-only

BUG-143 | 新增 membership 页面未同步能力审计精确路由集合

  • 状态:resolvedlocal candidate,远端 gate 待 follow-up 更新)
  • 首次发现:2026-08-07
  • 最近更新:2026-08-07
  • 影响面:tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources、Gitea staging quality gate;会员页实现本身未由本记录改动。
  • 用户现象:staging gate run 1549 中 290 passed、1 skipped、1 failed;能力审计已扫描到新 membership 页面(位于 login 之后、reports/[reportId] 之前的排序位置),但测试仍精确断言旧集合,publish 被跳过,自动 deploy 未发生。
  • 触发条件:新增 frontend/src/app/membership/page.tsx 后运行 capability audit 精确集合回归。
  • 根因:这是 BUG-126reports 页面)、BUG-132(后台 14 页面)同类精确路由集合防复发模式第三次复发。旧防线失效的直接原因是会员页本地交付矩阵只运行了 frontend testsmembership-page/sidebar/starter/epay 等静态合同),没有把 Python 侧 test_capability_audit_scans_registry_and_local_sources 纳入同变更验证,因此真实 App Router 路由集合与测试期望再次分叉;该测试由 staging quality gate 动态扫描 frontend/src/app 才能捕获。
  • 修复:在预期排序位置 login 之后、reports/[reportId] 之前加入 membership,保留精确完整集合,不放宽为子集/包含断言。
  • 验证:本地目标节点 /Users/jesse/Documents/Jyotisha/.venv/bin/python -m pytest tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources -q 通过(1 passed);git diff --check 通过;远端 staging gate 需在 follow-up 提交/推送后更新确认,本记录不提前声称远端收口。
  • 防复发:新增或删除任何 frontend/src/app/**/page.tsx 页面时,必须在本变更中同步运行 test_capability_audit_scans_registry_and_local_sources(或完整 test_api_server_security.py 聚焦切片),并在本地验证通过后才进入推送门禁;前端本地矩阵不能替代该 Python 能力审计节点。
  • 相关记录:BUG-126、BUG-132
  • 复发自:BUG-126(模式:新增页面未同步能力审计精确路由集合)
  • 修复版本:待 follow-up commit / gate

BUG-144 | db/migrations 副本依赖业务 schema 导致 identity-only fixture 迁移失败

  • 状态:resolvedlocal candidate,远端 gate 待 follow-up 更新)
  • 首次发现:2026-08-07
  • 最近更新:2026-08-07
  • 影响面:frontend/db/migrations/20260807020000_redeem_security.sql(已删除)、frontend/tests/redeem-orders-contract.test.tsfrontend/tests/database-redeem-security.test.tsstaging quality gate run 1552 frontend 1054 passed / 2 failedPython quick 291 passed / 1 skippedpublish / deploy 均未发生。
  • 用户现象:gate run 1552 数据库真实失败,publish 被跳过,自动 deploy 未发生。
  • 触发条件:新增依赖业务 schemapublic.redemption_codespublic.credit_transactions)的 redemption 安全迁移时,同时在 frontend/db/migrationsfrontend/supabase/migrations 各放一份;database-local-business 聚合迁移(两目录全量)通过,而只应用 frontend/db/migrations 的 identity-only fixture 中业务 schema 尚不存在。
  • 根因:database-self-hosted-identityidentity-auth-integration 只应用 frontend/db/migrationsidentity foundation),新的 db 副本假设 public.redemption_codes 已由 supabase compatibility 迁移创建;identity-only 序列因此 migration failed,与 BUG-127 同类(新增业务表必须精确选择迁移位置/全迁移),但不是 BUG-127 或 BUG-143 的复发,属本变更独立根因。
  • 修复:删除 frontend/db/migrations/20260807020000_redeem_security.sql,仅保留 frontend/supabase/migrations/20260807030000_redeem_security.sqlcontract 测试只审该单一 migration,删除 byte-identical 双副本断言,并新增硬防线:断言 frontend/db/migrations/20260807020000_redeem_security.sql 不存在(existsSync === false);DB security 测试 aggregate runner 只期望 applied 20260807030000_redeem_security.sql,不再期待 070200,并断言 stdout 不含 20260807020000_redeem_securitySQL 内容未作任何变更。
  • 验证:本机无 Docker,无法执行 identity-only 与 aggregate DB 实测;运行 npx tsx --test tests/redeem-orders-contract.test.ts7 passed,原 6 项 + 新增 existsSync 防复发守卫 1 项)、tsc --noEmitclean)与 git diff --check 通过;目标 identity DB 实测(database-self-hosted-identityidentity-auth-integration)与 aggregate DB 测试(database-local-businessdatabase-redeem-security)留待远端 gate 确认,本记录不提前声称远端收口。
  • 防复发:frontend/db/migrations 只放 identity foundation 独立可执行迁移;依赖业务 schema(public.redemption_codespayment_orders 等)的迁移只进 frontend/supabase/migrations;任何新增/删除迁移必须在本变更中同时跑 identity-only 两测试(database-self-hosted-identityidentity-auth-integration)与 aggregate DB 测试(database-local-business 等)。
  • 相关记录:BUG-127、BUG-143
  • 复发自:无(独立根因,非 BUG-127 / BUG-143 复发)
  • 修复版本:待 follow-up commit / gate

BUG-145 | staging Web health 被空模型目录错误阻断

  • 状态:resolvedlocal candidate,远端 gate 待 follow-up 更新)
  • 首次发现:2026-08-07
  • 最近更新:2026-08-07
  • 影响面:frontend/src/app/api/health/route.ts 的 Web 容器 healthcheck;数据库尚未发布默认模型时 staging Web 被错误判定为 unhealthy,真实基础设施故障的 503 语义不变。
  • 用户现象:staging 数据库没有 published default model 时,model catalog 只有 default_model_unavailabledatabase_model_catalog_empty,但 /api/health 返回 HTTP 503,导致 Web bootstrap/Compose healthcheck 互相等待,页面无法进入稳定 healthy 状态。
  • 根因:health route 将所有非 ok 聚合状态统一映射为 503;同时把可恢复的模型目录缺少默认模型状态标成 blocked,混淆了业务配置未就绪与模型目录数据库基础设施不可用。上一轮放宽为 degraded -> 200 后,又会把 Jyotish API 非 2xx 误报为 healthy。
  • 修复:模型目录缺少 published default model 时报告 degradeddatabase_model_catalog_unavailable 与 Jyotish API 非 2xx 保持 blocked。整体 health 仅在存在 blocked 检查时返回 503,模型目录缺省配置仍返回 200;未恢复 legacy model env vars,也未改变 consult/report/onboarding 的 fail-closed 模型解析路径。
  • 验证:npx tsx --test tests/health-deployment.test.ts12 passed);npx eslint src/app/api/health/route.ts tests/health-deployment.test.ts(通过);git diff --check(通过)。
  • 防复发:health contract 必须同时覆盖“无 published default model -> modelCatalog=degraded、HTTP 200”、“Jyotish API 非 2xx -> blocked”和“任意 blocked -> HTTP 503”,不得让真实基础设施故障落入 degraded
  • 相关记录:BUG-145
  • 复发自:无(staging bootstrap deadlock 的独立 health contract 根因)
  • 修复版本:待 follow-up commit / gate

BUG-146 | staging 后台写请求把 Caddy 上游协议误当公开来源

  • 状态:resolved
  • 首次发现:2026-08-07
  • 最近更新:2026-08-07
  • 影响面:所有经 requireAdminMutation 的后台写接口、admin.staging.jyotisha.chat 模型管理写操作;普通 staging 用户域名、其他后台功能的通用 ReasonActionModal 与 production 未改动。
  • 用户现象:管理员在独立后台域名提交 POST /api/admin/models 时,浏览器 Origin 为 HTTPS 公开后台域名,但 Caddy 转发后的 Route Handler 请求 URL 使用上游 HTTP 协议,旧校验因此返回 403 请求来源不可信
  • 触发条件:请求经 staging Caddy reverse_proxy web:3000 进入 Next.js,公开 origin 与上游 request.url 协议不同;旧 isSameOriginAdminMutation 只比较这两个 origin,未核对 Caddy 提供的公开 Host/Proto 头。
  • 根因:共享后台 mutation guard 把应用上游 URL 当作浏览器公开来源真值,没有结合既有 ADMIN_USER_ORIGIN、原始 Host 与 Caddy 的 X-Forwarded-Host / X-Forwarded-Proto;因此合法后台请求被拒绝,同时也不能安全地仅信任任意 forwarded host。
  • 修复:requireAdminMutation 统一调用可测试的共享来源策略;配置 ADMIN_USER_ORIGIN 时要求浏览器 Origin 精确匹配、Host 与规范化后的 forwarded host 一致、公开 host/proto 精确匹配后台 origin,缺失、歧义、畸形或冲突的 forwarded 值全部 fail closed;未配置后台 origin 的既有直连环境继续使用严格 same-origin fallback。复用 identity host 规范化 helper,未新增同义 envstaging Caddy 继续在用户域名对 /admin*/api/admin/* 返回 404。模型管理同时删除 saveProvider/saveDraft/publish/rollback 的客户端“操作原因”字段与交互,服务端分别注入固定中文审计说明后继续传给原 DB procedure 的非空 reason 参数;通用 ReasonActionModal 未改动。
  • 验证:npx tsx --test tests/admin-http-origin.test.ts tests/admin-model-management-ui-contract.test.ts tests/identity-host-routing.test.ts tests/admin-reauth.test.ts tests/health-deployment.test.ts34 passed);相关 ESLint、git diff --check 与最终差异审查见本提交验证记录。
  • 防复发:后台 mutation 来源测试必须同时覆盖合法 admin Origin + 公开 host/proto、错误 Origin、普通 staging host、Host/forwarded host 冲突、逗号多值、畸形 host、缺失 proto 与非法 ADMIN_USER_ORIGIN;模型管理合同必须拒绝客户端 reason,并确认四个固定审计说明仍传入现有 procedure。
  • 相关记录:BUG-134、BUG-138、BUG-139
  • 复发自:无
  • 修复版本:本地候选提交(未 push / deploy

BUG-147 | staging 模型供应商保存成功但列表为空

  • 状态:resolvedlocal candidatestaging migration/deploy 待验证)
  • 首次发现:2026-08-08
  • 最近更新:2026-08-08
  • 现象:POST 保存成功但 GET providers 为空。
  • 触发:self-hosted admin_runtime 直查五张 model 表:model_providersmodel_configsmodel_config_versionsmodel_publish_eventsmodel_connection_test_evidence
  • 根因:表启用 RLS 且有 SELECT grant,但缺少 admin_runtime SELECT policySECURITY DEFINER 写成功、读被静默过滤。
  • 修复:20260808020000 migration 为 model_providersmodel_configsmodel_config_versionsmodel_publish_eventsmodel_connection_test_evidence 增加仅 SELECT policy。
  • 验证:focused 7/7full frontend 1071/1071lint 0 errors、3 warningsbuild 通过;git diff --check 通过。
  • 防复发:模型管理数据库回归测试同时覆盖 admin_runtime 对五张 model 表的直接读取,迁移需保持 SELECT grant 与 SELECT policy 成对存在。
  • 相关记录:BUG-124、BUG-135、BUG-145、BUG-146
  • 复发自:无
  • 修复版本:待本次 staging SHA

BUG-148 | Node 22/24 all-address lookup 使模型发现 DNS pinning 失效

  • 状态:resolved
  • 首次发现:2026-08-08
  • 最近更新:2026-08-08
  • 影响面:共享 requestAllowedModelProvider 的固定 DNS HTTPS 请求;OpenAI-compatible 供应商模型发现无法到达已通过公网 SSRF 校验的上游。支付网关、SSRF 边界、重定向拒绝、超时与响应大小限制未放宽。
  • 用户现象:staging Web 容器内使用同一 DNS pinning/request 链路请求模型列表时失败,脱敏错误为 ERR_INVALID_IP_ADDRESS,已固定的地址族为 IPv4。
  • 触发条件:Node 22/24 的 https.request / net.connectlookup 选项 all=true 调用自定义 DNS callback;旧实现无条件调用 callback(null, address, family),而 all-address 契约要求第二参数为 [{ address, family }]
  • 根因:共享 HTTPS helper 只实现了旧的单地址 lookup callback 形态,没有根据 Node 传入的 options.all 切换返回值;因此安全校验和 DNS 解析均成功后,Node 在建连前把字符串结果按地址数组读取并抛出 ERR_INVALID_IP_ADDRESS。discover route 的 provider 读取、密钥解密、/models URL、响应解析和错误翻译均不是本次失败根因。
  • 修复:抽出 pinnedAddressLookup 供共享 HTTPS 请求使用;options.all=true 时返回单元素已验证地址数组,其他模式继续返回 address, family。仍只使用已通过完整公网校验的固定地址,不重新解析、不跟随重定向,也不删除任何 SSRF/信任边界校验。
  • 验证:npx tsx --test tests/model-provider-encrypted-credentials.test.ts 的真实本地 TCP 回归在修复前稳定失败为 ERR_INVALID_IP_ADDRESS4 passed / 1 failed),修复后对 autoSelectFamily=true 的 all-address 模式和 autoSelectFamily=false 的单地址模式均实际建连成功(5 passed / 0 failed)。
  • 防复发:DNS-pinned 请求测试必须通过 Node 网络栈实际调用自定义 lookup,并同时覆盖地址数组与单地址 callback 契约;不得用只匹配源码字符串的断言替代该回归。
  • 相关记录:BUG-146、BUG-147
  • 复发自:无
  • 修复版本:2026-08-08 staging 变更

BUG-149 | staging quality gate npm ci 缺少安装级 deadline 导致 45 分钟黑洞

  • 状态:resolvedlocal candidate,远端 gate/deploy 待本提交)
  • 首次发现:2026-08-09
  • 最近更新:2026-08-09
  • 影响面:Gitea staging quality gate 的依赖安装阶段、manman-linux runner 与 Gitea/runner 控制面可用性;测试、lint、build、exact-SHA checkout/publish/deploy 合同未改变。
  • 用户现象:Run 1618SHA ffbe505c)在 Python pip 完成后进入 digest-pinned Node 容器执行 npm ci;步骤无后续 npm 输出,直到 45 分钟 job timeoutpublish/deploy skipped。
  • 触发条件:self-hosted runner 在受限 Node 容器中执行 frontend npm ci,但安装命令本身没有 fail-closed deadlinenpm registry/fetch 也没有比 job-level 更短的诊断边界。
  • 根因:BUG-149 的宿主资源争用已由容器边界缓解,但剩余风险转移到容器内 npm ci 黑洞:安装命令缺少比 45 分钟 job-level timeout 更短的 hard deadline,且 npm fetch/retry 没有显式网络诊断边界;当前没有 OOM 或 PID 耗尽证据。
  • 修复:仍只将 npm ci 放入 digest-pinned Node 容器;保留 CPU 1.5、memory 2g、no swap、pids 256,并在容器内用 timeout --signal=TERM --kill-after=30s 900s 包住安装,避免外层 timeout 杀掉 docker CLI 后留下 orphan 容器。npm 增加 --fetch-timeout=60000、2 次重试和 1s/10s retry 上下限;超时或非零退出都输出明确诊断并 fail closed。测试、lint、build 与 exact-SHA 行为保持原样,不回退到宿主 npm。
  • 验证:本地 node --test frontend/tests/staging-backend-workflows.test.ts 32/32 passedgit diff --check 通过。远端 staging gate/deploy 待本提交后验证,本记录不提前声称远端收口。
  • 防复发:依赖安装必须维持 digest-pinned Node runtime、显式资源上限、命令级 hard timeout 与 npm 网络 timeout;合同测试需锁定只有 npm ci 在受限容器内执行,并持续确认测试、lint、build、exact-SHA checkout/publish/deploy 语义未漂移。
  • 相关记录:BUG-129、BUG-136、BUG-142
  • 复发自:无
  • 修复版本:待本次提交 / gate / deploy

BUG-150 | staging publish 的 Webpack 镜像构建耗尽共享 Gitea 资源

  • 状态:resolvedlocal candidate,远端 gate/deploy 待本提交)
  • 首次发现:2026-08-09
  • 最近更新:2026-08-09
  • 影响面:Gitea staging quality gate 的 publish 镜像构建、共享 Gitea/runner 可用性;production 未涉及。
  • 用户现象:Run 1634 的 validate 在约 12 分钟内成功,publish 随后在 Docker 内执行 next build --webpack 超过 25 分钟;期间 Gitea API 持续返回 502 和空 JSON,最终 job 中断。
  • 触发条件:frontend/package.json 将默认 build 改为 Webpack 后,Dockerfile 的 RUN npm run build 也继承该构建器;classic Docker builder 的慢 COPY 与资源受限 runner 进一步放大构建开销。
  • 根因:为非 push 校验引入的 Webpack 兼容参数错误地放进了全局 package script,使实际镜像发布也从已成功的 Turbopack 路径切换到高开销 WebpackGitea 与 runner 共享资源时因此拖垮控制面。直接重跑旧 revision 会重复相同故障。
  • 修复:恢复默认 npm run buildnext build,让 Docker publish 继续使用 Turbopack;仅在 PR/manual 的非 push validate 分支显式追加 -- --webpack。staging push 继续跳过重复 production build,由 publish 镜像构建唯一验证。
  • 验证:聚焦 workflow contract 锁定默认 Turbopack、非 push Webpack 与 staging push 单次镜像构建语义;本地测试和 git diff --check 通过。远端 gate、publish、deploy 与 exact-SHA smoke 待本提交后验证。
  • 防复发:不得把只用于隔离 worktree/非 push 校验的构建器参数写回全局 package script;发布构建器变化必须由 workflow contract 同时覆盖 Dockerfile 与事件分支。
  • 相关记录:BUG-142、BUG-149
  • 复发自:无
  • 修复版本:待本次提交 / gate / deploy

BUG-151 | 手工 Release Gate 被分配到缺少 Docker Compose v2 的 runner

  • 状态:mitigated
  • 首次发现:2026-08-09
  • 最近更新:2026-08-09
  • 影响面:Gitea 手工 release-quality-gate.yml Run 1638;旧生产、Supabase、DNS 和新生产应用均未被切换。
  • 用户现象:候选 SHA 9235ee66f6d889e6c27e4b85411448889dd7d37d 已通过 staging gate 并在公网 staging 运行,但手工 Release Gate 在 1113 个前端子测试中出现 17 个失败。失败均从 docker compose --project-namedocker compose --env-file 返回 unknown flag 开始。
  • 触发条件:完整 release profile 在 xiaoxin runner 执行 PostgreSQL/Compose 集成测试,而该 runner 只有 Docker CLI、没有可用的 Docker Compose v2 插件。
  • 根因:workflow 的工具链预检只执行 docker version,未验证 docker compose;同时 Release Gate 与已验证具备 Compose v2 的 staging/backend runner 分离,导致环境能力漂移直到完整测试阶段才暴露。
  • 修复:将手工 Release Gate 收敛到 manman-linux,并在依赖安装前强制 docker compose version --short 必须为 v2;增加 workflow 回归断言,防止重新绑定到无 Compose runner 或删除能力检查。
  • 验证:Run 1638 的脱敏日志确认 17 个失败均由 Compose 命令不可用触发;同日 manman-linux 的 backend gate Run 1636 已报告 Docker Compose 2.40.3 并成功完成质量门;本地聚焦 workflow 测试 4/4 通过。仍需新 SHA 的手工 Release Gate 成功后再视为完整关闭。
  • 防复发:任何执行 Compose 集成测试的 runner 必须在昂贵依赖安装和测试前显式验证 Compose v2Docker Engine 可用不能替代 Compose 能力证明。
  • 相关记录:BUG-128、BUG-129、BUG-136、ERR-095、ERR-099、ERR-103
  • 复发自:无
  • 修复版本:待新 SHA 的 Release Gate 验证

BUG-152 | 订单返回套餐后再次返回会重新进入订单页

  • 状态:resolvedlocal candidate
  • 首次发现:2026-08-10
  • 最近更新:2026-08-10
  • 影响面:/membership/membership/orders 的浏览器返回历史;支付、订单读取、兑换与 production 未改动。
  • 用户现象:从套餐与会员页进入订单记录,点击返回套餐页后,再点击套餐页返回,会再次进入订单记录页,形成往返循环。
  • 触发条件:套餐页用普通 Link 将订单页压入历史记录,订单页返回时又用普通 Link 将套餐页再次压入历史记录。
  • 根因:订单页的“返回套餐与会员”是返回语义,却使用了默认 push 导航,历史栈变成 套餐页 → 订单页 → 套餐页
  • 修复:订单页返回 Link 使用 Next.js 原生 replace,将当前订单页历史项替换为套餐页;套餐页再次返回时回到进入套餐前的原入口。
  • 验证:聚焦 membership 合同测试锁定返回 Link 的 replace 语义;前端生产构建 next build --webpack 通过;git diff --check 通过。未 push、deploy 或触碰 production。
  • 防复发:固定目标的“返回上级页”不能再次 push 当前上级页;会员页回归测试持续锁定订单页返回的 replace 语义。
  • 相关记录:无
  • 复发自:无
  • 修复版本:本地候选提交(未 push / deploy

BUG-153 | 个人报告页面无法下滑且命盘与打印版式失真

  • 状态:resolvedlocal candidate,待 review/deployment
  • 首次发现:2026-08-09
  • 最近更新:2026-08-09
  • 影响面:/reports/[reportId] 网页阅读、D1/D9/D10 命盘 SVG、浏览器打印/保存 PDF;聊天页保留既有全局滚动锁。
  • 用户现象:报告页只能通过缩小浏览器查看下方内容;打印或保存 PDF 时长文本、表格和分页缺少稳定版式;命盘内多个宫位的星体文字叠在左上宫格;当前版本还隐藏了已存在的 PDF 操作入口。
  • 触发条件:报告页运行在全局 html, body { overflow: hidden } 的聊天壳层中但自身没有纵向滚动边界;命盘为每个宫位绘制 occupants 时重复使用固定的局部坐标却未应用宫格偏移;打印样式继续保留屏幕表格的 min-width/横向滚动合同,并把可能超过一页的长摘要整体设为避免分页。
  • 根因:报告页面复用了聊天应用的根滚动策略,却没有为报告建立独立 100dvh + overflow-y:auto 阅读容器。PlanetList 的调用没有 translate(cell.x, cell.y),因此十二宫共享同一绘制原点,且未对 schema 允许的密集 occupants 做行数上限和裁切。打印 CSS 只有 A4 页面与少量 break-inside 规则,没有清理祖先高度/overflow、表格最小宽度、长单词换行与长叙事分页。PDF 操作此前被功能隐藏提交移除,并非底层打印 helper 缺失。
  • 修复:为 ready 报告建立独立 .personal-report-reader 滚动容器;将报告重构为 answer-first 的研究备忘录版式(封面快照、核心判断、D1 与关键证据双栏、主题解读、证据附录),使用既有中文正文字体、暖象牙纸张色和细分隔线,不引入卡片阴影或渐变。SVG 逐宫应用 transform 与 clipPath,密集星体最多显示七项加 +N 项。打印时恢复祖先自动高度和可见 overflow,重置表格 min-width/wrapper overflow,启用 fixed layout 与强制换行,并允许长主题自然分页;A4 @page 只随 ready 报告 route 注入,不再污染全站打印。恢复“打印 / 保存为 PDF”操作并保留不支持打印浏览器的能力保护;附录改用原生 details/summary,即使客户端 hydration 延迟也可展开,打印时始终显示内容。
  • 验证:个人报告聚焦测试覆盖 answer-first 顺序、十二宫坐标/裁切/密集数据、独立滚动边界、打印表格与分页、原生附录 disclosure、PDF 操作能力保护;浏览器在 1363×936 CSS viewport 实测报告容器 clientHeight=936scrollHeight=3466,滚动后 scrollTop=980 并可到达底部,附录展开后高度增至 5219;参考图与实现图完成同画布视觉比对。聚焦 ESLint、生产构建和 git diff --check 作为最终门禁重新执行。
  • 防复发:聊天根滚动锁与报告阅读滚动必须分层;报告回归测试不得删除 100dvh/overflow-y:auto 合同。任何命盘 occupants 改动必须验证至少两个不同宫位 transform、clipPath 与 12 项密集输入。打印回归必须覆盖长摘要、长主题、最小宽度表格和无滚动容器的 A4 输出;PDF 入口的隐藏/恢复必须同步更新明确的能力与 ready-state 合同。
  • 相关记录:BUG-126
  • 复发自:无
  • 修复版本:本地候选(未 push / deploy

BUG-154 | 个人报告错误绑定会话且创建请求同步阻塞

  • 状态:resolvedlocal candidate,待 staging gate/deployment
  • 首次发现:2026-08-09
  • 最近更新:2026-08-09
  • 影响面:个人报告入口、/reports 信息架构、POST/GET /api/reports、报告生成等待体验;报告证据合同、用户归属与每日限额不放宽。
  • 用户现象:只有进入一条已有消息的咨询 session 后才看得到“生成个人报告”,但实际报告使用的是账户出生资料而非该次对话;点击后 HTTP 请求会等待完整排盘与模型生成,用户必须停留并感知长时间阻塞,也没有集中查看历史报告的位置。
  • 触发条件:聊天页用 active session、消息数和临时 workflow receipt 控制报告 CTAPOST /api/reports 在插入 generating 记录后继续同步等待计算、模型生成、校验和 ready 写入。
  • 根因:产品入口沿用了最初的会话内 MVP,但生成主链实际构造 entryMode=direct_chart 并只读取认证用户的 canonical profile,会话只充当无意义的入口门槛。API 状态表已经支持 generating/ready/failed,却没有用响应后任务执行,也没有元数据列表端点与全局报告中心消费这些状态。
  • 修复:从聊天 header 删除 session-bound CTA,在全局侧栏加入“我的报告”并新增 /reports 报告中心;创建请求不再发送 sessionId,只携带报告身份字段。GET /api/reports 通过认证客户端/RLS 返回最近 20 条元数据,明确不返回正文与证据 hash。生产 POST 持久化 generating 后使用 Next.js 16 官方 after() 在响应后运行原有生成链并立即返回 202;完成/失败继续走原有 owner-scoped service 写入,意外异常也会落到稳定失败态。进程重启等中断留下的生成记录超过 15 分钟后会在下一次显式创建前安全回收为失败,避免永久占用单用户生成锁。中心与详情页每 3 秒轮询,用户可离开页面,现有 ready 报告可直接打开。
  • 验证:新增可执行回归证明 deferred 模式先返回 202 且行状态为 generating,执行任务后才转为 ready;个人报告、侧栏相关测试 185/185 通过;聚焦 ESLint、Next.js 生产构建(含 TypeScript、57 个静态页面、/reports 动态路由)和 git diff --check 通过。
  • 防复发:个人完整报告入口不得依赖 active session、消息数量或临时 workflow receipt;创建请求不得携带 chat text、出生明文或 sessionId。生产适配必须保留 response-after 调度与 202 合同,列表端点禁止返回 report_document、calculation/evidence hash。报告中心必须持续覆盖空、生成中、完成、失败和未登录状态。
  • 相关记录:BUG-126、BUG-153
  • 复发自:无
  • 修复版本:本地候选(待 staging push/gate

BUG-155 | 后台邮箱 OTP 登录后密码状态接口误报未登录

  • 状态:resolvedlocal candidate,待 staging gate/deployment
  • 首次发现:2026-08-10
  • 最近更新:2026-08-10
  • 影响面:独立后台域名的邮箱 OTP 登录、首次密码设置引导与 GET/POST /api/account/password;普通用户域名、未知 Host 拒绝和后台 RBAC 不放宽。
  • 用户现象:邮箱 OTP 校验成功并已创建 Better Auth session,但页面随后提示“暂时无法确认密码状态,请稍后再试”,密码状态接口返回 401“请先登录”。
  • 触发条件:浏览器在已配置的 admin origin 完成邮箱 OTP 登录后,用同一 host-only session Cookie 请求 /api/account/password
  • 根因:密码状态 route 在读取 session 前把身份 surface 硬限制为 user;admin host 虽然是已识别身份域名且持有有效 user session,仍被提前拒绝。前端把该非 2xx 响应映射成密码状态暂不可用。
  • 修复:密码状态 route 继续要求 self-hosted identity、已识别 Host 和有效 user session,但允许 useradmin 两个已配置 surface;未知 Host 仍返回 401Cookie 继续保持 host-only,不引入跨域会话共享。
  • 验证:身份集成回归新增 admin host OTP -> session -> GET /api/account/password,无密码账户必须返回 200 与 hasPassword=false;同一 Cookie 改投未知 Host 仍必须返回 401。
  • 防复发:共享 user identity session 的账户自助接口应校验“已识别身份 surface”,只有明确属于普通站的业务接口才限制 surface=user;任何 admin OTP 登录回归都必须继续检查登录后密码状态探测。
  • 相关记录:BUG-123、BUG-139
  • 复发自:无
  • 修复版本:本次后台 OTP 密码状态候选提交

BUG-156 | self-hosted 报告创建被未实现的数据库过滤器直接打断

  • 状态:resolvedlocal candidate,待 staging gate/deployment
  • 首次发现:2026-08-10
  • 最近更新:2026-08-10
  • 影响面:self-hosted staging 的 POST /api/reports;报告生成主链、用户归属、每日限额和 production 未放宽或改动。
  • 用户现象:合法的个人报告创建请求返回 {"error":"报告生成暂时不可用","code":"report_generation_failed"}
  • 触发条件:已登录用户调用报告创建接口,route 在进入报告创建核心逻辑前先回收超过 15 分钟的 generating 记录。
  • 根因:回收查询使用 Supabase 风格的 .lt("updated_at", staleBefore),但 self-hosted LocalPostgresQueryBuilder 只实现了现有的 .lte();运行时在构建查询时抛出 TypeError,被 route 的稳定错误边界统一映射为 report_generation_failed
  • 修复:复用本地 PostgreSQL 查询构建器已有的 .lte(),把边界定义为“更新时间小于或等于 staleBefore”;不新增过滤器 API。
  • 验证:个人报告 API 与入口聚焦测试 54/54 通过,静态回归锁定 route 使用 .lte();聚焦 ESLint 和 git diff --check 通过。
  • 防复发:self-hosted route 不得调用 LocalPostgresQueryBuilder 未实现的 Supabase 操作符;报告回收合同继续锁定 .lte(),数据库集成层已有 lte 查询覆盖。
  • 相关记录:BUG-154
  • 复发自:BUG-154staging 发布前遗漏 self-hosted 适配能力核对)
  • 修复版本:本次报告接口修复提交

BUG-157 | 侧栏“我的报告”按钮缺少 flex 布局导致图标与文字分离

  • 状态:resolvedlocal candidate,待 staging gate/deployment
  • 首次发现:2026-08-10
  • 最近更新:2026-08-10
  • 影响面:展开状态的全局侧栏“我的报告”入口;导航行为、折叠态 tooltip 与 accessibility 语义不变。
  • 用户现象:报告图标贴在按钮左上方,文字单独位于中间,整体未与上方“新对话”按钮对齐。
  • 触发条件:侧栏展开并渲染 .report-nav-button 的图标与文字。
  • 根因:样式设置了 justify-contentgap,但没有启用 flex formatting context,因此这些对齐属性不生效。
  • 修复:直接复用相邻“新对话”按钮的原生 CSS 布局,补齐 display:flex、水平/垂直居中和一致的水平内边距。
  • 验证:个人报告入口回归锁定 .report-nav-button 的 flex、垂直居中和水平居中;聚焦测试、ESLint 与截图检查通过。
  • 防复发:带图标和文字的侧栏按钮必须在同一 flex formatting context 中对齐;入口合同测试持续锁定关键布局属性。
  • 相关记录:BUG-154
  • 复发自:无
  • 修复版本:本次侧栏对齐修复提交

BUG-158 | 系统 Python 启动预检时重复使用不兼容解释器

  • 状态:resolved
  • 首次发现:2026-08-07
  • 最近更新:2026-08-07
  • 影响面:scripts/pre_work_check.py、开工预检、碎片扫描与聚焦治理测试
  • 用户现象:每次执行文档规定的 python3 scripts/pre_work_check.py ...,碎片扫描都会因 Python 3.9 不支持项目使用的 PEP 604 类型注解而退出,聚焦测试同时报 /usr/bin/python3: No module named pytest
  • 触发条件:macOS 的裸 python3 指向 /usr/bin/python3 3.9,而仓库 .venv 已安装 Python 3.11 与 pytest。
  • 根因:预检脚本把启动自身的 sys.executable 固定为全部子命令的解释器,没有验证项目声明的 Python >=3.11,也没有优先使用仓库虚拟环境;ERR-078 原防线只要求操作者手动改用 .venv,因此文档中的标准命令仍会稳定复发。
  • 修复:预检入口按确定顺序探测项目 .venv、当前解释器和可用的 Python 3.11+;只有同时满足 Python >=3.11 与 pytest 可用才执行全部 Python 子检查。报告显式记录选择结果和各候选探测信息;找不到兼容环境时失败闭合并给出安装指引。新增严格的 JYOTISH_PRE_WORK_PYTHON 覆盖入口,配置错误时不静默退回其他解释器。
  • 验证:tests/test_pre_work_check.py 覆盖虚拟环境优先、系统 3.9 跳过、严格 override 和 runtime failure 状态;使用裸 /usr/bin/python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45 完整通过,报告选择仓库 .venv/bin/python 3.11 且 focused governance tests 通过。
  • 防复发:预检不得再直接用未经探测的 sys.executable 启动项目脚本或 pytest;标准文档命令必须纳入真实系统 Python 启动回归,且 JSON 报告必须保留 python_runtime_ok 和实际解释器信息。
  • 相关记录:ERR-011、ERR-014、ERR-078
  • 复发自:无
  • 修复版本:本次同步提交

BUG-159 | consultation 报告漏挂 Ashtakavarga/KP 且 VedAstro raw response 晚于证据包生成

  • 状态:resolvedlocal candidate,待 staging gate/deployment
  • 首次发现:2026-08-10
  • 最近更新:2026-08-10
  • 影响面:个人报告 consultation workflow、Technique Audit、VedAstro official evidence、modules.ashtakavargamodules.kp_cusps
  • 用户现象:Technique Audit 将 Ashtakavarga、KP cusp 和官方 VedAstro 标成 blocked;即使已配置 API key,当前报告仍提示官方外部引擎未验证或不可用。
  • 触发条件:报告通过 consultation workflow 生成 machine evidence packet;本地排盘已有 planets/ascendant,或 VedAstro gateway 本次返回 official raw response。
  • 根因:consultation 本地补充层只挂入 Varga、Arudha 与 Narayana,未调用仓库已有的 Ashtakavarga/KP 计算;同时 VedAstro gateway 默认未请求 official full snapshot,且在 machine evidence packet 冻结后才执行,导致即使 key/endpoint/network ready 也没有本次 raw response 可进入报告审计。API key 只满足凭据配置,不证明本次调用、响应与证据传播成功。
  • 修复:复用现有 _compute_ashtakavarga_normalized_planets_from_body_compute_kp,把结果挂入既有 modules;让 gateway 入口显式请求 official raw snapshot,并将 gateway 调用提前到 evidence packet 生成前;仅在 official_verified 且存在本次 raw response 时合并 official evidence,缺 raw 时继续失败闭合。
  • 验证:consultation consumer/API 聚焦回归锁定 Ashtakavarga SAV、12 个 KP houses、两项 audit 为 used,以及 gateway 无需额外环境开关也会请求并传播 raw response 至 official_verified;相关聚焦测试与开工预检通过。
  • 防复发:本地计算器存在不等于已进入报告 modulesmachine evidence packet 不得在 official gateway 完成本次调用前冻结;official_verified 必须同时具备 closure state 与 raw response。
  • 相关记录:ERR-020、ERR-021、ERR-022、ERR-024、ERR-025、ERR-026、ERR-104
  • 复发自:无
  • 修复版本:待提交

BUG-160 | 管理端商品权益枚举未本地化且登录、套餐与充值返回状态出现前端闪烁或陈旧状态

  • 状态:resolvedlocal candidate,待真实登录态浏览器验收)
  • 首次发现:2026-08-11
  • 最近更新:2026-08-11
  • 影响面:管理端商品与权益列表/编辑表单、主站登录跳转、首页 composer、套餐页兑换码弹窗、支付后首页点数余额
  • 用户现象:商品类型、计费周期、权益类型和重置周期直接显示英文枚举;未登录进入 jyotisha.chat 时登录页接管前短暂出现“暂时无法进入 Jyotisha”;首页 composer 展示过多状态提示;套餐页默认打开兑换码弹窗;充值成功返回首页后仍显示旧点数。
  • 触发条件:管理端读取数据库枚举;首页并行账户/会话请求返回 401;composer 业务流程写入 notice;账户菜单携带 redeem=1 进入套餐页;支付页返回已存在的首页历史记录且该记录未按普通 pageshow 刷新账户。
  • 根因:商品与权益 UI 直接渲染数据库英文值;401 跳转后继续抛出普通错误并被首页 bootstrap 写入 accountErrorcomposer footer 集中渲染所有 notice;套餐页用 URL 参数初始化 redeemOpen,首页账户菜单默认写入该参数;余额同步事件只在套餐页当前窗口发出,而首页已卸载,首页又只在 BFCache pageshow.persisted 时刷新。
  • 修复:在管理端为商品类型、计费周期、权益类型和重置周期增加中文显示映射,API 原始值保持不变;401 使用 location.replace 和专用跳转错误,bootstrap/账户刷新忽略该跳转并保持 loading;删除 composer notice 可见渲染;移除 membershipHref 的 redeem 参数和套餐页 URL 自动开窗逻辑,保留用户手动兑换按钮;首页每次 pageshow 都重新读取账户与点数。
  • 验证:商品/会员/首页入口 66 条聚焦测试全部通过;本次 7 个目标文件 ESLint 通过;git diff --check 通过。完整 tsc --noEmit 仍被本任务外既有问题阻塞,包括缺少本地 boring-avatars 安装及既有报告、生产迁移和 staging workflow 测试类型错误,未把这些错误计入本修复通过条件。
  • 防复发:数据库枚举必须在管理 UI 显示边界映射,不得修改 API 真值;认证跳转不得进入普通错误页状态;对离开页面期间可能变化的账户余额,页面恢复必须主动重新读取服务端;兑换弹窗只能由明确用户操作开启。
  • 相关记录:BUG-010、BUG-090、BUG-123
  • 复发自:无
  • 修复版本:本地 staging 候选(未 push / deploy

BUG-161 | 咨询状态读取 503 与前台排盘在模型调用前超时

  • 状态:resolvedlocal candidate,待迁移与部署验收)
  • 首次发现:2026-08-11
  • 最近更新:2026-08-11
  • 影响面:GET /api/consult/status、首页前台咨询生成;个人报告与高严谨工作流继续保留完整外部证据链。
  • 用户现象:咨询状态接口返回“暂时无法读取咨询状态”;已发布且启用的模型仍无法开始聊天,咨询请求最终取消且没有生成回复。
  • 触发条件:self-hosted Web 以 service_role 直查启用 RLS 的 consultation_requests;前台咨询缓存未命中新星盘时,同步执行 VedAstro overview/full snapshot/range scan 与末尾 gateway,累计耗时超过 Web 请求时限。
  • 根因:这是两个独立故障。状态接口所用 service_role 缺少 consultation_requests 的表级 SELECT;聊天失败发生在模型调用前,排盘主链把可选外部交叉验证当作前台同步必经步骤,多个外部调用超时累计后触发前端 workflow 超时。
  • 修复:向前迁移仅授予 service_roleconsultation_requests 的 SELECT,浏览器角色仍无表权限;只有 /api/consult 传入 foreground 模式,Python 前台路径跳过 VedAstro main-entry overview 与 gateway,返回明确的 foreground_optional_evidence_deferred 降级状态并继续使用本地 D1、分盘、Arudha、Narayana、Ashtakavarga 与 KP 证据。个人报告保持默认完整工作流,排盘缓存键区分是否跳过外部 overview,避免快速结果污染完整报告缓存。
  • 验证:Python 聚焦回归覆盖 foreground 标志传播、overview/gateway 不执行、本地结果可回答且外部供应商不可用不致命;前端合同覆盖只有聊天传 foreground、报告不传;数据库合同覆盖 service_role 有 SELECT 且 anon/authenticated 无 SELECT。最终本地测试结果见本次任务记录。
  • 防复发:服务器直查启用 RLS 的表必须同时验证运行角色、表权限与策略;用户前台请求不得同步串联多个可选外部证据调用;快速排盘与完整证据排盘必须使用不同缓存键。
  • 相关记录:BUG-159、ERR-020、ERR-021、ERR-022、ERR-024、ERR-025、ERR-026、ERR-104
  • 复发自:无
  • 修复版本:本地 staging 候选(未 push / deploy

BUG-162 | 咨询工作流由路由预执行,Agent 无法真实编排 Skill 与服务器排盘工具

  • 状态:resolvedstaging candidate
  • 首次发现:2026-08-11
  • 最近更新:2026-08-14
  • 影响面:POST /api/consult 的个人/一般咨询、Mastra Agent、浏览器流式活动状态、咨询消息执行回执与计费结算;production 默认路径不变。
  • 用户现象:回答可以生成,但服务端在 Agent stream 前已经完成咨询工作流,Agent 只是复述结果;Web 只能按“有无正文”猜测状态,无法证明 Agent 实际加载 Jyotish Skill 或调用排盘工具。
  • 触发条件:咨询进入旧 runtime;route 直接执行 runConsultationWorkflow(),再把大段结果塞入 prompt,且仅返回 text/plain
  • 根因:编排权在 Next.js route,不在 Agent;个人 Agent 未绑定服务器排盘工具,Skill/tool 执行合同和公开事件协议均不存在。若直接透传 fullStream,还会泄露 reasoning、工具参数/结果、出生资料或 Skill 内容。
  • 修复:增加 legacy|canary|enabled runtime 开关;新路径由带 Jyotish Skill 的 Mastra Agent 调用请求级 run-jyotish-consultation,工具参数只允许 question/theme,出生资料始终由服务器上下文绑定,并以请求内 Promise 保证重复调用只计算一次。服务端把 fullStream 清洗为 NDJSON,仅公开安全的 run/Skill/tool/activity/answer 事件;Skill 和主工具合同未完成时最多重试一次,仍失败则释放预留点数且不保存成功消息。Web 按 content-type 保留 legacy text/plain 回滚路径,新路径只累积 answer.delta,收到 run.completed 后保存 workflow/execution receipt,并用服务器 activity 驱动状态 UI。浏览器断线后服务端继续完成互斥结算。2026-08-14 进一步加入 ConsultationPlan v2:服务端在真实点数预留前用最终 profile mode、精度边界和单次成本上限校验 plan;同一个 pre-reserve plan 原样传给 Agent 工具与 legacy workflow,模型提供的 question/theme 不能重建或改变 plan。Python 仅接受服务端 route allowlist 的完整 metadatapartial metadata 不得通过删除 plan_version 降级为 legacy,且 plan 只能收紧、不能授予精确应期权限。ConsultationPlan 在创建时对对象和嵌套数组执行深冻结,确保扣点后的 workflow 不能改写边界。模型 evidence packet 不再接收任意对象后依赖 denylist 清洗,而是分别对 natal_foundationdomaintimingvalidationevidence_contract 执行显式业务字段 allowlist 投影;主题 evidence 的 details 只选择批准字段,不整体透传,从结构上阻断出生、位置、账务、身份、权限和工具 payload 的别名绕过。
  • 验证:聚焦回归覆盖动态 Skill 工具、General Agent 无个人排盘工具、服务器绑定参数、并发幂等、unverified precise timing blocked、私有 chunk 过滤、任意 NDJSON 边界、合同完成前正文阻塞、失败不保存、真实 activity UI、execution receipt 持久化、legacy 流与断线结算合同;ConsultationPlan v2 的 TypeScript 定向测试 49/49、Python 合同与 API dry-run 测试 27/27 通过;回归覆盖 plan 与嵌套数组冻结,以及 reported_timelngutc_offsetaccount_balanceentitlementsaccess_scopes 六类恶意嵌套别名不进入模型 packet,同时保留本命、Shadbala、Ashtakavarga、Dasha、Narayana 与 evidence status。目标 ESLint、Python py_compilegit diff --check 通过;全量 tsc --noEmit 仅保留与本任务无关的既有 migration fixture 字段和 staging workflow 正则 target 错误。
  • 防复发:个人咨询不得在 Agent stream 前直接执行主 workflow;不得把出生资料放入模型工具参数;公开流不得包含 reasoning、provider metadata、工具输入/结果或 Skill 正文;只有 run.completed 可进入成功消息持久化,步骤最多 32 个,计算与结算都必须请求内幂等。版本化 plan metadata 必须 all-or-none,扣点前校验的 plan 必须作为同一不可变对象进入实际 workflow;required_layers/claim_boundary 只表示 allowlist 合同,不能冒充 layer 已执行或 evidence gate 已通过。
  • 回滚:仅将 staging 的 CONSULTATION_AGENTIC_RUNTIME 设为 legacy;无需回滚数据库或修改 production。
  • 相关记录:BUG-161
  • 复发自:历史咨询 runtime
  • 修复版本:本次 staging 候选提交

BUG-163 | 生时校正入口没有服务端 Case 状态,复用第一条校正 Session 且并发会重复创建

  • 状态:resolvedlocal candidate;真实 PostgreSQL fixture 待远端 runner 执行)
  • 首次发现:2026-08-12
  • 最近更新:2026-08-12
  • 影响面:生时校正首页入口、侧边栏校正 Session 恢复、Case/Session 绑定、并发打开、旧 Direct Agentic 数据映射
  • 用户现象:点击首页“生时校正”时,只要历史存在任意校正 Session 就进入“继续”,不管它是进行中、已采用、已确认还是已关闭;侧边栏点击某条校正 Session 可能被复用逻辑带向另一条记录;双击或多标签并发点击可能创建多组 Case/Session;已完成校正 Session 还会阻止用户重新开始。
  • 触发条件:使用 hasRectificationSession + sessions.find(sessionType === "birth_time_rectification") 的客户端猜测路径;历史 Direct Agentic Session 没有 V9 Case 状态。
  • 根因:旧入口把“存在任意校正 Session”当作“可继续”,把客户端数组第一条校正 Session 当作权威;没有服务端 Case 状态机(resumable/terminal 由消息数、是否有候选推断),Session 与 Case 没有数据库级双向绑定与精确恢复,open/create 没有 requestId 幂等账本和同用户串行化,旧 Agentic 数据也没有一次性的 Case 回填与结果映射。
  • 修复(V9 Agentic Domain lane):
    1. 新增独立 Skill skills/jyotish-birth-time-rectificationSKILL.md ≤200 行 + 5 个 references),方法源只在 Skill,系统提示词不再复制完整方法。
    2. 新增 V9 领域 contractsCase 状态机(10 状态 + resumable/terminal 谓词 + 单向终态)、Evidence kind/date precision/append-only lineage、public receipt/activity allowlist、open request/response schemassupersedeActive=true 被 schema 与 RPC 双重禁止。
    3. 新增向前业务迁移 20260812010000_agentic_rectification_v9_runtime.sql(只进 frontend/supabase/migrations,不进 frontend/db/migrations):agentic_rectification_cases(每用户最多一个 resumable 由 partial unique index 强制;Case/Session 双向一致由触发器+RPC+唯一索引保证)、agentic_rectification_evidence(原文 grounding、日期精度、revision lineage、confirmed 只能由服务器确认路径产生)、agentic_rectification_turnspending/completed/failed/retryable,禁止 reasoning)、agentic_rectification_tool_receipts(只存 fingerprint/phase/tool/status)、agentic_rectification_open_ledgerrequestId 幂等);agentic_rectification_results 前向扩展 case_id/evidence_ledger_fingerprint/candidate_range_fingerprint/skill_version;所有新表 RLS + 仅 service_role 授权,全部 RPC security definer + search_path='' 且仅 service_role 可执行;浏览器不传 userId、出生快照、range 或权限决定。
    4. 一次性幂等 legacy backfill(迁移内执行,应用启动不隐式批量跑):engine_confirmed→confirmed、user_accepted only→candidate_accepted、有消息无选择→collecting_evidence/candidate_ready、重复空/旧→abandoned/superseded、每用户只保留最新实际 active(窗口函数+部分唯一索引),旧文本绝不生成 confirmed evidence;提供 backfill_agentic_rectification_legacy_cases() 与可复跑的 verify_agentic_rectification_backfill()
    5. 新增 Case Service + 五个 APIPOST /api/rectification/cases/openhomepage resume-or-create / session 精确恢复 / new 安全冲突)、GET entry-summary(首页 CTA 真值)、GET cases/[caseId](脱敏投影,绝不返回 baseline_birth_snapshot)、POST closePOST upgrade-skill;profile 不完整不建案;错误不泄露身份、出生资料或 DB 原文。
  • 验证:新增 rectification-v9-contracts.test.ts12)、rectification-v9-case-service.test.ts14)、rectification-v9-migration.test.ts(17 静态合同)本地共 43 项全部通过;rectification-v9-database.test.ts(真实 PostgreSQL 迁移 apply/re-apply、open 原子+幂等+resume、profile 门禁、所有权、terminal 只读、evidence 生命周期、backfill 状态分布/单 active/结果映射/幂等)在本机因无 Docker 按环境 skip(5 项),待远端完整 runner 执行;既有生时校正相关测试 60/60 通过;tsc --noEmit 对本 lane 文件零错误(全量 6 个既有错误全部位于未触碰文件);目标 ESLint 0 error 0 warninggit diff --check 通过;database-local-business.test.ts 精确 public 表清单同步新增 5 张 v9 表并断言新迁移 appliedBUG-127/144 防复发)。
  • 防复发:Case 状态、resumable/terminal、shouldStartOpening 必须只由服务端决定;侧边栏必须以精确 sessionId 恢复并校验所有权;open/create 必须走 requestId 幂等账本 + 同用户 advisory 串行化;新增 self-hosted 业务表必须同时更新全迁移 applied ledger 与精确 public 表集合,且只进 frontend/supabase/migrations;新页面/新 API 必须同步能力审计;浏览器任何入参都不允许携带 userId、出生资料、range 或权限决定。
  • 相关记录:BUG-004、BUG-027、BUG-033、BUG-067、BUG-068、BUG-069、BUG-072、BUG-074、BUG-085、BUG-086、BUG-095、BUG-112、BUG-113、BUG-114、BUG-115、BUG-116、BUG-117、BUG-118、BUG-119、BUG-120、BUG-121、BUG-127、BUG-143、BUG-144
  • 复发自:无(新 Agentic Domain 主链;旧防线缺失的直接原因见下)
  • 修复版本:本地 staging 候选(未 push / deploy

后续推进(2026-08-13 · stream agent execution + entry routing lane

  • 状态:resolvedlocal candidate;真实 PostgreSQL fixture 待远端 runner 执行)
  • 最近更新:2026-08-13
  • 现象:旧 /api/rectification/agent 仍消费 textStream,客户端 history 可覆盖服务端历史,maxSteps=8 全局固定,billing identity 是 sessionId;旧工具让模型传 candidate_range/events[]confirmedGate 在进程内;UI 的“思考状态”不能证明 Skill/工具真的执行过,候选卡从 Agent 文本或隐藏 sentinel 解析。
  • 根因:第一轮 V9 runtime 落地了 Case/Evidence/Turn/Receipt 表与 open API,但 agent 执行层仍是旧 Direct Agentic 传话筒:没有 durable turnpending→completed)、没有 fullStream→allowlist NDJSON 映射、没有 Case-ref 工具、没有 Skill 真实加载证据、没有 DB 驱动的 runtime selector。
  • 修复:
    1. 重构 agentic-rectification.ts:系统提示词压缩为约 25 行高优先边界(不再复制 gate→scan→score→diagnostics);固定加载 skills/jyotish-birth-time-rectificationmaxSteps 按 action 有界(opening/read-only 6、evidence 8、rescore 12、accept/confirm 6+ 硬上限 16 + 重复工具调用检测(>3 次相同调用中止)。
    2. 新增十个 Case-ref 工具(rectification-v9-tools.ts):read-case / propose-evidence / confirm-evidence / revise-evidence / compare-candidates / read-diagnostics / offer-candidates / accept-candidate / confirm-birth-time / close-caseinput 只含 caseId/sourceTurnId/evidenceId/resultId/candidateId/quote/proposedKind 等最小引用,绝不接 userId/出生资料/range/events[]/分数/权限开关;每个工具走 RLS 仅 service_role 的 RPCevidence ledger、指纹缓存、receipt、幂等);acceptedconfirmed 由 RPC 分列,confirm 需要 confirmation gate + 用户原话 consent quote 原文匹配;candidate 相同 evidence/range/engine 指纹复用缓存。
    3. /api/rectification/agent 重写为 Case-ref API:请求仅 caseId/sessionId/requestId/action/message;服务端验证 Case↔Session exact binding 与 chat_sessions.agentic_rectification_case_id;客户端 history 不再进入上下文,每轮先从持久化 dossier 读取 turns/evidence;先原子 pending Turn,成功才 completed(半截文本永不成为 settled history);消费 result.fullStream 并按 allowlist 输出 NDJSONrun.started/skill.started/skill.loaded/case.loaded/evidence./candidates./diagnostics.completed/candidate.accepted/birth_time.confirmed/answer.delta/run.completed/run.failed),reasoning/raw/provider metadata/tool args/results/出生资料/评分/DB 错误永不透传;skill 加载证据来自真实框架(agent.getSkill 预校验 + fullStream skill tool-call/tool-result),首轮无 skill.started/loaded 允许一次受控重试,仍失败则 Turn failed/retryable 并释放用量;billing identity 绑定 rectification:case:{caseId}opening/read-only 不扣,首次实质运行预留一次,恢复/重试不重复;断线 abort 传播释放。
    4. 新增向前业务迁移 20260813010000_agentic_rectification_v9_agent_api.sqlcase dossier / case compute(含 baseline 但不向模型暴露)/ turn finalize / candidate persist(指纹缓存) / case-scoped accept / consent-gated confirm / guarded transition / needs_rebaseline profile guard trigger / agentic_rectification_run_phases(持久化 skill.started/loaded 等 phases/ turn receipt 读取;rectification_runtime_version feature flagpublished、100%config version=v9 legacy_mode=readonly),DB 驱动 selector,两个 runtime 不可能同时写 profile/扣费/confirm。
    5. 前端:page.tsx 删除 hasRectificationSessionsessions.find(sessionType==='birth_time_rectification');新增 openRectificationFromHomepage/openRectificationSession(sessionId)/startNewRectification,全部请求服务端 Case open API 并使用返回的 exact sessionId/caseId;首页 CTA 由 entry-summary 驱动(开始/继续上次/再次校正);侧边栏校正 Session 点击走 intent=session + 精确 sessionIdterminal 只读 + “再次校正”);shouldStartOpening 只来自服务端;聊天组件改为 caseId/sessionId/readonly/shouldStartOpening 初始化并从持久化 Turns 恢复,候选卡来自 Candidate Snapshot API,活动展示来自真实 NDJSON + 持久化 receipt(可折叠“本轮做了什么”,不显示 reasoning),删除本地 timer 模拟与隐藏 sentinel。
    6. 新增 GET cases/[caseId]?sessionId= 返回持久化 turns/evidence/latest_result/逐轮 receipt 支持刷新恢复;新增 POST cases/[caseId]/candidates/accept(UI 候选卡直连,不扣费、幂等、case-scoped)。
  • 验证:新增 rectification-v9-entry-routing.test.ts13)、rectification-v9-evidence.test.ts11)、rectification-v9-agent.test.ts12)、rectification-v9-stream.test.ts9)、rectification-v9-status-security.test.ts11);rectification-v9-migration.test.ts 扩 9 项静态合同;rectification-v9-database.test.ts 扩 agent API 迁移/flag/consent 测试(本机无 Docker 按环境 skip);重写 rectification-agentic-entry.test.ts(旧测试锁死了 hasRectificationSession + sessions.find 与 maxSteps=8、textStream、sessions.find 复用等错误行为,全部改为断言 V9 服务端路由);consultation-entrypoint.test.ts / application-billing-contract.test.ts 的 rectification 段同步为 caseId 绑定契约。本机运行:v9 聚焦 107 通过 / 0 失败 / 6 Docker skip;完整 frontend 套件 1231 通过 / 18 失败(全部为无 Docker/PostgreSQL 的环境类既有失败,基线 20 失败,本轮未新增环境失败);tsc --noEmit 仅剩 6 个既有测试文件错误(均未触碰);ESLint 0 errorgit diff --check 通过。
  • 防复发(补充):任何新 rectification 前端逻辑不得再根据消息数/候选存在/session 排序推断 Case 状态;/api/rectification/agent 只能消费 fullStream 并输出 allowlist NDJSON;工具输入 schema 必须 strict 且只含最小引用;首轮必须保留真实 skill 加载证据;billing request identity 只能绑定 caseIdDB 驱动 feature flag 是 runtime selector 的唯一来源。
  • 修复版本:本地 staging 候选(未 push / deploy

旧防线为何没拦住(hasRectificationSession + sessions.find()

  • hasRectificationSession 只断言“存在任意校正 Session”,不区分 draft / collecting / candidate_ready / candidate_accepted / confirmed / closed,因此已完成会话始终被当作可继续,且没有服务端 Case 状态可被测试断言。
  • sessions.find(sessionType === "birth_time_rectification") 取客户端数组第一条,既不保证精确 sessionId,也不校验 Case 绑定;排序、缓存或刷新差异都会改变打开哪条记录。
  • 旧测试只覆盖“能找到一条校正 Session”的客户端行为,没有服务端 Case 状态机、没有“点击指定 Session 必须精确恢复”的契约、没有并发/双击/多标签幂等断言,也没有 legacy→V9 一次性回填的数据库级验证,因此这些缺陷在回归中被遗漏。

BUG-164 | V9 红队审查:引擎适配器契约不匹配 + accept 重放幂等顺序错误

  • 状态:resolved(已修复;真实 PostgreSQL 17 与真实 Python 引擎双重实证)
  • 首次发现:2026-08-11(红队审查 60e2ce4f..724fb64c
  • 最近更新:2026-08-11
  • 影响面:V9 候选比较管线(compare-candidates / read-diagnostics)、候选采用重放幂等、确认重放返回字段
  • 现象:
    1. rectification-compare-candidates 在生产面对真实 Python 引擎必然失败:前端 engine-client.ts 假设 /api/rectification/v5/score 返回 rank/tied_minute_count/representative_time/confidence/margin_percent/selection_allowed/confirmation_allowed,实际引擎只返回 candidate_scores:[{time,score,supporting_event_ids,conflicting_event_ids}]scripts/rectification/api_service.py:score_candidates,已用真实 HTTP 请求实证);同时 V9 evidence kind/domaineducation_start/career_entry/promotion/relationship_commitment/finance_gain 等)直接透传给引擎,而引擎 SCOREABLE_EVENT_KINDS 只有 education_milestone/relocation/relationship_start|change/career_change/finance_change/self_health_event(health_pressure),几乎全部 400。结果:所有候选比较必然 engine_no_candidatesengine_http_errorV9 无法产出任何候选。
    2. accept_agentic_rectification_candidate_for_case 的幂等重放分支位于 profile 基线校验之后;第一次 accept 写入 active_birth_time 后,基线快照与 profile 必然分歧,重放/双击/断线重试返回 candidate_profile_changed 而不是 idempotent=true(旧 accept_agentic_rectification_candidate 是先重放后校验,语义回归)。
    3. confirm_agentic_rectification_birth_time 幂等分支在 v_result 载入前引用 v_result.idconfirmed 重放响应的 result_id 恒为 null。
  • 触发条件:任何进入 compare-candidates 的真实运行;accept 成功后同一候选再次 acceptconfirmed 后同一 confirm 重放。
  • 根因:
    • 新增 engine-client.ts 从未与真实引擎做契约测试(既有测试全部 mock runV9CandidateScore,未覆盖真实响应形状);V9 evidence 领域模型与引擎粗粒度评分词汇之间缺少 kind/domain 翻译层。
    • accept/confirm RPC 的重放语义被基线保护逻辑错误地前置/后置,未对齐旧实现的先重放后校验顺序。
  • 修复:
    1. engine-client.ts 对齐真实引擎契约:新增 toEngineScoreableEvent kind/domain 翻译(education_→education_milestone、career_→career_change、relationship_start/commitment→relationship_start、relationship_separation→relationship_change、relocation→relocation、finance_*→finance_change、self_health_event→health_pressurefamily/other 留在账本但不再进引擎);readCandidatestime+score 按分数降序推导 rank、同分 tied_minute_count、相对支持度归一化;representative_time=top1selection_allowed=有候选;confirmation_allowed 只来自引擎 can_confirm_exact_minute(真实引擎当前为 false,confirm 门诚实关闭);margin_percent 取自 diagnosticsconfidence 由 margin+retention 推导;无 scorable 事件/无候选时 fail-closedno_scorable_evidence/engine_no_candidates)。
    2. 20260813010000_agentic_rectification_v9_agent_api.sqlaccept 重放分支移到 profile 基线校验之前,重放分支校验 profile 与已采用时间一致(对齐旧语义);confirm 幂等分支补 select id into v_result.idresult_id 不再为 null。
    3. 移除 rectification-v9-tools.ts compare-candidates 中双分支同 throw 的死代码。
  • 验证(真实执行,非 mock):
    • 本机安装 PostgreSQL 17brew),按 deploy/postgres/001-bootstrap-roles.sh 建角色,从空库全量应用 95 个迁移 ×2second run 95 already applied--check exit 0),含两个 V9 迁移。
    • 真实 SQL 行为:open homepage create/resume、requestId 幂等、session 精确恢复、跨用户 404、new 冲突、profile_incomplete、evidence quote grounding/idempotent/confirm/revision lineage、terminal 只读、accept→accepted + 重放 idempotent=true(修复后)、confirm gate=false blocked + gate=true 且 consent 原文匹配 → confirmed + 重放 idempotent + result_id 非空、指纹缓存复用、profile 变更 → needs_rebaseline + 候选 invalidated、legacy backfillconfirmed/candidate_accepted/superseded/abandoned 分布、results_mapped=3、重跑 cases_created=0、verify 0 conflict/0 orphan)、RLS service_role-only 均实证通过。
    • 真实 Python 引擎:scripts/jyotish_api_server.py 启动后,修复后 engine-client 直连 /api/rectification/v5/score + /v5/diagnostics 成功产出候选(rank/relative_support/tied)、family_event 被排除、confirmation_allowed=false、diagnostics 键完整映射。
    • 新增 rectification-v9-engine-contract.test.ts(9 项,含真实引擎响应形状 fixture)与 migration 顺序静态回归 1 项;v9 聚焦 33+108 全部通过;全量 npm test 12561232 pass / 18 Docker ENOENT 环境失败,与基线 6d7a9a97 同因,worktree 实证);tsc --noEmit 仅剩 6 个既有未触碰测试文件错误;ESLint 0 errornext build 通过;git diff --check 通过。
  • 防复发:引擎适配器必须有真实响应形状的契约测试;新增任何映射层必须对照 scripts/rectification/contracts.py:SCOREABLE_EVENT_KINDS;RPC 重放/幂等语义以“先重放后校验、重放校验已落库状态”为唯一实现顺序;迁移修改必须在真实 PostgreSQL 上从空库全量应用并重跑。
  • 相关记录:BUG-163、BUG-112
  • 修复版本:本地 staging 候选(未 push / deploy

BUG-165 | 合并咨询 Agentic runtime 后生时校正执行步骤复用共享 activity 字段导致构建失败

  • 状态:resolvedlocal candidate,待 staging gate
  • 首次发现:2026-08-11
  • 最近更新:2026-08-11
  • 影响面:RectificationAgenticChat、共享 ChatMessageView、Next.js production type-check
  • 用户现象:生时校正与最新 staging 的咨询 Agentic runtime 单独测试均通过,但合并后 next buildrectification-agentic-chat.tsxstring[] 不能赋给结构化 AgentActivityView
  • 触发条件:将 V9 生时校正分支合入包含咨询 Agentic runtime 的最新 origin/staging,再运行 production build。
  • 根因:并行开发期间,共享 ChatMessageView.activity 从通用状态扩展为咨询 runtime 使用的单个 { phase, label };生时校正把执行回执的多步骤 string[] 也命名为 activity。聚焦源码/运行测试没有跨两个 runtime 做 TypeScript 组合检查,只有 production build 暴露交叉类型冲突。
  • 修复:生时校正的持久化/流式回执步骤改为独立 receiptActivity: readonly string[],只用于折叠的“本轮做了什么”;共享 activity 继续由咨询 runtime 独占并保持 { phase, label } 合同,不改其 UI 与状态映射。
  • 验证:合并最新 staging 后,rectification-v9-streamrectification-agentic-entrychat-stream-layout 共 36/36 通过;目标 ESLint 通过;Next.js 16.2.10 production build 完成 type-check、60 个静态页面生成与 route 列表收口;git diff --check 通过。
  • 防复发:生时校正执行回执与咨询即时 activity 必须使用不同字段;任何同时修改 ChatMessageView 与专用聊天 surface 的分支,合并最新 staging 后必须运行共享 layout 测试和 production build,不能只依赖各自聚焦测试。
  • 相关记录:BUG-162、BUG-163、BUG-164
  • 复发自:无(并行 runtime 合并冲突)
  • 修复版本:本次 staging 候选

BUG-166 | V9 Docker DB 测试在 staging gate 失败:service 连接池未按 URL 关闭 + fixture 种子角色越权

  • 状态:resolved(本地 Docker PostgreSQL fixture 复跑通过,待 Gitea gate
  • 首次发现:2026-08-11Gitea staging gate run 1732
  • 最近更新:2026-08-11
  • 影响面:frontend/tests/rectification-v9-database.test.tsfrontend/src/lib/db/local-postgres-client-core.ts 的按 URL 连接池生命周期
  • 用户现象:staging gate 的 Docker DB 套件恰好三个 V9 测试失败。
  • 触发条件:在 CIDocker fixture)中运行 rectification-v9-database.test.ts
  • 根因(脱敏):
    1. 两个测试创建了 createLocalPostgresDataClient 的 service 客户端后没有关闭其全局按 URL 缓存的连接池;fixture.stop() 先销毁 PostgreSQL,池的异步终止随后触发 57P01terminating connection)类报错。不能全局关闭所有池(node:test 的 DB 用例可能并发),必须按各自 service URL 关闭,且任何情况下 fixture.stop() 都要执行。
    2. 最后一个 “v9 agent api migration…” 测试用 identity_runtime 角色向 public.profiles/chat_sessions/agentic_rectification_cases/agentic_rectification_turns 播种 fixture 行;生产最小权限正确拒绝了这些写入。该用例还重复插入由 identity→auth→profile 触发器已自动创建的 profile,把 JavaScript .repeat() 写进 SQL,并错误期待未 grounded 的 consent quote 进入候选查询。
  • 修复:
    1. 审查并保留新增的 closeLocalPostgresDataPool(connectionString):先按 key 从全局缓存删除再 pool.end(),未知 key 与重复关闭均为安全 no-op,删除后再注册同名 key 可创建新池,与全局 closeLocalPostgresDataPools 并发/先后调用无冲突(end 幂等)。
    2. rectification-v9-database.test.ts 中所有创建 service 客户端的测试(open / profile gating / evidence / backfill / agent api,共 5 个)在 finally 中先 await closeLocalPostgresDataPool(service_url)fixture.stop(),嵌套 try/finally 保证池关闭失败时 fixture 仍会停止;纯迁移测试无需关闭。
    3. 最后一个测试先经合法的 identity_runtime 播种 identity.users,再由 postgres admin 更新触发器自动创建的 profile 并播种其它 public.* 行;同时改用模板中的既有 fingerprint,并按 RPC 真实合同断言 consent grounding 失败。保留生产最小权限,不改任何 grant、不改迁移。
    4. 新增非 Docker 单测 tests/local-postgres-pool-close.test.ts4 项):未知 key no-op、按 key 关闭互不影响、关闭后同 key 可重建、全局关闭后再按 key 关闭 no-op。
  • 验证:local-postgres-pool-close.test.ts 4/4 通过;本地 Docker PostgreSQL fixture 的 rectification-v9-database.test.ts 6/6 通过;目标 ESLint 0 errorgit diff --check 通过。Gitea gate 仍需对最终提交复跑。
  • 防复发:任何测试创建本地数据客户端必须在 fixture.stop() 之前按自身 service URL 关闭连接池;测试不得用 identity_runtimepublic.* 播种 fixture 行(一律走 postgres admin 的 fixture.psql);禁止为测试放宽运行时 grant 或改迁移。
  • 相关记录:BUG-163、BUG-164、BUG-165
  • 修复版本:本地 staging 候选(未 push / deploy

BUG-167 | 管理端账号重置重复要求邮箱验证码

  • 状态:resolved(本地验证通过,待 staging 部署验收)
  • 首次发现:2026-08-11
  • 最近更新:2026-08-11
  • 影响面:管理端用户列表“重置资料与会话”弹窗与 POST /api/admin/customers/reset
  • 用户现象:已登录且具备账号管理权限的管理员执行账号重置时,仍需额外发送并输入邮箱验证码,增加不必要的操作步骤。
  • 触发条件:在用户列表打开重置弹窗并提交账号重置。
  • 根因:账号重置复用了面向角色变更、账务调整等操作的 requireHighRiskAdminMutationreauthPermission,把一次性邮箱复核错误扩展到了已有登录、权限、原因、可信来源及数据库审计保护的重置路径。
  • 修复:该路由改用既有 requireAdminMutation(request, "admin.users.manage_roles"),前端仅移除该弹窗的 reauthPermission;保留管理员会话、权限、可信 Origin、确认字面量、操作原因、数据库权限检查与审计日志。其他高风险管理员操作继续使用邮箱复核。
  • 验证:账号重置合同测试确认前后端均不再要求邮箱复核,同时管理员角色变更仍使用 requireHighRiskAdminMutation;管理端 Origin 与授权边界的既有测试继续覆盖未登录 401、缺少权限/不可信来源 403
  • 防复发:账号重置只允许使用普通管理员 mutation guard;共享高风险 guard 不做全局放松,并由合同测试锁定角色变更仍需邮箱复核。
  • 相关记录:无
  • 修复版本:本次 staging 候选

BUG-168 | 生时校正运行时开关被 RLS 静默隐藏并误报服务未开放

  • 状态:resolved(本地回归已通过,待 staging 迁移与真实接口验收)
  • 首次发现:2026-08-11
  • 最近更新:2026-08-11
  • 影响面:POST /api/rectification/agent、共享 loadRuntimeFeatureFlags 的运行时开关读取,以及管理端 feature flag 列表。
  • 用户现象:staging 的 V9 生时校正接口返回 503 rectification_runtime_disabled,即使数据库中的 rectification_runtime_version 已是 publishedenabled=truerollout_percentage=100
  • 触发条件:self-hosted Web 通过 ADMIN_DATABASE_URLadmin_runtime 查询启用了 RLS 的 public.feature_flags
  • 根因:20260806050000_operations_feature_flags.sqladmin_runtime 授予了表级 SELECT,但启用 RLS 后没有创建对应 SELECT policy。PostgreSQL 因此不报权限错误而是返回零行;loadRuntimeFeatureFlags 将缺失记录安全降级为 disabled,路由遂返回“生时校正服务暂未开放”。
  • 修复:新增向前迁移 20260811030000_feature_flags_admin_runtime_read_policy.sql,保留最小 SELECT grant,并为 admin_runtime 创建 feature_flags_admin_read RLS SELECT policy;不放宽匿名、普通用户或其它运行时角色权限。
  • 验证:Docker PostgreSQL 回归先在修复前稳定得到空结果,新增迁移后要求 admin_runtime 能读取 true:100:published;staging 还需验证迁移账本、角色可见性及真实 Agent 接口不再返回 runtime disabled。
  • 防复发:任何对启用 RLS 的表新增 runtime grant 时,必须同时测试对应运行时角色的真实可见行,而不能只断言 has_table_privilege=truefeature flag 种子测试必须以 Web 实际使用的 admin_runtime 读取。
  • 相关记录:BUG-151、BUG-166
  • 修复版本:待提交

BUG-169 | V9 新建生时校正 Session 未绑定模型导致 Agent 立即返回模型不可用

  • 状态:resolved(本地候选)
  • 首次发现:2026-08-11
  • 最近更新:2026-08-11
  • 影响面:V9 open_agentic_rectification_case 新建会话、既有 model_id is null 的生时校正会话,以及 POST /api/rectification/agent 的模型解析。
  • 用户现象:运行时开关恢复后,原请求继续返回 409 模型暂不可用;请求体包含有效 modelId,管理端模型及供应商也均为 published/enabled。
  • 触发条件:V9 Open Case RPC 原子创建 birth_time_rectification Session 后立即发送 opening。
  • 根因:RPC 插入 chat_sessions 时没有写入 model_id;前端仅在内存中把目录默认模型显示为当前选择,而 Agent 路由按安全合同只解析服务端持久化的 chatSession.model_id/model_config_version,不会信任请求体覆盖会话模型。
  • 修复:新增向前迁移 20260811040000_rectification_session_default_model.sql;数据库触发器为缺少模型的生时校正 Session 绑定当前 published/enabled 默认模型,由既有 pin trigger 固定配置版本,并一次性回填同类历史 Session。普通咨询 Session 与已有明确模型选择均不改变。
  • 验证:V9 PostgreSQL fixture 在创建 Case 前播种默认模型,要求 RPC 新建 Session 后持久化为 v9-default-model:1;staging 还需核对迁移账本、原 Session 回填结果和真实 opening 请求。
  • 防复发:任何服务器端创建 birth_time_rectification Session 的路径都必须在同一事务内得到可解析的持久化模型与版本;前端显示的默认模型不能替代数据库绑定。
  • 相关记录:BUG-060、BUG-163、BUG-168
  • 修复版本:待提交

BUG-170 | V9 Agent 开场未收到 Case ID 导致工具调用失败

  • 状态:resolved(本地候选)
  • 首次发现:2026-08-11
  • 最近更新:2026-08-11
  • 影响面:POST /api/rectification/agent 的 opening、message、read-only Agent 消息,以及所有要求 caseId 的 V9 rectification 工具调用。
  • 用户现象:接口已返回 200 application/x-ndjsonSkill 与 Case 均已加载,但回答正文报告 invalid_case_id,最后事件为 run.failed
  • 触发条件:Agent 按指令调用 rectification-read-case,但服务端构造的 Agent 消息没有提供当前 Case ID。
  • 根因:请求路由和 runV9AgentTurn 已验证 Case/Session 绑定,但 buildAgentMessages 只传时间与用户消息;模型只能猜测工具所需的 caseId
  • 修复:在共享 Agent 消息构造处加入服务端已验证的唯一 Case ID,并明确所有 rectification 工具必须原样使用;不放宽 UUID、所有权或 Case/Session 绑定校验。
  • 验证:新增回归测试捕获实际传给 Agent 的 opening 消息,要求包含精确服务端 Case ID;staging 需以原请求确认流以 run.completed 结束。
  • 防复发:任何由模型调用、但值由服务端拥有的工具引用,都必须在 Agent 上下文中显式提供,不能要求模型猜测。
  • 相关记录:BUG-163、BUG-169
  • 修复版本:待提交

BUG-171 | V9 免费开场生成成功后仍执行 usage settlement 导致 run.failed

  • 状态:resolved(本地候选)
  • 首次发现:2026-08-11
  • 最近更新:2026-08-11
  • 影响面:POST /api/rectification/agentopeningread_only 免费 Turn,以及回答完成后的 Turn 最终状态与 assistant message 持久化。
  • 用户现象:Agent 已加载 Skill 与正确 Case,并输出完整回答,但 NDJSON 最终事件仍为 run.failed;数据库 Turn 为 retryable 且没有持久化 assistant message。
  • 触发条件:免费 openingread_only Turn 正常生成回答并进入成功收尾。
  • 根因:路由的 billing.reserve() 对免费 Turn 不创建 usage_reservations,但 billing.complete() 仍调用 complete_usage;数据库因找不到 reservation 返回 request_missingAgent runner 将已成功回答降级为 usage_settlement_failed
  • 修复:免费 Turn 在 settlement adapter 中直接成功返回;只有 message Turn 才执行 reservation 与 settlement,保留原有付费消息的计费、幂等与失败保护。
  • 验证:staging 数据库确认目标 Case 没有 usage reservation,直接调用同一结算函数稳定返回 request_missing;新增合同回归要求免费 Turn 同时绕过 reservation 与 settlement。部署后需以真实 opening 确认最终 run.completed、Turn completed 且 assistant message 已持久化。
  • 防复发:任何声明为免费的 Agent action 必须在授权和结算两个阶段保持同一策略,不能只跳过预授权而继续结算。
  • 相关记录:BUG-163、BUG-168、BUG-169、BUG-170
  • 修复版本:待提交

BUG-172 | V9 执行记录顺序与技法展示失真,Agent 缺少完整出生地时区上下文

  • 状态:resolved(本地候选)
  • 首次发现:2026-08-12
  • 最近更新:2026-08-12
  • 影响面:V9 生时校正 Activity、SSE/Turn receipt、rectification-read-case、Profile baseline 与结果失效链。
  • 用户现象:Agent 消息下方出现无样式的白底黑字执行记录,并固定显示“开始本轮执行、正在加载专用方法、专用方法已加载、本轮完成”等泛化文案;界面没有展示引擎实际使用的分盘、大运或行运,也无法确认 Agent 是否收到生日、出生地、精确经纬度与时区。
  • 触发条件:打开或继续 V9 生时校正 Case,渲染已持久化 Turn receipt,或让 Agent 调用 rectification-read-case
  • 根因:前端从 receipt phases 重建固定步骤且 <details> 没有专用样式;read-case 只返回 Case/证据摘要,没有合并服务端 Profile compute context;引擎结果与公开 receipt 之间也没有安全、可验证的技法 provenance 字段。
  • 修复:Activity 移到对应 Agent 消息上方,使用现有颜色、间距、圆角与键盘焦点样式;已完成记录仅从成功工具 receipt 生成,并仅在引擎返回 allowlist provenance 时展示技法。服务端内部 read-case 增加出生日期、地点标签、经纬度、IANA 时区、UTC offset、填报/当前时间与候选范围;公开 Case GET、React props、receipt 与 SSE 不返回出生上下文或原始评分,只投影 allowlist 工具和技法。Profile 的地点标签或时区 ID 变化会使可恢复 Case needs_rebaseline 并失效活动结果。
  • 验证:新增 UI 静态合同、SSE 安全投影、内部 read-case、引擎 provenance、Profile 完整性与迁移排序/权限回归;本地聚焦测试、TypeScript、ESLint 与 PostgreSQL fixture 结果见本轮交付记录。
  • 防复发:明确区分“服务端内部 Agent 工具结果”和“公开 UI/API projection”;Activity 不得从泛化 phase 文案推导技法,不得公开思维链、Prompt、工具参数、出生资料、原始分数、权重、规则 ID 或 Provider metadata。
  • 相关记录:BUG-163、BUG-170、BUG-171
  • 修复版本:待提交

BUG-173 | 恢复生时校正会话时历史消息未随异步 Case GET 渲染

  • 状态:resolved(本地候选)
  • 首次发现:2026-08-12
  • 最近更新:2026-08-12
  • 影响面:V9 生时校正恢复入口、历史 turns 和持久化 Activity 技法展示。
  • 用户现象:点击“继续上次校正”后只看到页面壳层,Case GET 已返回 assistant turn,但消息区为空。
  • 触发条件:恢复已有 Case;聊天组件先以空 initialTurns 挂载,Case GET 稍后返回历史 turns。
  • 根因:RectificationAgenticChat 仅在 useState 初始化时投影 initialTurns,未在异步 props 更新后同步;同时 page.tsx 映射持久化 receipt 时漏掉 methods
  • 修复:复用同一个 turns→messages 投影函数,并让父层在持久化 turns 到达时用最后一个 Turn ID 重挂载该局部聊天组件;补齐 receipt methods 的客户端类型和安全映射。
  • 验证:新增静态回归检查覆盖异步 hydration 和 methods 映射;部署后以真实登录浏览器确认历史 Agent 消息及 Activity 渲染。
  • 防复发:任何异步加载后传入的初始化数据不能只依赖子组件首次 state 初始化;公开 receipt 新字段必须贯通 API projection、page mapping 与组件类型。
  • 相关记录:BUG-172
  • 修复版本:待提交

BUG-174 | V9 当前用户事件无法绑定服务端 Turn,Agent 要求重复发送

  • 状态:resolved(本地候选)
  • 首次发现:2026-08-12
  • 最近更新:2026-08-12
  • 影响面:V9 生时校正事件证据提出与最终出生时间确认的用户原话绑定。
  • 用户现象:用户输入“2016 年 9 月上大学”后,Agent 能识别事件并调用“整理事件证据”,但声称当前轮锚点没有接通,要求用户重复发送同一句话。
  • 触发条件:Agent 在当前消息轮调用 rectification-propose-evidence;工具 schema 要求模型回传 sourceTurnId,但消息上下文和 rectification-read-case 安全投影都不公开当前 Turn UUID。
  • 根因:服务端 runner 已在 Agent 执行前创建并持有可信 turnId,工具仍错误地把该内部引用交给模型提供,形成模型无法满足的参数合同;重复发送不会修复这一合同缺口。
  • 修复:rectification-propose-evidencerectification-confirm-birth-time 不再接受模型提供的 sourceTurnId,统一使用工具上下文中的服务端当前 turnId;保留数据库 Case ownership、Turn existence 与 quote/consent 原文匹配校验。
  • 验证:回归断言 propose/confirm schema 拒绝模型传入 Turn ID,并确认事件证据与最终确认 RPC 的 p_source_turn_id 均等于服务端当前 Turn;聚焦测试通过,staging 部署证据见本轮发布记录。
  • 防复发:当前请求已经由服务器掌握的内部 ID 不得再要求模型猜测或回传;原文真实性继续在数据库信任边界验证,不能以放宽 quote 校验规避绑定问题。
  • 相关记录:BUG-170、BUG-172、BUG-173
  • 修复版本:本次提交(staging 精确 SHA 以发布记录为准)

BUG-175 | V9 明确事件被强制要求额外二次确认

  • 状态:resolved(本地候选)
  • 首次发现:2026-08-12
  • 最近更新:2026-08-12
  • 影响面:V9 生时校正事件证据写入、Agent 访谈连续性与候选评分输入。
  • 用户现象:用户已经明确说出“2016 年 9 月上大学”后,Agent 仍要求再回答一次“对/确认”,否则事件不进入评分账本。
  • 触发条件:当前轮包含日期、主体和事件语义均明确的新事件,Agent 完成 rectification-propose-evidence 后继续按旧 Prompt/Skill 等待下一轮确认。
  • 根因:Prompt、工具描述、Skill 文档与 TypeScript 状态机把“confirmed 只能由服务器确认路径产生”错误等同于“必须额外等待一轮用户同意”;同时 scorableEvidence() 又把 pending_confirmation 纳入正式评分,导致确认语义与评分边界不一致。数据库确认 RPC 实际已支持 draft -> confirmed
  • 修复:当前轮主动、明确、单一且无歧义的用户事件由 Agent 在同一个 run 内依次调用 propose 与服务器 confirm;只有模糊、冲突、修订或需要补充原文外信息时追问。评分输入统一只接受 confirmed 且有日期的证据,保留 quote grounding、Case/Turn ownership、幂等与 append-only 修订链。
  • 验证:回归测试覆盖同轮 propose -> confirm 工具顺序、draft -> confirmed 合法迁移、Prompt 不再要求重复确认,以及 pending/draft 不进入正式评分。
  • 防复发:服务器确认路径与额外对话轮次必须分开建模;任何 pending 状态不得隐式参与正式候选评分。
  • 相关记录:BUG-170、BUG-174
  • 修复版本:本次提交(staging 精确 SHA 以发布记录为准)

BUG-176 | V9 普通 Turn 的 Agent 回复在刷新后消失

  • 状态:resolved(本地候选)
  • 首次发现:2026-08-12
  • 最近更新:2026-08-12
  • 影响面:V9 生时校正历史消息、持久化 Activity 与 Agent 下一轮上下文。
  • 用户现象:事件提交后当轮可以看到 Agent 回复和 Activity,但刷新或重新进入 Case 后只剩用户事件;opening Agent 消息仍可显示。
  • 触发条件:一条物理 agentic_rectification_turns 记录同时保存非空 user_messageassistant_message
  • 根因:get_agentic_rectification_case_dossier() 把每条物理 Turn 只投影成一条逻辑消息,并用 coalesce(user_message, assistant_message) 优先返回用户文本,导致同一行已持久化的 Agent 回复和 receipt 关联在恢复 API 中丢失。
  • 修复:新增向前业务迁移,将每条物理 Turn 通过 lateral values 展开为按 user、assistant 排序的最多两条逻辑消息;保留同一真实 Turn ID,使 assistant 消息继续读取对应 Activity receipt。
  • 验证:迁移契约测试锁定双消息展开、空消息过滤、顺序、迁移唯一性和禁止复制到 identity migration treestaging 需继续验证 Case GET、页面刷新、下一轮上下文及计费不变量。
  • 防复发:持久化行与对话消息不是一对一时,恢复投影必须显式展开全部逻辑消息,不得用 coalesce 静默舍弃其中一侧。
  • 相关记录:BUG-173、BUG-174、BUG-175
  • 修复版本:本次提交(staging 精确 SHA 以发布记录为准)

BUG-177 | 首页生时校正被未完成 Session 强制劫持,无法新建独立校正

  • 状态:resolved(staging 发布与真实环境验收以本次发布记录为准)
  • 首次发现:2026-08-12
  • 最近更新:2026-08-12
  • 影响面:首页生时校正卡片、POST /api/rectification/cases/open、V9 Case 并发约束
  • 用户现象:账户存在任意未完成的生时校正时,从首页点击“生时校正”会直接回到旧 Session;用户无法保留旧记录并另开一段校正。
  • 触发条件:存在 draftcollecting_evidencecandidate_readycandidate_acceptedneeds_rebaselinepaused Case 后点击首页生时校正卡片。
  • 根因:V9 初始设计把首页 homepage intent 定义为 resume-or-create,并用 agentic_rectification_cases_one_resumable_per_user 部分唯一索引和 active_case_conflict 强制每用户最多一个 resumable Case;首页 UI 又据 entry summary 显示“继续上次校正”。该安全约束错误扩大成产品限制。
  • 修复:首页入口始终作为显式创建动作;新增向前迁移 20260813040000_allow_parallel_rectification_cases.sql,删除每用户单 resumable 唯一索引并重定义 open RPC,使 homepage/new 创建独立 Case + Sessionsession 仍按精确 Session 恢复;requestId 幂等与同用户 advisory lock 保留。首页有未完成记录时明确提示可从左侧历史继续,但主 CTA 仍是新建。
  • 验证:入口、Case service、迁移静态合同等聚焦测试共 119 项,113 通过、0 失败;6 项真实 PostgreSQL 测试因本机 Docker 不可用跳过(测试已覆盖不同 requestId 新建多个 resumable Case、同 requestId 幂等和精确 Session 恢复)。目标 ESLint 与 git diff --check 通过;全量 TypeScript 检查仅命中仓库既有的 dayjs 缺失及无关测试类型错误。
  • 防复发:首页“新建”和历史“继续”必须使用不同 intent;不得用“存在 resumable Case”改变首页主 CTA 或阻止新 Case;同一 requestId 重试只能返回同一 Case。
  • 相关记录:BUG-163
  • 复发自:BUG-163
  • 修复版本:本次提交(staging 精确 SHA 以发布记录为准)

BUG-178 | staging 发布控制器被默认 main 分支耦合,阻止测试分支独立演进

  • 状态:resolved(本地候选,待 staging gate/deploy 验收)
  • 首次发现:2026-08-13
  • 最近更新:2026-08-13
  • 影响面:Gitea staging quality gate、Deploy staging、Migrate Staging Databaseproduction 发布门禁未修改。
  • 用户现象:staging 作为测试分支需要领先或偏离 main 时,旧部署工作流仍要求 main 与 staging 同一 SHA,且 workflow_run 从默认 main 加载控制器,导致 staging-only 变更无法按自身已测试工作流发布。
  • 触发条件:staging 推送了尚未进入 main 的测试提交并完成 quality gate。
  • 根因:staging 发布把 production 的 reviewed-main 收敛约束复用到了测试环境,同时依赖默认分支的 workflow_run controller;即使删除 SHA 相等检查,旧 main controller 仍可能继续执行旧门禁。
  • 修复:将 staging gate 重命名为 Independent Staging Quality Gate,使默认 main 上遗留的 workflow_run 监听器不再匹配并抢占 staging-mutation 并发组;staging push gate 在发布同一 exact-SHA 的不可变镜像与 allowlisted controller bundle 后,显式从 refs/heads/staging dispatch Deploy staging,并传入源 gate run ID。deploy 等待并验证该 gate 最终成功,正常发布仍要求当前 staging HEAD,回滚仍要求当前 staging history 中的旧成功 gate SHA。staging migration 只要求当前 staging HEAD 和 exact-SHA gate artifact。main 与 production workflow 均不改动。
  • 验证:工作流契约测试锁定 staging-ref dispatch、源 gate run 证明、无 main 引用、当前 HEAD 防陈旧发布、同一 gate artifact 的 controller/digest 校验和回滚祖先限制;远端 gate/deploy 与运行时 SHA 待本次 staging 发布记录。
  • 防复发:测试环境的部署控制器必须来自被同一 quality gate 证明的 staging SHAstaging gate 名称不得恢复为默认 main 遗留监听器匹配的 Staging Backend Quality Gate,也不得重新引入 workflow_run 默认分支控制器或 staging/main 相等门禁。production 继续保持独立的 main/staging 收敛要求。
  • 修复版本:本次 staging workflow 提交

BUG-179 | 生时校正候选未显示卡片且回复被推荐问题与内部执行叙述干扰

  • 状态:resolved(代码已验证,staging 部署与真实环境验收待发布流程)
  • 首次发现:2026-08-13
  • 最近更新:2026-08-13
  • 影响面:V9 生时校正候选结果展示、对话输入区、Agent 回复自然度与候选采用后的会话延续。
  • 用户现象:服务器已有候选快照时,候选时间仍被写进 Agent 正文而没有出现既有候选卡;输入框下方固定出现三条推荐问题;Agent 还会叙述读取 Skill、加载 Case、调用工具和读取诊断等内部步骤。用户采用候选或表示没有更多事件后,回复容易被迫继续追问或引导结束、暂停、保存进度。
  • 触发条件:Case API 返回已解析为公开 camelCase 字段的 latest_result,同时生时校正复用通用 Agent 回复解析器及其三条建议兜底,并且 Prompt/Skill 未明确零问题回复、静默工具执行和无更多事件的停止边界。
  • 根因:候选组件仍按数据库 snake_case 字段读取 Case API 的公开 camelCase 快照,因缺少 result_id 判定而丢弃结果;通用 parseAgentReply() 在模型未提供恰好三条建议时自动生成三条兜底建议,生时校正又持有并渲染 composer-suggestionsPrompt/Skill 只限制“最多一个问题”,未明确完整回复可以零问题、内部执行不得进入正文、用户没有更多事件时不得继续轮换领域,也未划清候选卡与 Agent 正文的内容所有权。
  • 修复:新增客户端候选快照解析模块,按 Case API camelCase 外层字段读取结果并规范化候选内部字段;候选卡独占时间、排名、相对支持度、采用动作和选中状态,近乎并列且未开放确认门时不标记“当前推荐”;生时校正改用只提取正文与标题的解析入口,移除建议状态、持久化和 composer-suggestionsPrompt、Skill 与会话策略明确工具静默、零或一个问题、没有更多事件时停止领域轮换、采用后不强制追问或关闭,Session 依现有机制自然保留。
  • 验证:候选解析、回复解析、V9 入口静态合同与 V9 合同聚焦测试共 48 项通过;V9 Agent 聚焦测试 14 项通过;目标 ESLint 与变更源文件定向 TypeScript 检查通过,git diff --check 通过。仓库全量 TypeScript 仍命中既有的无关测试类型错误,未在本修复中扩展处理。
  • 防复发:Case API 的公开 DTO 必须有单一解析边界并由合同测试锁定;特殊业务流不得继承通用推荐问题兜底;Agent 正文、Activity 和结构化业务卡片必须各自拥有唯一内容职责,不得重复呈现或把内部执行过程写进自然回复。
  • 相关记录:BUG-173、BUG-174、BUG-175、BUG-176
  • 修复版本:本次提交(staging 精确 SHA 以推送结果为准;尚未部署)

BUG-180 | 生时校正缺少跨轮安全上下文且单一事件契约破坏自然叙述

  • 状态:resolved(已验证,待本次 staging 发布完成)
  • 首次发现:2026-08-13
  • 最近更新:2026-08-13
  • 影响面:V9 System Prompt、首次开场、rectification-read-case 安全投影、多轮承接与多事件证据记录。
  • 用户现象:Agent 容易把每条输入当成新事件;同一段中的多件事件被合并或要求逐条重发;“是的、那年、后来改了”等承接回答缺少上下文;用户拒答后可能被重复追问;首次开场像客服式流程介绍。
  • 根因:Prompt 缺少以理解真实经历和信息增益为导向的正向行为目标,并把输入限制为单一事件;buildAgentMessages 只提供当前轮消息,rectification-read-case 又只返回统计,没有受限的近期对话、事件摘要、当前修订目标和近期拒答目标。
  • 修复:整体替换 System Prompt 与首次开场指令;支持同轮多事件分别 propose/confirmrectification-read-case 从服务端 Dossier 派生有界的 evidence_contextconversation_context;允许当前 pending 用户轮立即关闭已拒绝的 active follow-up;同步 Skill 与 conversation strategy。
  • 验证:V9 Agent、status/security、evidence、contracts 聚焦行为与安全合同测试共 56 项通过;目标 ESLint 通过,git diff --check 通过。
  • 防复发:不得恢复单一事件问卷契约;跨轮承接必须通过服务端安全投影,不得传递 raw ledger;拒答不落成证据,也不得重新开启同一追问目标。
  • 相关记录:BUG-173、BUG-174、BUG-175、BUG-179
  • 修复版本:本次 staging 发布提交(精确 SHA 以提交与部署结果为准)

BUG-181 | 生时校正 Activity 写死状态且完成凭证抢在 Agent 正文之前

  • 状态:resolved(已验证,待本次 staging 发布与真实浏览器验收)
  • 首次发现:2026-08-13
  • 最近更新:2026-08-13
  • 影响面:V9 生时校正 NDJSON Activity、运行中 Orbs、完成凭证、候选采用按钮与 Agent 执行静默。
  • 用户现象:Agent 运行时显示与真实工具不一致的“正在核对星盘信息”等写死文案;只有候选比较能在开始阶段出现公开状态,其他工具实时状态缺失;完成后的大块执行记录位于 Agent 正文之前,用户先看到系统日志而不是结论,技法又以不可点击的标签堆叠产生交互误导。
  • 根因:前端把运行中 Activity 与持久化执行凭证混在同一展示状态中,通用消息行在没有真实事件时虚构具体动作;服务端 stream mapping 只为少数 phase 映射开始状态,没有为所有公开校正工具投影统一的 started/completed/failed 生命周期;Prompt/Skill 也未明确禁止正文自行生成执行记录标题与 Activity 文案。
  • 修复:新增仅包含 allowlist 工具、状态与公开 executed_methodstool.activity 协议,并在 API 出口再次清理;所有十个公开工具均按真实 tool-call/tool-result/tool-error 映射 started/completed/failed。客户端拆分当前 Activity 与完成凭证,运行时只显示 Orbs 和当前真实动作,正文开始后清除状态;完成凭证移动到 Agent 正文之后,使用原生默认折叠 details/summary、普通文本分组和可见键盘焦点,不再使用大灰卡与胶囊标签。候选采用按钮内部改为“正在采用…”,不增加第二个 Orbs;文字流光支持 160ms 切换淡入和 reduced-motion。Prompt、Skill 与会话策略禁止正文生成“本轮做了什么”“执行步骤”“使用技法”或内部错误细节。
  • 验证:V9 Agent/status/evidence/contracts、Activity stream、入口和布局聚焦回归共 98 项全部通过;目标 ESLint 与定向 TypeScript 检查通过;Next.js webpack 生产构建完成 TypeScript、60 个静态页面及全部路由生成;git diff --check 通过。默认 Turbopack 仅因隔离 worktree 的外部 node_modules 符号链接触发环境性 panic,非本次代码错误。真实键盘、读屏器、移动端状态切换和登录业务流仍需浏览器验收。
  • 防复发:Activity 只能来自服务端清理后的真实工具生命周期,不得从 Agent 正文、未来步骤或前端猜测生成;工具参数、出生资料、评分、权限、Provider metadata 与内部错误不得进入公开事件;Agent 正文、运行状态、完成凭证和候选卡必须保持单一内容所有权。
  • 相关记录:BUG-172、BUG-173、BUG-179、BUG-180
  • 修复版本:本次 staging 发布提交(精确 SHA 以提交与部署结果为准)

BUG-182 | 候选卡允许“改选”但数据库把不同分钟误报为“该时间已采用”

  • 状态:resolved(已验证,待 staging 发布与登录态业务验收)
  • 首次发现:2026-08-13
  • 最近更新:2026-08-13
  • 影响面:V9 生时校正候选卡的 改选为此时间 操作、Case/Profile/Result 三方采用状态一致性。
  • 用户现象:一个候选分钟已经采用后,点击同一候选结果中的另一分钟,界面返回“该时间已采用”;卡片明明显示“改选为此时间”,实际却无法切换。
  • 触发条件:agentic_rectification_results.selected_time 已有值,随后以同一个有效 result_id 请求另一个候选分钟。
  • 根因:accept_agentic_rectification_candidate_for_case 只实现了首次采用和同分钟幂等重放;只要请求分钟不同,就直接抛出 agentic_rectification_candidate_already_selected,没有实现前端合同所承诺的安全改选分支。
  • 修复:新增前向业务迁移,保留同分钟幂等;仅允许 candidate_accepted、未 confirmed、同一有效未过期结果、旧采用时间与 Profile/Case/Result 完全一致时改选。改选原子更新 Profile、Result 与 Case,并强制保持 user_accepted / accepted / candidate_accepted,不绕过显式确认门;终态、已确认、资料漂移、更新结果或非候选时间仍拒绝。
  • 验证:新增迁移合同回归覆盖顺序、事务、幂等、改选门、Profile 旧值校验、accepted 写入、confirmed/终态不可变及业务迁移隔离;新增 PostgreSQL 集成场景覆盖首次采用、跨分钟改选、三方状态落库与新分钟幂等重放。部署后仍需在 staging 登录态点击候选卡完成真实业务验收。
  • 防复发:候选卡 CTA 与数据库状态机必须共享同一行为合同;任何“改选”文案都必须有跨分钟成功路径测试,不能只测试按钮未禁用或同值幂等。
  • 相关记录:BUG-127、BUG-144、BUG-179、BUG-181
  • 修复版本:本次 staging 发布提交(精确 SHA 以提交与部署结果为准)

BUG-183 | 首页主标题展示字体被重构为粗黑体

  • 状态:resolved(本地候选,待 staging 部署与视觉验收)
  • 首次发现:2026-08-13
  • 最近更新:2026-08-13
  • 影响面:首页欢迎主标题与两个产品入口卡片标题。
  • 用户现象:首页原有的宋体/衬线标题变成黑体加粗,整体视觉层级和既有编辑感丢失。
  • 触发条件:加载 starter-heroproduct-entrypoint 首页区域。
  • 根因:前端视觉清理提交将 .starter-hero h1.product-entrypoint-copy h2 从既有 var(--font-display) / font-weight: 400 改为 var(--font-body) / font-weight: 650-680;中文因此回退到 PingFang SC 等无衬线字体并明显加粗。全局展示字体变量本身并未删除。
  • 修复:仅恢复两个首页标题使用现有 var(--font-display) 和常规字重 400,保留当前重构后的字号、行高与布局,不新增字体依赖或平台硬编码。
  • 验证:新增首页字体合同测试,分别锁定欢迎主标题和产品入口标题使用 font-displayfont-weight: 400,并拒绝回退到 font-body;聚焦测试、目标 ESLint、生产构建、git diff --check 与 staging 视觉验收按发布结果记录。
  • 防复发:首页编辑型主标题与产品入口标题必须继续使用设计系统的展示字体 token;通用去风格化或排版重构不得把展示标题批量替换成正文无衬线字体。
  • 相关记录:BUG-026
  • 修复版本:本次 staging 发布提交(精确 SHA 以提交与部署结果为准)

BUG-184 | 生时校正完成凭证显示在 Agent 正文下方

  • 状态:resolved(本地候选,待 staging 部署与登录态视觉验收)
  • 首次发现:2026-08-13
  • 最近更新:2026-08-13
  • 影响面:V9 生时校正每轮完成后的“本轮完成 · 查看详情”执行凭证与 Agent 正文阅读顺序。
  • 用户现象:执行凭证显示在整段 Agent 回复之后,与运行中 Activity 位于 Agent 消息上方的空间关系不一致;用户需要读完正文后才看到本轮执行状态。
  • 触发条件:生时校正消息完成且持久化了公开 completedReceipt
  • 根因:消息容器的 JSX 顺序固定为 ChatMessageRow 后渲染 CompletedActivityReceipt,并由静态合同测试锁定了错误的下方顺序。
  • 修复:在同一消息容器内先渲染 settled 状态的 CompletedActivityReceipt,再渲染 ChatMessageRow;运行中的真实 Activity 仍由消息行自身在正文上方展示,不改变公开事件协议、折叠行为或凭证内容。
  • 验证:更新 Agentic rectification DOM 合同测试,明确要求 CompletedActivityReceipt 位于 ChatMessageRow 之前;聚焦测试、目标 ESLint、生产构建、git diff --check 与 staging 精确 SHA 验证按发布结果记录。
  • 防复发:生时校正的运行中状态与完成凭证都必须位于对应 Agent 正文上方;不得仅通过 CSS order 视觉重排而保留错误的 DOM/读屏顺序。
  • 相关记录:BUG-172、BUG-181
  • 修复版本:本次 staging 发布提交(精确 SHA 以提交与部署结果为准)

BUG-185 | 生时校正工具重试成功后仍显示失败,且 V9 消息操作栏丢失

  • 状态:resolved(本地候选,待浏览器与 staging 验收)
  • 首次发现:2026-08-13
  • 最近更新:2026-08-13
  • 影响面:V9 生时校正 Activity 完成凭证,以及 Agent 正文下方的赞、踩、复制和重新生成操作。
  • 用户现象:同一证据工具首次失败、同轮自动重试成功且最终回复正常时,界面仍显示“未完成,当前进度已保留”;同时此前已有的消息操作栏在 V9 页面中消失。
  • 触发条件:同一公开校正工具在一个 NDJSON run 中出现 failed → started → completed;或查看任意已完成的 V9 Assistant 消息。
  • 根因:客户端 Activity 聚合只保留“曾经失败”的集合,后续成功没有覆盖同工具旧失败;旧版生时校正运行时退役时删除了消息操作 JSX、状态和 handler,而 V9 入口没有迁入,虽然对应 CSS 仍被保留。
  • 修复:Activity 改为按工具记录最终终态,后续 completed 清除同工具旧失败,只有最终仍为 failed 才显示失败凭证。V9 恢复赞、踩、复制与重新生成操作栏,并保持 完成凭证 → Agent 正文 → 操作栏 的 DOM 顺序。重新生成使用独立只读 Jyotisha Agent,仅可读取当前 Case,免费且原位替换最新 completed Assistant 正文;不新增 Turn、不写证据或候选、不修改 Case 状态、不调用计费。
  • 验证:新增 Activity reducer、重新生成 runner、公开 stream、UI DOM 与数据库迁移合同回归;聚焦测试、目标 ESLint、TypeScript 与 git diff --check 结果以本次本地验证记录为准。首次 staging quality gate 还发现全库 schema 快照漏列新表 agentic_rectification_turn_regenerations,现已同步更新并纳入全门禁。真实剪贴板权限、键盘焦点和登录态重新生成仍需浏览器验收。
  • 防复发:Activity 必须以每个工具的最终终态为准,不能把历史瞬时失败永久化;V9 消息动作不得依赖已退役组件。任何“重新生成”都必须是最新回复的只读原位替换,严禁复用普通 message 发送链导致重复证据、重复 Turn 或重复计费;新增迁移表时必须同步全库 schema 快照测试。
  • 相关记录:BUG-049、BUG-050、BUG-181、BUG-184

BUG-186 | 生时校正运行合同仅靠 Prompt,失败 attempt 与长会话焦点缺少服务器隔离

  • 状态:resolved(本地候选,待 staging migration、部署与登录态业务验收)
  • 首次发现:2026-08-14
  • 最近更新:2026-08-14
  • 影响面:V9/V10 生时校正每轮 Skill/Case 门禁、自动重试、计费完成凭证、证据写入、承接回答、长会话摘要与历史 Skill 身份。
  • 用户现象:模型可能在真正加载绑定 Skill 或读取 Case 前生成正文;失败 attempt 已产生的半句话、Activity、usage 或工具记录可能混入成功重试;“是的、不是、不记得、换个方向”等承接词依赖正则和上一条 Assistant 文本猜测目标;长会话只靠 recent turns 截断,单条消息中的多件事件又缺少独立批量持久化结果;历史 Case 还可能受当前全局 active Skill 版本变化影响。
  • 触发条件:首个 stream attempt 在输出部分正文后以 empty_streamstream_abortedstream_unfinished 失败并自动重试;用户对 active question 作简短承接回复;会话超过近期窗口;一条消息包含多件明确经历;或注册表 active Skill 版本在 Case 创建后升级。
  • 根因:旧运行器把 skill.boundcase.loaded 主要写在 Prompt 约定中,没有 durable attempt ownership 和成功 attempt 投影;ConversationFocus 由 Assistant 文本正则倒推,没有服务器持久化的 target evidence/domain/kindCase dossier 缺少 durable conversation summary 和批量 evidence 幂等合同;运行时按全局 active Skill 解析,而不是严格使用 Case 已绑定的 immutable package identity。
  • 修复:新增 additive V10 migration,建立 agentic_rectification_run_attemptsagentic_rectification_conversation_focusesagentic_rectification_case_conversation_summaries,并把 phase/tool receipt 绑定 attempt。每轮先锁定 Case/Turn 并取得 attempt 执行权;只有完成 skill.bound → case.loaded → intent.classified 后才能提交答案,失败 attempt 的正文、Activity、usage 与 receipt 不进入成功 Turn。completed attempt 必须同时具备 billing.settledrun.completed 和与 Case 精确 Skill name/version/SHA/source commit 匹配的 immutable run receipt。Turn 的 request_id 进入数据库唯一幂等边界;同请求只返回原 Turn,不同正文/模型拒绝复用。V10 migration 同时撤销 service_role 对旧无 request-id append 与旧无 attempt 所有权 Turn finalizer 的执行权,避免兼容 overload 绕过;terminal Case 仍阻止新 attempt,但允许幂等取回已存在 attempt 完成收口。新增 focus set/resolve 与批量 evidence RPC,批量项目保留独立 quote、kind、domain、date precision 和幂等键,并且只有唯一匹配的 accepted evidence 才能解决 focus。Dossier 同时返回受限 recent turns 与 durable summary;开场只传服务器 brief,由 Skill 的 OpeningPolicy 生成自然措辞。terminal Case 拒绝新 attempt、focus、evidence 与 confirmation/revision 写入。
  • 验证:V10 migration 静态合同 55/55 通过;migration 与 stream 隔离合跑 77/77 通过;PR-3 聚焦门禁 199 passed、0 failed、7 skippedDocker 场景由独立真实数据库测试覆盖);真实 Docker PostgreSQL 从空库应用全部 migration 并完成业务测试 1/1,覆盖 request replay/mismatch、legacy RPC 撤权、terminal attempt 取回与新 attempt 拒绝、completed Turn 单调性及 superseded attempt;目标 ESLint 0 error/0 warninggit diff --check 通过。全库 tsc --noEmit 的本 PR 新增错误已清零,仅剩 production-data-migration.test.ts 两处 fixture 字段缺失和 staging-backend-workflows.test.ts 三处低 target 正则 flag,共 5 个既有无关错误。pre-work governance 的 Python、fragment scan、外部引擎诊断与远端可见性均通过,focused governance tests 仍被既有 fragment 计数断言 candidate_count 4 < workspace_residue_count 9 阻塞,不属于本 PR 文件。
  • 防复发:不得把 Skill/Case read gate、focus 目标、attempt 成功归属或 Skill 身份降级为 Prompt 约定;公开正文、usage、Activity 与 receipt 必须只来自 successful_attempt_id;失败/重试不得重复扣费或重复 evidencefocus resolve 必须引用服务器持久化 focus/evidence,不能解析 Agent prose;历史 Case 必须继续绑定创建时可核验的 immutable Skill package。
  • 相关记录:BUG-173、BUG-174、BUG-175、BUG-179、BUG-181、BUG-185
  • 修复版本:本次功能分支提交(精确 SHA 以提交与远程分支核对结果为准;未合并 staging,未部署)

BUG-187 | 生时校正详细事件被压平,候选采用与精确确认缺少版本化服务器门禁

  • 状态:resolved(本地候选,未部署)
  • 首次发现:2026-08-14
  • 最近更新:2026-08-14
  • 影响面:V9/V10 生时校正事件评分输入、候选排序与展示、候选结果持久化、候选采用/精确确认 RPC、Activity 技法凭证。
  • 用户现象:教育、事业、关系、迁居和财务事件在进入 Python 评分前被压成粗粒度类型;Web 根据 margin、候选数量和事件 domain 自行判断置信度、采用权限与已执行技法;候选采用可能在代表分钟上隐式升级为 confirmed;所谓 candidate ID 实际是调用方提交的 HH:MM,不能证明来自服务器保存的候选结果。
  • 触发条件:提交 career_entry/career_exitrelationship_commitment/relationship_separation 等生命周期事件进行候选评分;展示候选卡或 Activity;采用代表候选;或调用旧的 time-based candidate RPC。
  • 根因:Python 事件合同只接受少量粗粒度 kindTypeScript adapter 承担了有损映射、排序/tie/置信度/permission 和 technique 推断;结果表与 RPC 只保存调用方提供的布尔值和候选 JSON,没有版本化 decision receipt、真实 execution ledger 与服务器 candidate UUID 边界;历史 accept RPC 仍可把 accepted 与 confirmed 混合。
  • 修复:引入 Event Contract v2 与 Decision Policy v2,由 Python 返回版本化 candidate_decisionsdecision_receipt 和真实 execution_ledger,日期精度进入服务器评分;Web 只做严格安全投影,不再自行推断 rank、tie、margin confidence、selection gate 或已执行技法。forward-only migration 保存政策与执行凭证,以服务器生成的 candidate UUID 绑定 result,并将 representative candidate 重写为持久化 UUIDaccepted 与 confirmed 使用独立 RPC 和状态门禁,精确确认必须引用已接受候选、有效 consent/source turn,旧 time-based RPC 对 service_role 撤权。Case/Dossier 恢复路径同时投影完整 v2 receipt、ledger、selected candidate 与 display gate。
  • 验证:Python 服务回归 36 passedTypeScript 核心合同 49 passed、0 failedmigration 与 PR-4 真实 PostgreSQL 场景 75 passed、0 failedV9/V10/candidate 全量聚焦门禁 219 passed、0 failed,覆盖 request replay、payload conflict、候选 UUID 归属、representative UUID 缓存一致性、accepted/confirmed 原子分离、consent/source-turn 精确确认门禁、legacy RPC 撤权与 Case/Dossier 恢复。目标 ESLint 0 error/0 warningPython Ruff 与 py_compile 通过,git diff --check 通过。全库 tsc --noEmit 仅剩 5 个既有无关测试错误:production-data-migration.test.ts 两处 fixture 字段缺失、staging-backend-workflows.test.ts 三处低 target 正则 flag。
  • 防复发:详细事件不得在 adapter 层压平;“有候选”不得等于“允许采用”;Web 不得自行推断执行技法或业务 tie;采用候选永远不能隐式确认精确分钟;candidate action 必须引用服务器持久化 result 中的 candidate UUID。
  • 相关记录:BUG-172、BUG-179、BUG-184、BUG-186
  • 修复版本:本次功能分支提交(精确 SHA 以提交与远程分支核对结果为准;未合并 staging,未部署)

BUG-188 | 个人报告按 general 单次计算且 v1 证据包无法闭合主题结论

  • 状态:resolved(本地候选,未部署)
  • 首次发现:2026-08-14
  • 最近更新:2026-08-14
  • 影响面:个人完整/专题报告的主题计算编排、报告 Agent 输入、出生时间政策、主题证据引用与报告生成状态。
  • 用户现象:完整报告即使请求多个主题也只执行一次 general,专题报告只使用首个主题;写作 Agent 只能获得缺少 Claim Card、Blocked Section 和执行账本的 ReportEvidencePacket v1,因此内容短、主题证据不足,且 accepted 时间曾通过零宽候选区间表达。
  • 触发条件:创建包含多个 requested themes 的 personal_full 报告;请求 wealth 等需要专题分盘/技法但证据未闭合的报告;或使用 accepted 出生时间生成报告。
  • 根因:报告路由把所有完整报告折叠到单次 general workflow,旧 packet 仅提供窄化盘面与技法状态,没有服务器持有的主题 Claim Graph、最低证据计划、blocked coverage、execution receipt 与规范化 hash;Agent 又被禁止自行推算未提供事实。
  • 修复:新增 ReportEvidenceBundle v2、主题最低证据计划、规范化 hash 与强引用校验;路由按 requested themes 分别运行 workflow,并把缺失专题证据表示为 Blocked Section,而不是伪造已执行技法或让部分 blocked 主题拖垮整份报告。服务器从 Bundle 生成确定性的 section plan,writer 只能填写计划允许且引用闭合的章节,再由服务器 guard 规范化为 ReportDocument v2;新建报告固定写入 v2,读取端保持 v1/v2 双读。生产创建路径不再依赖 Next.js after(),改为由数据库原子创建的持久化 job 与 lease worker 执行,覆盖 heartbeat、bounded retry、historical generating backfill、expired-lease recovery、幂等 request identity 和进程重启续跑;job migration 使用唯一版本 20260814040000。ready 与 failed 两种终态都通过 exact-live-lease RPC 在同一事务更新 report/job,且 complete/fail 在取得 job 行锁后才读取 lease 校验时间;final-attempt lease 过期会原子收敛为 report=failed/job=failed 并释放用户生成槽位。scheduleRetry() 耗尽预算时保留 live lease 交给失败 RPC 收敛;若 report 已 ready 而 job ready 对账遇到临时存储错误,则返回 reconcile_deferred 并保持 report ready、job running,绝不把 ready 报告降级为 failed。authenticated 对 job 保持 select-only,完成 RPC 仅授予 service_rolewriter 与单次 repair retry 均接收 worker AbortSignal。accepted 状态改为 accepted_directional_onlyBundle 不生成 candidateRange;缺失 D2/D11 等证据只能生成 blocked + executed=false receipt。Web renderer 继续只消费结构化文档和真实 chart data,并使用浏览器原生打印,不接受模型生成 HTML/CSS/SVG。
  • 验证:个人报告合同、planner/writer、API、entry、job state/service/worker、renderer/export、migration 与 Skill registry 完整聚焦回归 247 passed、0 failedPython 报告合同与 Skill package 41 passed、0 failed;目标 ESLint 0 error/0 warninggit diff --check 通过。真实本地 PostgreSQL 已成功应用两个 PR-6 migration,聚焦 smoke 1 passed、0 failed,覆盖 historical generating backfill、authenticated owner 删除拒绝、错误 lease 对 ready/failed 均不写入、正确 lease 原子完成 ready/failed、final-attempt 历史 ready/job-running split recovery、final-attempt generating/running 原子 failed/failed,以及失败后可创建新的 generating/queued 报告。migration 唯一性检查只剩基础分支既有的 20260806010000_admin_rbac.sql / 20260806010000_personal_reports.sql 冲突;全库数据库业务测试仍在既有 Rectification V10 场景因 service_role 无权执行 accept_agentic_rectification_candidate 失败,该权限问题不属于本条报告修复。全库 tsc --noEmit 仅剩 5 个既有无关测试错误:production-data-migration.test.ts 两处 fixture 字段缺失、staging-backend-workflows.test.ts 三处低 target 正则 flag。pre-work 仅剩既有 fragment candidate 计数断言 4 >= 0 + 23 失败。
  • 防复发:每个 requested theme 必须且只能由 Claim Card 或 Blocked Section 覆盖;未执行或 blocked/partial 技法不得升级为 verified/consensusAgent 不得接收 raw workflow、坐标、内部路径、secret、聊天历史或工具轨迹;Bundle hash 必须由服务器对规范化且排除自身 hash 的内容计算。报告文档、job 状态和日志不得写入用户资料、密钥、内部 URL 或模型自由生成的 HTML/CSS/SVG;不得把本地测试结果虚构为 staging 或生产验收。
  • 相关记录:BUG-152、BUG-159
  • 修复版本:本次功能分支提交(精确 SHA 以提交与远程分支核对结果为准;未合并 staging,未部署)

BUG-189 | 普通咨询域与独立产品域漂移,导致多域请求、专题证据与 UI 状态越界

  • 状态:resolved(本地候选,未部署)
  • 首次发现:2026-08-14
  • 最近更新:2026-08-14
  • 影响面:普通咨询入口与会话持久化、Mastra 多域计划、Python 咨询编排与专题报告、chat_sessions.theme 约束、生时校正 Activity/候选文案,以及个人报告、合盘、生时校正独立产品 API。
  • 用户现象:同一个自然语言问题涉及多个主题时只能落入单一 theme;前端、API、Python 与数据库允许域不一致,新增普通咨询域可能在写会话或专题执行时失败;不具备原生专题证据的域可能被静默当成 general;候选采用曾显示成“已确认”,流式正文到达后会清除真实运行中 Activity,失败回合的服务端 receipt 也可能丢失;独立产品入口与 API 缺少统一的服务端产品开关。
  • 触发条件:请求 education/migration/family/annual 等旧数据库或旧专题枚举未覆盖的普通咨询;Agent 从同一问题规划多个咨询域;采用但未确认生时候选;工具运行中先收到 answer.delta 或工具失败;关闭独立产品但直接调用其写入/计算 API。
  • 根因:咨询域在多个客户端与服务端枚举中重复维护,单 theme 合同无法表达多域;专题报告只认识旧主题枚举;UI 把 accepted 与 confirmed、正文与真实执行状态混合;独立产品没有与普通咨询 taxonomy 分离的 registry 和 fail-closed 服务端 gate。
  • 修复:建立十个 canonical consultation domain 的 TypeScript/Python 单一 registry、alias 规范化与最多六域的有序去重计划,Agent 按域逐一执行并由服务器聚合真实 domains receipt;数据库前向迁移同步 chat_sessions.theme 约束。career/marriage/wealth/health 使用原生专题报告,其余 canonical 域返回明确 degraded/blocked adapter,未知域和独立产品域 fail closedannual 普通咨询不等于 annual_report。UI 保留 answer delta 期间的真实 Activity 与失败 receipt,只从服务端 event/receipt 渲染执行状态,并严格区分“已采用(未确认)”和“已确认”。另建独立 product registry 与 feature flags;个人报告、合盘、生时校正写入/计算 API 在鉴权后执行 fail-closed 产品 gate,报告中心同时保留既有环境变量开关。
  • 验证:TypeScript focused 矩阵 178 passed、0 failedPython consultation/thematic/API focused 70 passed、0 failedpy_compile 通过;全部 PR-7 TypeScript/TSX 目标 ESLint 与 git diff --check 通过。全库 tsc --noEmit 仍只有 5 个既有无关测试错误:production-data-migration.test.ts 两处 fixture 字段缺失、staging-backend-workflows.test.ts 三处低 target 正则 flag。全量 Ruff 会报告 legacy Python 文件既有存量问题,因此未将其声明为 PR-7 通过门禁;未应用任何远端数据库迁移,未部署。
  • 防复发:普通咨询只能持久化 canonical consultation domain,独立产品 ID 永不进入 chat_sessions.theme;多域执行必须保序、去重、逐域产生真实 receipt,未执行能力不得伪装为 general 或 verifiedaccepted 不得升级为 confirmedActivity 不得从 Agent 正文推导;所有独立产品写入/计算 API 必须执行服务端 fail-closed gate,数据库迁移保持前向兼容且默认关闭未发布产品。
  • 相关记录:BUG-159、BUG-181、BUG-185、BUG-188
  • 修复版本:本次功能分支提交(精确 SHA 以提交与远程分支核对结果为准;未合并 main/staging,未部署)

BUG-190 | Agent 评测缺少完整去标识化场景,观测日志无严格非 PII 合同

  • 状态:resolved(本地候选,未部署)
  • 首次发现:2026-08-14
  • 最近更新:2026-08-14
  • 影响面:普通咨询、生时校正、个人报告与安全边界的 Agent 回归评测;普通咨询 Agent 运行时日志、用量与结算观测。
  • 用户现象:仓库缺少一套按多轮业务场景统一组织的 Agent golden dataset,无法确定性证明 Skill/工具合同、证据引用闭环、主题覆盖、精确时间边界与校时焦点;自然度等模型评审项也容易被误写成事实门禁。普通咨询运行日志使用拼接字符串且字段零散,没有统一的严格 allowlist,后续增加正文、异常消息、出生资料、密钥或内部路径时缺少 fail-closed 保护。
  • 触发条件:新增或修改普通咨询、校时、报告、安全行为但只运行局部单元测试;把模型评审结果当成事实通过条件;向 Agent 日志添加任意字段或直接记录原始异常消息。
  • 根因:没有版本化、去标识化、多轮 golden dataset 与通用 deterministic scorer;评测事实门禁、模型评审和性能统计未分层。运行观测沿用 route 内自由拼接日志,没有闭合 schema、受控错误码和未知字段拒绝机制。
  • 修复:新增 agent_golden_dataset.v1,以 intent code/context tags 表达 34 个 synthetic 多轮场景,完整覆盖五个核心咨询主题、多主题/改问/无出生分钟/accepted-confirmed 边界,校时多事件与不同精度、更正/拒答/跳过/承接/长会话/候选采用确认,报告完整/部分/冲突证据与 accepted 时间、多主题,以及 prompt、凭据、内部路径、高风险确定性请求和伪造出生资料/candidate ID。新增确定性 scorers,分别检查 Skill/工具合同、引用集合闭包、canonical 主题覆盖、规则型 unsupported facts、精确时间违规、校时焦点、工具经济性与延迟/成本统计;引用闭环只接受本轮 availableEvidenceIds/producedEvidenceIds 中且属于 case catalog 的证据,fact/timing 不得用 requiresEvidence=false 绕过,acceptedconfirmed minute 显式分离且后者只在明确 gate/consent case 放行。自然度、重复性、follow-up relevance 和模型型 unsupported-fact review 只产生显式 pending 输入。新增严格非 PII Agent observability schema/logger,未知或禁止字段 fail closed、sink 失败不影响业务、异常只映射为受控错误码,并将普通咨询现有 logRun/usage/结算接入结构化日志;取消结算重试耗尽时记录 failed/settlement_failed,不得伪记为 cancelled
  • 验证:PR-8 combined focused TypeScript 矩阵 97 passed、0 failed;目标文件 ESLint 与 git diff --check 通过;fixture 隐私扫描未发现姓名、邮箱、真实出生日期/时间/地点、凭据、内部绝对路径或完整用户正文。全库 tsc --noEmit 仍只有 5 个既有无关测试错误:production-data-migration.test.ts 两处 fixture 字段缺失、staging-backend-workflows.test.ts 三处低 target 正则 flag。未运行 quick/browser/accuracy/release 或 staging canary,未部署。
  • 防复发:golden fixture 只能保存 synthetic intent code/context tags,不得保存真实用户正文或出生资料;事实、权限、证据、状态与精确时间边界只能由确定性门禁判定,模型评审必须保持 pending 直到真实执行;catalog membership 不能替代本轮 evidence availabilityrequiresEvidence 不能关闭事实/时间证据规则,accepted 不得升级为 confirmedAgent observability 不得加入自由格式 metadata、正文、prompt、messages、出生资料、身份信息、secret/API key、provider payload、stack 或内部路径,结算失败不得降级为 cancelled,公开 NDJSON 不得扩展为内部 telemetry。
  • 相关记录:BUG-181、BUG-186、BUG-187、BUG-188、BUG-189
  • 修复版本:本次功能分支提交(精确 SHA 以提交与远程分支核对结果为准;未合并 main/staging,未部署)

BUG-191 | PR-8 全量发布门禁被过期测试合同与本地解释器假设阻断

  • 状态:resolved(本地候选,待 staging 精确 SHA 发布验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:PR-8 全量前端测试、TypeScript 编译、本地 PostgreSQL 业务迁移测试、staging workflow YAML 合同与质量门禁。
  • 用户现象:PR-1 至 PR-8 功能提交本身已完成,但全量前端门禁仍有 5 个失败:starter questions 继续锁死旧四主题并扫描派生 wrapper 的重复文案;数据库业务测试继续调用已撤权的 legacy 校时候选接受 RPC;生产迁移 fixture 缺少新增列元数据;staging workflow 测试使用 ES2018 /s flag 且假设 PATH 中存在可导入 PyYAML 的 python。因此不能安全提交、推送或发布 staging。
  • 触发条件:在 PR-7 十域 registry、PR-4 V2 candidate decision contract、生产迁移列模型与独立 staging workflow 合同合并后运行全量 tsx --test tests/*.test.tstsc --noEmit 或 Python quality gate。
  • 根因:测试仍复制旧业务常量和旧 RPC 调用方式,没有跟随新的单一真源与服务端 UUID 合同;测试 fixture 未补齐列模型新增字段;Node 启动的 YAML 检查未继承质量门禁实际使用的 Python 解释器,并包含依赖特定 worktree 深度的临时 fallback。
  • 修复:starter tests 改为验证全部 10 个 canonical domain、label/prompt/evidence/claim 投影及按 domain 归属的 D10、D9、Ashtakavarga、negative holdout gatebilling 测试分别锁定 reserve/complete/release 的免费 turn 短路与付费调用;数据库测试保留 legacy RPC 撤权断言,并恢复 persist_agentic_rectification_candidate_v2accept_agentic_rectification_candidate_for_case_v2 的真实纵向链路,覆盖服务端 candidate UUID、首次接受、幂等重放、切换候选、profile 落库、reported time 保留及基线变化后的 expired 拒绝;生产迁移 fixture 补齐 identity/data type 元数据;YAML 检查改用 PYTHONVIRTUAL_ENV、仓库 .venv 与 PATH fallbackquality gate 通过 os.environ.setdefault("PYTHON", sys.executable) 向前端测试传递解释器,并移除 worktree 层级假设。
  • 验证:聚焦非数据库测试 70 passed、0 failed;本地 PostgreSQL 全迁移与业务链路 1 passed、0 failed;全量前端 1532 passed、0 failedtsc --noEmit 通过;ESLint 0 errors、4 个既有 warningsNext 16 webpack production build 与静态生成通过;git diff --check 通过。四档质量门禁中的 quick、browser、accuracy 已完整通过。release profile 在 Playwright POC 前实际执行的公开发布隐私扫描通过(3212 files、0 findings);随后 report_renderer_isolation_poc.py --strict 因本机 Playwright Chromium 不可用/持续闪退而阻断,并按用户明确要求停止,未将完整 release profile 宣称为 passed,后续不再通过 Playwright 调用 Chrome。停止该 profile 后,另以独立非浏览器命令完成三引擎 parity validator 与 golden cases3/3 passed);业务比对仍如实记录为 92 行中 32 match、60 mismatch、无缺失引擎或高严谨 section,未启用 --require-external-parity,不得解释为外部公式完全一致。staging 仍需以正式 Gitea 独立质量门禁、精确 SHA 部署与非 Playwright HTTP/API smoke 完成验收。
  • 防复发:测试必须读取 canonical registry,不得重新硬编码派生业务真源;正式 V2 RPC 替代 legacy RPC 时必须保留完整成功、幂等、切换、失效和持久化业务覆盖,不能以“旧入口被拒绝”替代纵向链路;跨语言测试必须显式传递当前解释器,不得依赖 PATH 别名或 worktree 深度;类型 fixture 必须跟随共享列模型演进。
  • 相关记录:BUG-178、BUG-181、BUG-189、BUG-190
  • 修复版本:本次功能分支提交(精确 SHA 以提交、远程分支和 staging 发布核对结果为准)

BUG-192 | 外层 .gitignore 漏提交上游 .agentsclean staging package identity 不完整

  • 状态:resolved(本地候选,待 staging 精确 SHA 发布验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:jyotish-vedic-astrology@6.9.14 上游研究快照完整性、immutable package identity、Agent 模块导入与 Gitea 独立 staging 质量门禁。
  • 用户现象:本地全量前端测试通过,但同一提交在 Linux clean checkout 上有 5 个 Agent/registry 测试连锁失败;registry 期望 d3d6d05c…runner 计算为 ec528fc8…,因此 Agent 在 import-time fail closed。
  • 触发条件:版本化 RishiAI 上游参考仓包含 .agents/rules、14 个 .agents/skills 和 14 个 .agents/workflows 文件,但仓库根 .gitignore 的非锚定 .agents/ 规则也匹配该嵌套上游目录,使文件留在本地快照却没有进入 Git tree。
  • 根因:package hash 正确地覆盖完整磁盘树,但外层 ignore 规则把上游 README 明确列为仓库组成部分的 29 个文件静默漏提交。核对结果为本地 1175 files、tracked/clean archive 1146 files;差集正好是 references/open_source_sources/rishi-ai-mcp/.agents/ 的 29 个上游文件。本地完整快照 hash 为 registry 已登记的 d3d6d05c1da25bb684af3be591687f0321b9a3a69a9896454ea3e25b57b6071b,残缺 clean tree 才产生 ec528fc873aca4b971ca8e537d076fefdec47825a11be9572158f1274004d6d6
  • 修复:保留严格的完整 package hash 算法与既有 d3d6… identity;在根 .gitignore 中只为该精确版本化 RishiAI 路径增加反向例外,并提交 29 个上游 .agents 文件。没有全局放开 .agents,也没有把普通未知文件或上游源码排除在 identity 外。
  • 验证:检查 git ls-files 与本地 package traversal 均为 1175 files;当前树和由候选提交生成的 clean Git archive 必须得到同一 d3d6… hash。聚焦 Agent/registry 测试、全量前端测试、TypeScript、ESLint、quick gate 与正式 staging gate 均不得调用 Playwright/Chrome。
  • 防复发:版本化第三方研究快照必须检查外层 ignore 规则导致的漏文件;registry hash 必须从完整、可由 clean checkout 重建的 Git tree 生成,不能通过放宽 hash traversal 掩盖缺失源码。
  • 相关记录:BUG-190、BUG-191
  • 修复版本:本次功能分支提交(精确 SHA 以提交、远程分支和 staging 发布核对结果为准)

BUG-193 | staging revision 状态文件存在但不可读时部署提前失败

  • 状态:resolved(待 staging 精确 SHA 发布验收)
  • 首次发现:2026-08-14
  • 最近更新:2026-08-15
  • 影响面:staging 部署和数据库迁移的 forward-only revision 检查。
  • 用户现象:.state/deployed-revision 已存在但部署用户无法读取时,发布流程在比较旧、新 revision 之前以 Permission denied 退出,无法使用运行中容器的 GITHUB_SHA 完成回退发现。
  • 触发条件:状态文件由不同权限上下文写入,文件存在但当前执行用户没有读取权限。
  • 根因:GitHub staging workflow、run-staging-deploy.shrun-staging-migration.sh 只用 -f 判断文件存在;存在性不代表可读性,因此错误地进入直接读取分支。
  • 修复:将三处判断收紧为 -r;状态文件不可读时按既有协议从当前 Web 容器发现 GITHUB_SHA。Gitea staging deploy workflow 已有相同的可读性判断,保持不变。
  • 验证:新增部署合同测试锁定 workflow 与两个 runner 必须使用 -r;聚焦部署测试、Shell 语法检查、正式 staging gate、精确 SHA 部署与非 Playwright HTTP 健康检查分别记录。
  • 防复发:任何 revision state 快路径都必须验证可读性,并保留容器镜像 revision 的只读回退,不得仅以路径存在作为可消费条件。
  • 相关记录:BUG-083、BUG-192
  • 修复版本:本次 staging 集成提交(精确 SHA 以远端 staging 与部署结果为准)

BUG-194 | 移动端出生资料空状态无法滚动且主题问题被压成窄列

  • 状态:resolved(待 staging 精确 SHA 验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:首页空会话出生资料填写、移动端内部滚动容器、首页主题问题入口。
  • 用户现象:手机上填写出生资料时无法继续向下滚动,底部字段与“继续”按钮不能进入视口;首页十个主题问题在桌面横向挤成窄条,中文几乎逐字换行。
  • 触发条件:宽度不超过 767px 的空会话首页内容高于可视区域;或桌面显示完整主题问题集合。
  • 根因:早期移动端 .conversation.is-empty { display: block; } 声明位于后续桌面 .conversation.is-empty { display: grid; place-items: center; } 之前,被级联顺序覆盖;外层页面又固定为隐藏溢出,只能依赖该内部容器滚动。主题区同时使用单排 flex 并让所有问题共享整体边框,十项内容被压缩到不可读宽度。
  • 修复:把移动端空状态覆盖移动到桌面 Grid 规则之后,恢复独立纵向滚动、iOS 惯性滚动,并为动态视口高度增加 100vh 回退和 100dvh;主题入口改为带独立边框和圆角的响应式 Grid,桌面、平板、手机分别为 3、2、1 列,同时沿用按压态底色与仅桌面 hover 箭头反馈。
  • 验证:移动端滚动与主题布局契约测试、首页入口回归、目标 ESLint 和 git diff --check 通过;真实浏览器在 1440、820、390 宽度下无水平或文字溢出,390×844 与模拟键盘高度 390×500 均可滚动到底部并显示“继续”。
  • 防复发:移动端覆盖必须位于桌面空状态 Grid 之后;测试锁定滚动容器、vh/dvh 顺序、iOS 惯性滚动和主题区 3/2/1 列断点,并禁止恢复为单排 flex 压缩布局。
  • 相关记录:BUG-042、BUG-048、BUG-140
  • 复发自:无
  • 修复版本:本次 staging 集成提交(精确 SHA 以远端 staging 核对结果为准)

BUG-195 | 生时校正焦点已写入却被前端误报为未完成

  • 状态: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 与 idempotentset-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-196 | 新建生时校正错误继承已采用时间和用户误差范围

  • 状态:resolved(本地候选,待 staging 发布与登录态业务验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:初始化出生时间采集、homepage/new 生时校正 Case 基线与候选搜索范围、历史 Session 恢复。
  • 用户现象:用户再次新建校正时,系统可能以上一次采用的分钟而不是最初填报时间为中心,并继续继承旧的前后误差;只有大致时段或完全未知声明的 Profile 也可能被错误当成可创建精确分钟扫描的基线。
  • 触发条件:Profile 同时存在 reported_birth_time、历史 active_birth_time 和 uncertainty,或只有 period/unknown 声明时创建 fresh Case。
  • 根因:fresh Case 的范围推导优先使用 active_birth_time,再直接读取 Profile uncertainty;初始化模型把用户声明误差和引擎搜索窗口混为同一字段,并允许用 period 或全天范围代替具体初始时间。
  • 修复:新填报的准确时间保存为 reported + 0/0,不自动宣称引擎 confirmed;初始化仍允许用户如实声明大致时段或完全未知,但这些声明不能作为 fresh 精确分钟扫描的基线。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;初始化入口回归另由 BUG-197 锁定。与 Focus、receipt 和 migration 回归合并运行 203 passed、0 failed。
  • 防复发:reported_birth_time 是 fresh Case 唯一用户时间基线;active_birth_time 只表示已采用的当前排盘时间,不能反向改写新校正起点;用户声明字段、服务器搜索策略与最终 confirmed truth 必须保持分层。
  • 相关记录:BUG-127、BUG-177、BUG-187、BUG-197
  • 修复版本:本次功能分支提交(精确 SHA 以提交、远程分支与 staging 发布结果为准)

BUG-197 | 精确分钟校正前置条件误删不确定和未知出生时间入口

  • 状态:resolved
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:初始化“出生日期与时间”表单、移动端资料填写、无准确出生时间用户的普通产品入口。
  • 用户现象:表单只显示“我知道准确出生时间”,原有“我不确定准确时间”、大致时段、补充描述和“完全不清楚,跳过出生时间”全部消失,无法准确填写分钟的用户无法继续。
  • 触发条件:staging 包含 075c62e5 后打开未确认出生时间的初始化表单。
  • 根因:BUG-196 修复把“fresh 生时校正 Case 必须有合法 reported_birth_time”错误扩大为“初始化表单只能接受具体时间”,同时测试也被改成明确禁止不确定/未知入口,导致业务回归被质量门禁当成正确结果。
  • 修复:恢复“我知道准确出生时间 / 我不确定准确时间”两条一级选择;不确定路径恢复大致时段、可选描述和完全未知跳过入口,未知状态允许返回描述范围。保留 BUG-196 的服务端边界:准确时间继续保存为 reported + 0/0 且不自动 confirmedperiod/unknown 只能完成资料声明和使用无需分钟的功能,不能创建 fresh 精确分钟扫描。
  • 验证:先把两个错误测试合同改回用户路径合同并确认旧实现稳定失败,再恢复实现后通过;目标回归锁定 family_exact + period_only 两个一级选项、period/unknown 的真实组件分支、跳过入口、reported 状态及 consultation 的 minute-free 行为。390×844 Chrome 真实点击验证两个一级选项、时段表单、完全未知跳过和返回范围按钮均可见;滚动容器 overflow-y: auto,可从 scrollTop=414 滚到 718 并到达底部,继续按钮可进入视口。预览模式的三个 401 来自无登录态的只读背景接口,不影响本表单交互;仍需完成 staging 远端 SHA 验收。
  • 防复发:资料声明完整性和精确分钟校正可启动性必须是两个独立条件;任何 fresh Case 前置条件调整不得删除 period/unknown 资料入口。UI 合同测试必须正向断言两个一级选择、时段选择和跳过路径存在,禁止再用负向断言把产品能力删除写成门禁。
  • 相关记录:BUG-127、BUG-196
  • 修复版本:本次 staging 修复提交(精确 SHA 以远端分支核验结果为准)

BUG-198 | 不确定或未知出生时间被错误拒绝创建生时校正 Case

  • 状态:resolved(本地候选,待 staging 发布与登录态业务验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:/api/rectification/cases/open、首页和新建生时校正入口、只有大致时段或完全未知出生时间的用户。
  • 用户现象:用户已选择“上午/下午/晚上/深夜”等出生时段,或明确选择“完全不清楚”,资料保存成功,但开始生时校正时仍返回 HTTP 422 profile_incomplete,无法进入 Case。
  • 触发条件:Profile 的 reported_birth_time 为空,且 birth_time_sourceperiod_onlyunknown 时,以 homepage/new intent 创建 fresh Case。
  • 根因:BUG-196 将“没有具体分钟不能直接使用 ±15 分钟精细扫描”错误实现为“没有具体分钟不能创建 Case”;case-service.ts 的 fresh 候选范围只接受 reported_birth_time,没有恢复资料模型已经支持的时段范围和全天范围。对应测试也把该错误边界锁定为预期行为。
  • 修复:继续保持 fresh Case 不继承历史 active_birth_time 和 uncertainty;有合法 reported_birth_time 时仍使用服务器控制的前后 15 分钟范围。period_only 改为使用用户已选择的服务器映射时段,包含 late_night 的跨午夜 23:0003:59unknown 使用 00:0023:59。缺少出生日期、地点、时区,或选择 period_only 却没有合法时段等真正不完整组合,仍在调用创建 RPC 前 fail closed。
  • 验证:回归测试先证明旧实现对 period/unknown 稳定抛出 profile_incomplete,修复后锁定 morning 08:0011:59、late-night 23:0003:59、unknown 00:0023:59 均能传入 open_agentic_rectification_case_v2;非法缺时段/缺具体时间组合仍不调用 RPC。既有引擎范围判断支持跨午夜,工具合同已覆盖全天宽范围不伪造中午分钟。
  • 防复发:资料完整性、Case 可创建性和是否可以立即执行分钟级扫描必须分层;宽范围应先通过事件问题逐步缩小,不得以 profile_incomplete 阻止用户进入,也不得生成虚假具体出生时间。
  • 相关记录:BUG-127、BUG-196、BUG-197
  • 修复版本:本次 staging 修复提交(精确 SHA 以远端分支与 staging health 验收结果为准)

BUG-199 | Case 创建把可解析的空时区偏移误判为出生资料不完整

  • 状态:resolved(本地候选,待 staging 发布与登录态业务验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:/api/rectification/cases/open、保存了 IANA 时区但 timezone_offset 为空的全球出生地点资料,尤其是 period_only / unknown 用户。
  • 用户现象:用户已选择“晚上”等合法出生时段,页面也认为出生资料完整,但开始生时校正仍返回 HTTP 422 profile_incomplete
  • 触发条件:Profile 已有出生日期、地点标签、坐标、timezone_id 和合法时间声明,但缓存字段 timezone_offsetnull
  • 根因:资料表单和账户保存合同允许用 IANA timezone_id 表达完整地点,既有普通咨询与 Journey 链路也会按出生日期和参考时间动态解析历史 offset;V9 Case 服务却在调用同一解析器之前直接强制 timezone_offset !== null,把可恢复的派生字段缺失误判成用户资料缺失。
  • 修复:V9 Profile 读取后先调用共享 resolveMissingBirthTimezoneOffset;具体时间使用填报分钟,时段声明使用服务器定义的时段参考时刻,未知时间使用中午参考时刻,只用于解析该日期的历史 UTC offset,不会生成或确认具体出生分钟。解析成功后再执行原有完整性和候选范围校验;解析服务异常映射为 profile_unavailable,不再冒充 profile_incomplete
  • 验证:新增真实服务边界回归,先证明 period_only + evening + timezone_id + timezone_offset null 在 RPC 前稳定抛出 profile_incomplete,修复后确认调用历史时区接口、候选范围仍为 18:0022:59、baseline 使用解析得到的 offset 并成功创建 Case。聚焦测试、Lint、TypeScript、远端 SHA 与 staging 业务结果按本次发布记录补充。
  • 防复发:IANA 时区是地点真相,timezone_offset 是依赖出生日期与参考时刻的派生值;所有需要 offset 的服务必须先走共享解析边界,再区分真正资料不完整与下游服务异常。
  • 相关记录:BUG-127、BUG-198
  • 修复版本:本次 staging 修复提交(精确 SHA 以远端分支与 staging health 验收结果为准)

BUG-200 | 无准确出生分钟时首页主题退化为第三方占星百科问题

  • 状态:resolved(本地候选,待 staging 发布与视觉验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:首页“每日运势”、十个主题问题、Onboarding Agent 生成的事业/关系/时运建议,以及已有 onboarding 缓存。
  • 用户现象:用户进入首页后看到“印度占星一般如何……”“通常会看哪些因素”“包含哪些证据层”等教学式问题;卡片在介绍占星方法,而不是帮助用户直接询问自己的每日运势、事业、关系或未来一年重点。
  • 触发条件:Profile 没有可用于个人星盘的准确出生分钟,首页选择 generalGuidedJyotishTopics;或 Onboarding Agent 生成未使用第一人称的客观式问题。
  • 根因:无分钟降级主题被写成“不依赖个人出生分钟”的占星知识入口,错误地把真实性边界实现成百科模式;同时 Onboarding Agent 只被要求介绍产品能力,服务端也未校验问题是否以用户本人为中心,因此模型输出和缓存都可能继续保存第三方视角文案。
  • 修复:把每日运势和十个无分钟主题统一改成“请帮我……”的任务式请求;首页明确提示出生时间不足的部分会说明限制,但仍允许用户从自己的问题开始。Onboarding Prompt 强制每个建议包含“我”并禁止百科式句型,服务器解析层再次拒绝客观教学文案并回退到安全的第一人称问题;缓存版本升级到 ayanam-onboarding-v4,使旧问题重新生成。
  • 验证:新增回归测试锁定无分钟主题全部为用户视角,并证明客观式 Agent 输出会被拒绝且使用第一人称 fallback;聚焦测试 25/25 通过,目标 ESLint 与 TypeScript --noEmit 通过。真实 staging 视觉验收待发布后完成。
  • 防复发:缺少准确出生分钟只限制分钟敏感的个性化结论,不得把用户入口改写成占星教学。首页卡片和 Agent 推荐问题必须直接表达用户要解决的事;Prompt 约束之外必须保留服务端输出校验和版本化缓存失效。
  • 相关记录:BUG-194、BUG-197、BUG-198
  • 修复版本:本次 staging 修复提交(精确 SHA 以远端分支与 staging health 验收结果为准)

BUG-201 | 咨询完成后的账户刷新重复请求每日星语接口

  • 状态:resolved(staging 质量门禁修复候选,待部署验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:首页每日星语卡片、咨询完成后的账户与积分刷新、POST /api/daily-starlanguage
  • 用户现象:每完成一次普通咨询,浏览器都会再次请求每日星语接口;即使账户返回的标准化出生资料没有任何变化,也会重复生成同一张每日卡片。
  • 触发条件:咨询流成功完成后调用 refreshAccount();账户接口返回与当前状态值完全相同但引用不同的 Profile 对象。
  • 根因:readProfile() 每次都会创建新的标准化对象,refreshAccount() 又无条件用该对象替换 Profile state;每日星语 effect 需要跟踪完整 Profile,因此依赖对象引用并在引用变化后重新执行。问题不在咨询结算,也不能通过移除账户刷新或缩减 Profile 依赖来规避。
  • 修复:保留咨询完成后的账户与积分刷新;新增浅等值引用保持 helper。账户刷新得到的新 Profile 与当前 Profile 所有标准化字段等值时继续使用当前引用,只有真实字段变化时才替换 state,从而避免无意义地重跑每日星语及其他 Profile 对象 effect。
  • 验证:先添加回归测试并确认因 helper 尚不存在而失败;修复后 Profile 引用行为与 refreshAccount() 集成测试 3/3 通过。相关 account、consultation entrypoint、starter questions 聚焦测试合计 53/53 通过;目标 ESLint 与 TypeScript --noEmit 通过。首次独立 staging gate Run 1856 暴露既有 Agentic 测试仍硬编码 setProfile(nextProfile);该测试已改为验证等价的新函数式更新语义,同时继续禁止覆盖 setProfileDraft,避免把正确的引用保持修复误判为回归。git diff --check 按本次本地验收执行。
  • 防复发:服务器资料刷新不得把“值相同”转化为无意义的状态引用变化;依赖完整 Profile 的 effect 必须在真实资料变化时执行,不能为消除重复请求而遗漏依赖字段。
  • 相关记录:BUG-200
  • 修复版本:本次 staging 质量门禁修复提交(精确 SHA 以远端分支与 staging health 验收结果为准)

BUG-202 | 无出生分钟的“每日运势”仍被 General Agent 整段拒绝

  • 状态:resolved(staging 修复候选,待质量门禁与业务验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:首页“每日运势 / 深入看今日”入口、普通咨询请求 schema、服务端咨询路由、无出生分钟 General Agent,以及公共 Panchanga 证据注入。
  • 用户现象:用户已经在初始化资料中如实选择“晚上”等出生时段,但从首页点击“深入看今日”后,Agent 仍回复“今天运势这个请求,我无法在这个模式下回答”,并把用户引导到占星百科问题或生时校正,无法获得首页承诺的“适合推进什么、需要注意什么”。
  • 触发条件:服务端 Profile 的 birth_time_sourceperiod_only 或其他没有具体分钟的状态,首页以 general_no_birth_time 发起 daily_starlanguage 请求。
  • 根因:BUG-200 只修正了首页任务式文案,没有闭环服务端能力合同:前端无分钟请求主动丢弃 daily_starlanguage entrypointGeneral Agent 又只允许百科知识,并把所有 forecast 一律拒绝。初始化保存的出生时段不能安全替代具体分钟,因此也不能直接走个人命盘日运链路。
  • 修复:无分钟请求保留受限的 daily_starlanguage entrypoint,并在服务端确认最终咨询模式后将其展开为“公共日历趋势”问题。新增服务器公共 Panchanga 客户端,仅向 /api/panchanga_range 发送当天日期和已保存地点的经纬度、时区偏移;将经过结构校验的 Vara、Tithi、Nakshatra、Yoga、整体质量、条件标签与计算策略作为 <public-daily-panchanga> 证据注入 Agent。General Agent 只在存在该服务端证据时回答今日整体趋势、适合推进事项、注意事项和一个立即行动;普通无证据的个人预测仍按原边界拒绝。公共数据不可用或字段不完整时 fail closed,不编造答案,并由既有外层流程取消结算。
  • 验证:在最新 origin/staging 基线上,相关 TypeScript 聚焦测试 82/82 通过,覆盖无分钟 daily prompt、请求保留 entrypoint、公共 API 不携带出生分钟、不完整证据拒绝、period_only 不生成个人 serverChart、地点参考来自服务端 Profile、输出 guard 继续拦截个人星盘断言,并兼容既有 Profile 引用保持与 Agentic 生时校正契约;tsc --noEmit 通过,目标 ESLint 通过,Python Panchanga endpoint 测试 2/2 通过。staging 登录态点击、最终流事件、持久化回合与结算不变量仍待发布后验收。
  • 防复发:出生时段必须继续按 period_only 诚实保存,不能转换成时段中点、00:00 或任何候选分钟。无分钟“每日运势”只能使用服务器公共 Panchanga,必须明确它不是个人命盘日运;不得声称个人上升点、宫位、分盘、大运、本命过境叠加、确定事件或精确时间。
  • 相关记录:BUG-127、BUG-198、BUG-200
  • 修复版本:本次 staging 修复提交(精确 SHA 以远端分支与 staging 质量门禁结果为准)

BUG-203 | 生产恢复点因 .state/mutation.lock 所有权漂移无法创建

  • 状态:resolved(待重新通过 staging、release gate 与生产恢复验收)
  • 首次发现:2026-08-15
  • 最近更新:2026-08-15
  • 影响面:Create Production Recovery Point、生产 schema migration 前置恢复门禁,以及后续 production deploy。
  • 用户现象:mainstaging 已同步且 release gate 成功,但生产恢复 workflow Run 1865 在创建备份前失败,日志为 /opt/jyotisha-production/.state/mutation.lock: Permission denied。首次修复后的 Run 1869 仍在 pg_dump 前失败,准确日志为 sudo: a password is required。切换到受限 helper 后,Run 1873 又在同一备份前阶段报告 chown: /opt/jyotisha-production/.state/mutation.lock: Permission denied。改为 child-first 后的 Run 1877 仍在 pg_dump 前报告同一错误;迁移和部署因此持续阻断,生产运行版本未改变。
  • 根因:生产 bootstrap 遗留的 .state 路径或既有 lock 仍为非 deploy 所有。原恢复脚本只执行 install -d -m 700;对已经存在的目录该命令不会恢复所有权,随后由 deploy 打开共享锁即被内核拒绝。首次修复又错误假设主机已为 deploy 配置独立的免密 chown,但真实生产 sudo 边界只允许已审查的 Docker 命令,因此 sudo -n chown 立即失败。第二次修复的 helper 只保留 CHOWN capability,却按“父目录在前、lock 子文件在后”的顺序处理;Run 1873 已先把 .state 改成 deploy:deploy 0700,再因没有 DAC_OVERRIDE 无法遍历到仍未修复的 lock,留下半修复状态。Run 1877 虽改为 child-first,但 helper 启动时父目录已经是不可遍历的 0700 deploy,所以仍无法通过原宿主路径到达 lock。四次失败都发生在 pg_dump 前,没有生成可用恢复证明,也没有执行 schema migration。
  • 修复:恢复脚本先对 .statebackups 和既有 mutation.lock 做类型与非符号链接校验,取得当前运行 PostgreSQL 容器的不可变本地 image ID,再复用既有 sudo -n docker 边界启动一次性所有权修复容器:--pull never、无网络、只读根文件系统、no-new-privileges、删除全部 capability 后只保留 CHOWN。除绑定 .statebackups 外,把已经验证为普通非 symlink 文件的 lock inode 直接绑定到容器 /mutation.lock,先通过该直接 mount 恢复 lock,再恢复两个目录 mount point 到当前 deploy UID/GID。这样无需遍历半修复的 mode-0700 父目录,也不需要增加 DAC_OVERRIDE;不递归改动历史备份、不删除或替换 lock inode,随后仍用同一个 flock -n fail-closed 获取共享 host lock。同步更新生产 runbook 和静态安全契约测试,明确不得扩大主机 sudoers 或 capability。
  • 验证:本地 bash -n、YAML parse、聚焦 workflow contract 与 git diff --check 必须通过;远端必须重新完成 staging quality/deploy、exact-SHA release gate、真实 production dump + disposable restore + off-site artifact,再允许 migration/deploy。
  • 防复发:生产私有状态目录必须保持 deploy:deploy 0700,共享 lock 必须是普通非 symlink 文件且不可通过删除重建来“修复”;任何恢复流程失败都不得手填 restore_verified=true 或跳过恢复门禁。
  • 相关记录:生产迁移 runbook、Run 1865、Run 1869、Run 1873、Run 1877
  • 修复版本:待提交(精确 SHA 以重新发布后的远端分支与 production health 为准)

BUG-204 | 生产发布后报告页因 Next.js chunk 版本偏斜落入通用错误页

  • 状态:resolved(待 staging 与 production 精确 SHA 发布验收)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:发布切换期间已打开旧页面的用户进行客户端导航时,包括 /reports 等动态页面。
  • 用户现象:生产 /reports 显示 This page couldnt load,只能点击 Reload 或手工刷新;同一登录会话刷新后报告列表恢复正常。
  • 根因:生产 Next.js Web 构建未设置 deploymentId。旧标签页仍运行上一发布的客户端 runtime,在新镜像切换后进行客户端导航时请求了当前发布无法匹配的静态 chunk,触发 ChunkLoadError 并落入 Next.js 通用错误页。报告 API、登录态和报告数据本身没有失败。
  • 修复:Web Docker build stage 接收并设置 NEXT_DEPLOYMENT_IDGitea 主发布链与 GitHub fallback 都把各自完整 commit SHA 作为 build argument 注入。Next.js 因而在构建产物中写入 deployment marker,并为静态资源请求附加 deployment query,使跨发布的客户端版本不一致能够触发完整导航,而不是继续加载不匹配的 chunk。新增 workflow 合同测试,锁定 Dockerfile 和两个构建入口都不能丢失该参数。
  • 验证:聚焦 workflow 合同测试 36/36 通过;使用固定 40 位测试 SHA 的真实 Next.js production build 成功,生成 HTML 含 data-dpl-idJS/CSS URL 含同一 ?dpl= 参数。远端仍需完成 staging quality/deploy、release gate、生产恢复点与 restore drill、migration gate、production deploy,以及登录态 /reports 浏览器验收。
  • 防复发:所有可发布 Web 镜像必须在 next build 阶段注入与镜像/发布清单相同的完整 Git SHA;仅设置容器运行时变量无效。发布验收必须覆盖已登录页面和静态资源 deployment marker,不能只看 /api/health
  • 相关记录:deploy/README.mddeploy/railway-web.Dockerfile、staging/production exact-SHA release workflows
  • 修复版本:待提交(精确 SHA 以重新发布后的远端分支与 production health 为准)

BUG-205 | 普通咨询参数重试复用已拒绝计算缓存并误报运行合同未完成

  • 状态:resolved(本地修复,未提交、未发布)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:普通咨询 Mastra Agent 在同一 Agent 执行尝试内对 run-jyotish-consultation 的参数纠正、服务端运行合同补跑与计算 Promise 去重。
  • 用户现象:普通咨询运行较长时间后返回“Agent 未完成必要的方法与计算步骤,本次不会扣点”,实际主咨询 workflow 一次也没有执行。
  • 触发条件:模型首次同时传入 domains 与兼容字段 theme,触发 invalid_consultation_domain_plan;随后模型或服务端合同补跑改用合法 theme: timing 再次调用同一上下文绑定工具。
  • 根因:createConsultationTools()canonicalDomainPlan(input, ctx) 参数校验前就创建并缓存 calculation Promise。首次非法调用产生的 rejected Promise 被永久保留;后续合法调用命中缓存后直接复用旧拒绝,导致 workflow 调用数保持为零,最终由既有合同门禁判定 runtime_contract_incomplete
  • 修复:在读取或写入计算缓存前同步完成域计划校验,非法参数不启动、不计数也不污染缓存;实际计算 Promise 拒绝时仅清除仍指向该 Promise 的缓存,使后续调用可以重新执行。成功 Promise 继续保留,合法并发调用仍共享同一次服务器计算。未移除运行合同门禁,未伪造 workflow receipt 或成功状态。
  • 验证:新增 invalid domains + theme → valid theme: timing 回归,确认首次 workflow 调用数和工具调用计数均为零、第二次合法调用执行一次并完成合同;新增 workflow Promise 首次拒绝后后续调用不复用旧拒绝的回归;既有合法并发调用只计算一次测试继续通过。frontend/tests/consultation-agentic-runtime.test.ts 共 15/15 通过。
  • 防复发:所有可复用的请求级 Promise 必须先完成同步输入合同校验再缓存;rejected Promise 不得长期占用幂等缓存。并发去重测试必须同时覆盖合法并发、非法后合法重试和真实异步拒绝后的缓存释放。
  • 相关记录:BUG-186、BUG-189
  • 修复版本:本地未提交候选

BUG-206 | 新用户完成初始化后首页生时校正打开失败被静默吞掉

  • 状态:resolved(本地修复,未提交、未发布)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:新用户完成出生资料初始化后,从首页“生时校正”卡片打开 V9 Agentic Rectification Case 的入口错误处理。
  • 用户现象:用户完成初始化资料后点击首页“生时校正”,页面没有切换,也没有显示任何错误,看起来像按钮没有反应。
  • 根因:POST /api/rectification/cases/open 返回 profile_incomplete 时,客户端仅在本地 missingProfileStep(profile) 非空时处理;新用户本地资料已被判断完整时该分支不更新任何 UI 状态便直接返回。网络异常同样只写入未渲染的 composer notice,导致 Case 未打开时没有可见反馈。
  • 修复:服务端 profile_incomplete 统一进入既有资料重新确认流程;invalid_open_request 与网络异常写入可见的 rectification error;首页入口附近增加 role="alert" 错误提示。成功响应后的 Case/Session 合并与校正界面切换逻辑保持不变。
  • 验证:新增修复前失败的首页 open 错误回归,锁定 profile incomplete、invalid request、网络异常和可见 alert;修复后聚焦入口测试 69/69 通过,git diff --check 通过。
  • 防复发:首页业务入口不得把 API 失败只写入未渲染状态;本地资料完整与服务端资料不一致时必须提供可见错误和恢复动作,不能静默返回。
  • 相关记录:BUG-177、BUG-198、BUG-199
  • 修复版本:本地未提交候选

BUG-207 | 管理端业务操作重复要求邮箱验证码、手工原因与二次确认弹窗

  • 状态:resolved(补充修复已完成,待提交与发布)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:管理端兑换码生成/编辑/撤销、管理员角色变更、账务与订阅调整、商品保存/发布、功能开关发布、模型发布、易支付设置等写操作。
  • 用户现象:管理员已经登录后台并具备对应权限,执行批量生成兑换码等日常操作时仍需发送邮箱验证码;首轮删除邮箱验证后,又先填写“操作原因”,提交真实业务表单后还出现额外的“确定”弹窗,形成连续二次确认。
  • 根因:多个管理写入被统一接入 operation-level requireHighRiskAdminMutation,公共 ReasonActionModal 内置权限级邮箱 OTP challenge/proof;后续只把它替换成 ConfirmActionModal,仍保留了多余的操作级确认层,没有让真实的数据录入表单直接执行。
  • 修复:所有管理业务写入统一使用 requireAdminMutation,继续强制管理员会话、对应权限与可信 Origin;删除 /api/admin/reauth、high-risk challenge/proof cookie、ReasonActionModalConfirmActionModal、相关 Popconfirm / Modal.confirm 与 pending-confirm 状态。真实的数据录入 Modal 继续保留,但点击表单的“生成 / 保存 / 发布”等主按钮即直接执行;无额外参数的撤销、重试与开关动作由原按钮直接执行。客户端不再提交手工 reason,服务端按动作注入固定审计标识并继续传给数据库 RPC 的非空审计字段。
  • 安全边界:保留 request ID、数据库 actor 身份校验、领域 RPC、细粒度权限、可信 Origin、append-only 审计和最后一位 Owner 保护;保留账户级 Better Auth TOTP MFA 与一次性恢复码;普通用户登录、注册和找回密码的邮箱 OTP 不受影响。
  • 验证:全局源码扫描确认管理业务中不存在 ConfirmActionModalPopconfirmModal.confirmreason-action-modal 或“操作原因”;管理端权限、业务直提交流程、兑换码/账务合同、账户级 MFA 等 6 个聚焦测试文件共 52/52 通过;tsc --noEmit、改动 TS/TSX 文件 ESLint 与 git diff --check 通过。
  • 防复发:新增管理业务时只能在登录、权限、Origin、服务端审计标识和数据库审计边界内扩展;不得把邮箱 OTP、手工原因或通用二次确认弹窗放回日常管理操作。只有真实的数据录入/选择表单可以使用 Modal,账户级 MFA 与普通用户身份验证必须保持独立。
  • 相关记录:BUG-155、BUG-156、BUG-209
  • 修复版本:本地未提交候选(首轮邮箱复核移除:4f6cf5782d3871a28e531a4dff9bcc6a2633ce09

BUG-209 | self-hosted 管理端生成兑换码先返回通用 500,随后合法管理员被权限链拒绝

  • 状态:resolved(本地修复,待提交、迁移与发布)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:self-hosted runtime 的兑换码列表、批量生成、编辑与撤销;普通用户兑换和 production 未在本次修复中验证。
  • 用户现象:第一次提交合法点数、数量、到期时间和备注时,POST /api/admin/codes 返回 500 {"error":"后台服务暂时不可用"};修正参数序列化后,已登录且能进入后台的管理员再次提交无 reason 请求,返回 {"error":"无权执行此操作"}
  • 根因:存在两个独立问题。第一,runCodeRpc() 将 JavaScript 对象数组直接作为 $5::jsonb 参数交给 node-postgres,被编码成 PostgreSQL array 文本而不是 JSON 数组。第二,兑换码页面、Refine access-control、API 与 PostgreSQL wrapper 使用了不一致且过窄的 billing.adjustments.write;部分合法后台角色只有所有管理员共有的 admin.access,因此请求在 UI、API 或数据库任一层都可能被拒绝。
  • 修复:调用 public.admin_create_redemption_codes 前显式执行 JSON.stringify(input.p_codes)。兑换码列表、创建、编辑、撤销的 UI 可写判断、Refine 资源读写权限、GET/POST/PATCH/DELETE 路由及三个 PostgreSQL wrapper 全部统一为 admin.access。新增向前迁移重建 wrapper 与审计 trigger,把兑换码审计行的 permission_used 统一写成 admin.access;保留服务端固定审计标识、原 RPC 签名、request ID、明文码仅单次返回和数据库 actor 身份校验。
  • 验证:本地使用项目实际 pg serializer 对比确认显式序列化后保持合法 JSON 数组文本;源码合同锁定兑换码 UI、Refine、四个 API 方法、三个数据库 wrapper 与审计权限一致,且不再依赖客户端 reason 或操作确认弹窗。相关 6 个聚焦测试文件共 52/52 通过;tsc --noEmit、改动 TS/TSX 文件 ESLint 与 git diff --check 通过。
  • 发布要求:必须先把 20260816010000_admin_redemption_admin_access.sql 应用到 staging 数据库,再部署同一精确 SHA;只部署应用代码仍会被旧 PostgreSQL wrapper 按 billing.adjustments.write 拒绝。
  • 防复发:self-hosted pgjsonb 参数必须显式 JSON 序列化;一个管理资源的列表、UI access-control、API guard、数据库 permission check 与审计 permission_used 必须使用同一权限语义,不能只改前端或 API。
  • 相关记录:BUG-155、BUG-207
  • 修复版本:本地未提交候选

BUG-208 | 生时校正 opening 首步依赖模型主动加载 Skill,失败时只返回 run.started → run.failed

  • 状态:resolved(staging 发布候选,待质量门禁与业务验收)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:POST /api/rectification/agent 的 V9 Agentic Rectification opening/普通 turn、Skill 绑定收据、首步 Case 读取与公开 NDJSON 事件。
  • 用户现象:已通过鉴权、Case/Session 绑定和模型校验的 opening 请求,只收到 run.started 后紧接 run.failed,没有可见的 Skill、Case 或回答事件。
  • 触发条件:服务端已经通过 agent.getSkill() 加载并核验 Case 绑定的不可变 Skill,但首个 provider step 仍使用自动工具选择;模型直接回答,或先调用 rectification-read-case 而没有先主动调用框架 skill 工具时,运行器按 skill_not_loaded / skill_not_bound fail closed。attempt 内的活动与文本在成功前统一缓冲,因此该合同错误在公开流中折叠成只有 run.started → run.failed
  • 根因:Skill 的真实性与版本已经由服务器加载和校验,但运行合同仍把“是否完成绑定”交给模型是否主动选择 skill 工具,形成服务器事实与模型行为之间的不一致;首步 Case 读取同样没有由服务器强制。该缺陷可确定性复现用户现象,但在缺少 staging 运行日志时不据此断言某个具体 provider 一定返回了直接文本或特定工具序列。
  • 修复:要求 agent.getSkill() 返回非空指令,并将其作为本 attempt 的服务器 system bootstrap 注入;在 provider 执行前持久化唯一 Skill receipt 和 skill.bound phase,并将 Skill 标记为已绑定。通过 Mastra prepareStep 把 step 0 的可用工具缩减为 rectification-read-case 且强制调用;重试提示一并放入 bootstrap,不再覆盖 stream instructions。模型若冗余调用 skill 不会重复写入收据,其他校正工具在 case.loaded 前仍继续 fail closed。
  • 验证:新增回归覆盖服务器 Skill 指令注入、首步强制 rectification-read-case、无需模型调用 skill 即可完成、Skill receipt 只写一次,以及 getSkill() 缺失时 provider stream 不得启动。Rectification Agent/stream/Skill registry 聚焦测试 53/53 通过;目标 ESLint、TypeScript --noEmitgit diff --check 通过。部署同构 Docker build target 成功,镜像内 @mastra/core1.50.1
  • 防复发:服务器已经确定的 Skill 身份、指令和首个事实读取步骤不得再依赖模型自动选工具;所有 provider 调用前必须完成可审计的 Skill 绑定,首步工具面保持最小化,并继续以最终 run.completed、持久化 Turn 和计费不变量作为部署后验收标准。
  • 相关记录:BUG-177、BUG-198、BUG-206
  • 修复版本:本次 staging 发布候选(精确 SHA 以远端 staging 与健康检查验收为准)

BUG-209 | 用户选择准确出生时间后仍停留 reported,个人报告固定返回 birth_time_not_usable

  • 状态:resolved(本地候选,待 staging 迁移、精确 SHA 发布与登录态报告验收)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:初始化出生资料保存、账户资料编辑、POST /api/reports 出生时间可用性门槛、既有准确时间 Profile。
  • 用户现象:用户在初始资料明确选择“我知道准确出生时间”并填写具体分钟,资料与地点均完整,但生成个人报告仍返回 422 birth_time_not_usable
  • 触发条件:Profile 保存为 birth_time_source=family_exact、前后误差均为 0 且有合法 reported_birth_time,但 active_birth_time 仍为空、birth_time_status 仍为 reported;报告接口正确要求 accepted/confirmed + active_birth_time,因此请求必然被拒绝。
  • 根因:账户资料写入逻辑把所有非引擎确认的出生时间声明统一降为 reported + active null,没有表达“用户明确采用自己提供的准确分钟”这一独立状态。初始化资料声明与报告事实门槛各自符合旧合同,但组合后准确时间永远无法成为报告可用时间。
  • 修复:账户资料应用层只对 family_exact + 0/0 + 合法分钟 写入 active_birth_time=reported_birth_timebirth_time_status=accepted,继续保留原始 reported_birth_time,绝不伪装为 confirmed;同一准确声明重新保存可修复既有 reported,修改已采用的准确分钟会同步新的 active time。带 10/15 分钟误差的 family 声明、approximate、period-only、unknown 仍保持 reported + active null,普通资料编辑仍不得覆盖 confirmed。新增 forward-only 业务迁移,仅回填无校正 Case、active 为空、状态为 reported 的严格 0/0 family-exact 记录;该迁移只进入 frontend/supabase/migrations,不污染 identity-only frontend/db/migrations
  • 验证:账户回归覆盖新建、既有 reported 原样重存、已 accepted 分钟修改、confirmed/legacy confirmed 保护及所有非严格准确来源,13/13 通过;账户、出生时间 intake、报告 API 与报告入口聚焦测试 82/82 通过。PostgreSQL 全业务迁移测试实际执行新增 migration,验证严格 0/0 记录得到 05:00:05:00:accepted10 分钟误差记录保持 active null + reported1/1 通过;TypeScript --noEmit、目标 ESLint 与 git diff --check 通过。
  • 防复发:reported 表示用户声明但尚未采用,accepted 表示用户明确采用为当前排盘输入,confirmed 只表示引擎或校正流程确认;任何初始化来源语义变更必须同时覆盖 Profile 持久化、历史回填、报告服务端门槛和客户端入口,不得通过放宽报告接口读取未采用的 reported_birth_time 绕过事实边界。
  • 相关记录:BUG-125、BUG-196、BUG-197
  • 修复版本:本地未提交候选

BUG-210 | 用户填报具体出生分钟被生时校正状态错误阻断精确应期

  • 状态:resolved(本次 staging 发布候选)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:普通咨询 unverified_birth_time 模式的 Consultation Plan、Python workflow 精度边界、Agent/legacy 回答 receipt 与确定性日期输出过滤。
  • 用户现象:用户已经明确填报到具体分钟并使用个人星盘咨询,回答仍以生时未校正为由拒绝精确应期,真实大运或阶段日期被替换成 [具体时间已省略];生时校正因此被错误实现成查看精确日期的付费前置条件。
  • 触发条件:服务端 Profile 含合法 reported_birth_time,咨询模式解析为 unverified_birth_time,且 workflow 原始证据本可允许精确应期。
  • 根因:TypeScript Consultation Plan 把除 verified_chart 外的所有模式统一投影为 precise_timing_blockedworkflow 返回后 applyBirthTimeModeToWorkflowContext() 又无条件把 can_answer_precise_timing 改为 false。最终输出 guard 根据被强制阻断的 receipt 删除年月日,而不是根据计算证据是否完整决定。
  • 修复:有具体分钟的 verified_chartunverified_birth_time 统一使用 server_evidence_required,精确应期权限由服务器计算证据决定;未校正模式继续保留 birth_time_confidence=unverified_reported_timecandidate_is_confirmed=false,但不再覆盖 workflow 的精度许可。无出生分钟的 general_no_birth_time 继续使用 precise_timing_blocked,证据确实不足时仍保留原确定性 guard。输出事件、receipt 字段和回答结构未改变。
  • 验证:回归测试先稳定复现 unverified plan/receipt 被强制 blocked 和日期脱敏,修复后确认具体填报分钟投影为 server_evidence_required、证据允许时 receipt 为 allowed、真实起止日期保持原文,同时证据阻断和无分钟模式仍继续过滤不允许的精确日期。
  • 防复发:生时校正状态只能作为出生时间来源与置信度元数据,不得充当普通咨询功能 entitlement;精确应期许可必须由服务端证据完整性决定。Plan、workflow context、receipt 与输出 guard 的回归必须同时覆盖 verified、reported-minute 和 no-minute 三种模式。
  • 相关记录:BUG-194、BUG-198、BUG-200
  • 修复版本:本次 staging 发布候选(精确 SHA 以远端 staging 为准)

BUG-211 | 多领域咨询 receipt 可生成但无法写入聊天记录

  • 状态:resolved(本次 staging 发布候选)
  • 首次发现:2026-08-16
  • 最近更新:2026-08-16
  • 影响面:普通咨询首轮完成后的整段聊天记录 PATCH、立即追问流程及 workflowReceipt.domains 持久化。
  • 用户现象:首轮多领域回答正常完成,用户立即追问时返回 HTTP 400 聊天记录格式不正确,问题被放回输入框,第二次 /api/consult 未开始。
  • 触发条件:assistant message 同时包含公开 workflowReceipt.domainsagentExecutionReceipt.workflow.domains,前端在下一轮生成前 PATCH 完整消息数组。
  • 根因:公开 Agent 事件使用的 canonical workflowReceiptSchema 已允许可选 domains,聊天写入合同却重复维护了一套 .strict() 旧 schema,导致 messages[].workflowReceipt.domains 被 Zod 判定为 unrecognized_keys;嵌套 execution receipt 使用新版 schema,因此同一业务对象在两个位置具有不同合法字段。
  • 修复:聊天写入合同直接复用 consultation-agent-events.ts 导出的 workflowReceiptSchemaWorkflowReceipt 类型,删除重复字段定义;API、NDJSON、消息和 receipt 输出结构不变。
  • 验证:新增与真实失败 payload 同形的回归,assistant message 同时携带顶层与 execution receipt 的 domains=[general,timing]chatSessionWriteSchema 成功解析;既有 same-origin PATCH、错误重试和所有权边界测试继续通过。
  • 防复发:跨响应、UI 状态和持久化边界共享的 receipt 必须只有一个 canonical schema;禁止在写入合同中复制 .strict() 子结构。新增字段必须以包含完整真实消息形状的 round-trip 回归验证。
  • 相关记录:BUG-186、BUG-189
  • 修复版本:本次 staging 发布候选(精确 SHA 以远端 staging 为准)

BUG-212 | 管理端新生成兑换码复制为 [object Object]

  • 状态:resolved(本地修复,待提交与发布)
  • 首次发现:2026-08-17
  • 最近更新:2026-08-17
  • 影响面:管理端批量生成兑换码后的“完整兑换码(仅显示本次)”弹窗。
  • 用户现象:完整兑换码在页面上显示正常,但点击复制图标后,剪贴板内容是 [object Object],无法直接发送或兑换。
  • 根因:Ant Design Typography.Paragraph 开启了布尔值 copyable,其直接子节点却是嵌套的 React Typography.Text 元素;组件默认复制子节点时把 React 元素对象字符串化,因而写入 [object Object],而不是业务字段中的明文兑换码。
  • 修复:为每个新生成兑换码的 copyable 显式指定 text: record.code ?? "";显示仍使用 code 样式,完整明文仍只存在于本次创建响应与当前弹窗,不改变列表脱敏和服务端存储边界。
  • 验证:兑换码管理源码合同锁定复制源必须是 record.code,禁止再次依赖嵌套 React 子节点的默认字符串转换;运行对应聚焦合同测试及 git diff --check
  • 防复发:只要可复制 UI 的 children 不是直接字符串,就必须显式提供 copyable text;一次性秘密值不得从 mask、ReactNode 或对象隐式转换。
  • 相关记录:BUG-207、BUG-209
  • 修复版本:本地未提交候选

BUG-213 | 会员页显示预置套餐但管理端商品列表为空

  • 状态:resolved(本地修复,待提交、迁移与发布)
  • 首次发现:2026-08-17
  • 最近更新:2026-08-17
  • 影响面:管理端商品列表与详情;会员页公开套餐读取的数据来源说明。
  • 用户现象:会员页显示“体验卡 / 标准月卡 / 标准年卡”,但 GET /api/admin/products 返回 {"data":[],"total":0},看起来像套餐被前端写死且无法在后台管理。
  • 根因:套餐不是前端常量,而是 20260806020000_billing_products_subscriptions.sql 预置到 billing_products / product_entitlements 的默认发布商品。公开 /api/payment/packages 通过 service_runtime 能读取这些记录;管理 API 通过受限 admin_runtime 查询。原迁移虽然授予 admin_runtime 表级 SELECT,但两张表已启用 RLS,且只创建了 anon/authenticated 公开策略,遗漏 admin_runtime SELECT policy,导致合法管理查询被 RLS 静默过滤为零行。
  • 修复:新增向前迁移,为 admin_runtime 重新授予 billing_productsproduct_entitlements SELECT,并分别创建 using (true) 的管理员只读策略。API 仍先执行 billing.products.read 权限校验;未给 admin_runtime 增加 service_role 成员关系,也未放开直接写表,保存与发布继续只能经过既有审计 RPC。
  • 验证:数据库回归以真实 admin_runtime 连接读取预置 standard_monthly 商品及其 3 条权益;同时保留无法 set role service_role 与敏感 Profile 列不可读断言。发布后还需确认管理商品接口不再为空,并与公开套餐接口中的商品 ID/版本一致。
  • 防复发:对启用 RLS 的管理资源,table grant 与 RLS policy 必须成对验证;后台列表测试必须使用 admin_runtime 真实角色,不能只用 schema owner 绕过 RLS。
  • 相关记录:BUG-156、BUG-209
  • 修复版本:本地未提交候选

BUG-214 | 计算重试成功后仍误报运行合同未完成并丢弃已算出的星盘

  • 状态:resolved(本地修复,未提交、未发布)
  • 首次发现:2026-08-17
  • 最近更新:2026-08-17
  • 影响面:/api/consult 个人咨询在 run-jyotish-consultation 出现任何一次瞬时失败后的全部后续行为。
  • 用户现象:staging 事业类咨询运行约一分钟后返回 run.failed / runtime_contract_incomplete,提示“Agent 未完成必要的方法与计算步骤,本次不会扣点”。事件流显示 Skill 已加载、星盘实际已计算成功,用户却拿不到任何回答文本。
  • 触发条件:run-jyotish-consultation 首次调用失败,模型据此重试,并在后续某次调用中成功。观测到的事件序列为两次 tool.failed calculation_failed、一次 tool.completed(20278ms),随后补跑命中缓存再次 tool.completed55ms)。
  • 根因:合同门禁 contractReady() 要求 consultationToolCallCount === 1,而该计数在 createConsultationTools() 中对每次未命中缓存的执行递增,失败尝试同样计入。BUG-205 的修复让被拒绝的计算缓存可以释放、从而允许重试,但门禁仍按总尝试次数判定,导致只要发生一次瞬时失败,计数就永久大于 1,之后无论计算是否成功都不可能满足合同。服务端补跑无法降低计数,因此纯属浪费,最终以 runtime_contract_incomplete 结束并丢弃已经算出的结果。
  • 修复:新增 consultationToolSuccessCount,仅在工作流真正成功时递增;门禁改为判定成功次数为 1。失败尝试继续记入 consultationToolCallCount 供观测使用,但不再影响合同。未放宽单次计算边界:请求级缓存保留成功 Promise,后续调用一律复用,因此每个请求仍最多执行一次计费计算;两次真实成功计算依然判定为违约。
  • 验证:新增修复前失败的回归,复现“两次失败 + 一次成功 + 补跑命中缓存”序列并断言 run.completed 且回答正常输出;新增“两次成功仍然违约”边界回归以锁定单次计算约束;工具层补充断言失败重试后 consultationToolCallCount=2consultationToolSuccessCount=1。回退门禁到旧实现可确认新回归失败。frontend/tests/consultation-agentic-runtime.test.ts 17/17 通过。
  • 防复发:运行合同门禁只能依据成功语义的计数,不得用包含失败尝试的总调用次数;任何允许重试的缓存改动,必须同步检查下游门禁是否仍按尝试次数判定。合同类回归必须覆盖“失败后恢复”与“重复成功”两个方向。
  • 相关记录:BUG-205、BUG-186、BUG-189
  • 修复版本:本地未提交候选

待跟进

前两次 calculation_failed 的服务端原因尚未定位(工作流超时为 90s,两次失败均在 20s 内,可排除超时)。本条修复只保证瞬时失败可恢复,不替代对失败本身的排查。

排查所需的可观测性已随本批补齐:calculation_failedsafeToolError() 的兜底码,除中止与超时外的一切失败都会被压成它,而上游真实错误文本属于 provider payload,按 agent-observability.ts 的封闭契约不得进入日志。因此改为按封闭机器码分类:runConsultationWorkflow 抛出带 codeConsultationWorkflowError,按 HTTP 状态区分 workflow_rate_limited(429)、workflow_bad_request(400)、workflow_queue_full(503)、workflow_server_error(5xx) 等,并单独标识 workflow_contract_invalidHTTP 通过但响应未过 consultationWorkflowResponseSchema,此种情况 Python 侧日志显示成功,仅凭访问日志无法发现)。该码记入运行步骤的 failureCode,经 agentObservabilityToolCallSchema 的新增受控可选字段进入观测日志。同时把 request_id 透传给 Python API,用于与其访问日志交叉对齐;此前两侧无任何关联标识,只能靠时间戳猜测。公开回执改由 publicConsultationRuntimeSteps() 按白名单构建,内部 failureCode 不出现在对外契约中——executionStepSchema 是 strict,若直接透出会让成功运行在解析回执时报错。