Since BUG-989 a new chat is saved on its first send, and that create already carries the session's model. selectSessionModel still PATCHed the session right away; with no row yet the route answered 404 and the composer showed 「云端同步失败」 although the local choice had taken effect. The selection now returns before any PATCH for an unsaved empty consultation (same predicate as the send path). Saved chats sync as before. Full suite 4988 / fail 24, identical to 11333460; build keeps / Static. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2.2 MiB
Bug History
本文件是本仓库产品与代码 Bug 的长期知识库。处理任何 Bug 前先读并搜索本文件;完成修复或确认阻塞后,在同一变更中更新本文件。
基础设施、外部引擎、碎片目录和开工预检类风险仍记录在 docs/research/pre_work_error_ledger.md。同一问题若同时影响两类台账,应互相引用,不复制大段内容。
使用流程
- 用报错原文、接口路径、状态码、模块名和用户操作搜索本文件。
- 找到相似记录时,先验证既有防复发措施是否仍存在,再定位新的回归入口。
- 修复必须包含与风险相称的自动化测试;生产问题还要记录部署版本和脱敏后的生产验证。
- 完成后更新已有记录,或按下方模板添加新记录。复发问题必须填写
复发自,不能伪装成无关的新问题。 - BUG 编号必须唯一。追加新记录前先搜索本文件当前最大编号,并从其后继续分配;出现重复编号时保留较早记录的编号,为后加入的记录分配新编号,并同步修正指向它的
相关记录与复发自。 - 不记录姓名、出生资料、邮箱、用户/案例 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]、Supabasechat_sessions - 用户现象:用户点击删除后对话无法真正删除,或删除交互与账户弹窗不一致。
- 触发条件:登录后删除自己的一条历史会话。
- 根因:删除曾缺少所有者约束的服务端入口和对应数据库授权,前端也没有统一的站内确认面。
- 修复:增加所有者约束的服务端删除接口与数据库 grant,并统一站内确认弹窗。
- 验证:
tests/test_session_management_entrypoints.py、frontend/tests/chat-session-delete-contract.test.ts。 - 防复发:删除必须经服务端所有权校验;测试同时锁定 API、迁移和前端入口。
- 相关记录:无
- 复发自:无
- 修复版本:
e3d619c、65b8c42、5650ed1、fcf5794
BUG-002 | Onboarding 首次请求 409、重复请求或读到旧问题
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-21
- 影响面:
POST /api/onboarding、首页入门问题缓存与恢复 - 用户现象:出生资料已经填写,第一次请求仍返回资料未完成或
pending,随后又请求一次并返回缓存结果。 - 触发条件:入门资料完成、缓存生成尚未结束或资料在并发生成期间发生变化。
- 根因:缓存就绪/处理中状态没有完整绑定当前资料指纹,旧请求完成时可能覆盖新资料的生成结果。
- 修复:用全部决策字段生成无明文 SHA-256 身份;领取和完成都使用版本、时间戳和 pending 身份 CAS,并校验账户所有权。
- 验证:onboarding route/cache/candidate completion 测试矩阵,历史完整前端套件 455/455。
- 防复发:缓存命中、pending、超时回收和完成写入必须绑定同一资料身份;任何旧请求只能返回安全
pending。 - 相关记录:无
- 复发自:无
- 修复版本:
28d58d4、5ee925c、308e5d5、e13d595、346ec02、a9ffbd9
BUG-003 | 浏览器原始报错 “The string did not match the expected pattern” 泄露给用户
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-21
- 影响面:生时校正自动流程、候选时间确认、“都不符合”分支
- 用户现象:选择候选时间或“都不符合”后直接看到浏览器英文 DOMException。
- 触发条件:自动或手动生时流程中的底层浏览器/传输异常进入未归一化错误分支。
- 根因:部分 journey effect 直接展示实现层异常信息,没有统一转换为安全、可操作的中文错误。
- 修复:所有相关 mutation/effect 统一经
birthTimeUserError归一化,屏蔽 DOMException、网络和语法实现细节。 - 验证:
frontend/tests/birth-time-user-errors.test.ts包含该英文原文回归用例。 - 防复发:禁止
setError(caught.message);新增异步入口必须走统一错误映射。 - 相关记录:无
- 复发自:无
- 修复版本:
a38a096
BUG-004 | 点击生时校正后先进入中间卡片,失败时无法自然回到首页
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:首页生时校正入口、首轮加载与恢复 UI
- 用户现象:点击入口后先看到“开始生时校正”或“正在恢复账户里的校正进度”卡片;依赖失败时停留在重试卡片。
- 触发条件:首轮校时 RPC 尚未返回或返回失败。
- 根因:页面在首轮有效 turn 生成前就切换到专用校正会话。
- 修复:首轮生成期间保持首页;只有有效首轮 turn 返回后才进入校正会话,失败以首页输入区提示呈现。
- 验证:
frontend/tests/consultation-entrypoint.test.ts及生产入口 smoke。 - 防复发:会话切换以“首轮可展示结果”而非“请求已发出”为边界。
- 相关记录:ERR-087
- 复发自:无
- 修复版本:
dc0077e
BUG-005 | 保存出生时间返回 PATCH /api/account 500
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:账户出生资料、Supabase
profiles - 用户现象:选择记录时间和误差后显示“暂时无法保存账户资料”,接口返回 500。
- 触发条件:账户已被候选时间写入 reported 字段,之后再次编辑原始出生时间声明。
- 根因:旧触发器把候选/活动时间复制为用户报告时间,同时数据库又把报告时间视为不可变字段。
- 修复:报告时间保持可编辑,禁止候选时间反写原始声明,修复不一致历史行并增加来源/时间一致性约束。
- 验证:迁移
20260721140000已进入生产账本;生产合成账户修改与恢复均返回 200。 - 防复发:候选、活动、用户报告时间保持独立语义;profile persistence 测试锁定迁移行为。
- 相关记录:ERR-088
- 复发自:无
- 修复版本:
dc0077e、20260721140000_repair_reported_birth_time_revision.sql
BUG-006 | 生时校正首轮在没有历史事件时进入确认态并返回 409
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:
POST /api/birth-time-conversation首轮、计费释放 - 用户现象:等待约一至两分钟后首轮返回
action_conflict,费用预留被释放。 - 触发条件:技术扫描在零条历史事件时直接返回
ready_for_confirmation。 - 根因:应用构建了
confirming首轮,而数据库契约只允许首轮为active;技术就绪错误绕过了三条有效历史事件的业务门槛。 - 修复:所有技术包统一经过
MINIMUM_SCOREABLE_EVENTS门禁;未满三条时清除 result ID,并保持pending_validation/active。 - 验证:核心 orchestrator/e2e 回归测试;生产首轮随后成功创建并收费一次。
- 防复发:确认态只能由三条以上有效、已发生、可评分事件触发。
- 相关记录:ERR-089
- 复发自:无
- 修复版本:
b8ed740
BUG-007 | finance 证据通过应用校验但被数据库拒绝为 409
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:生时校正技术包、事件证据、Supabase durable validators
- 用户现象:真实首轮计算完成后仍返回统一的
action_conflict。 - 触发条件:D2/D11 产生
finance建议主题,或摘要包含应用已支持的domain字段。 - 根因:TypeScript 已支持
finance和摘要domain,初始 SQL 枚举及表约束仍是旧版本。 - 修复:向前迁移同步 evidence request、life event、private candidate、public recap 和事件表约束。
- 验证:迁移
20260721150000已进入生产账本;迁移契约测试和生产首轮 smoke 通过。 - 防复发:应用证据枚举变更必须同时更新 durable SQL,并由迁移契约测试检查。
- 相关记录:BUG-006、ERR-090
- 复发自:无
- 修复版本:
b981c4e、20260721150000_align_conversational_finance_domain.sql
BUG-008 | 后续证据把范围压得过窄后返回 503
- 状态:resolved
- 首次发现:2026-07-21
- 最近更新:2026-07-22
- 影响面:生时校正后续回答、技术包构建
- 用户现象:首轮、“都不符合”、暂停恢复均正常,但提交后续明确事件时返回
service_unavailable。 - 触发条件:事件评分产生的窄区间不足两个时间样本或不足两个可区分分盘主题。
- 根因:这是“证据仍不足”的正常业务状态,代码却把它作为
TypeError依赖故障终止。 - 修复:显式分类区间区分度不足;撤回本次过度收窄,保留上一候选范围、评分证据和未确认状态,然后继续提问或安全保存范围。
- 验证:核心回归 85/85;生产从失败点续跑通过;全新生产 smoke 覆盖资料、首轮、“都不符合”、暂停恢复、多事件、范围终态、计费和问题交接。
- 防复发:任何新候选范围必须先满足技术证据契约;不满足时回退,不返回 503,也不伪造确定分钟。
- 相关记录:ERR-091
- 复发自:无
- 修复版本:
b981c4e
BUG-009 | 未校正的已填报时间被降级为零星盘咨询
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生日初始化、普通咨询路由、生时校正提示
- 用户现象:已填写具体出生时间但没有完成生时校正时,普通问题仍提示只能回答一般知识或必须先完成校正。
- 触发条件:出生时间来源包含有效具体分钟,但状态不是
confirmed,且当前聊天没有旧的临时授权状态。 - 根因:前端把“使用未校正填报时间”设计成逐会话授权;没有授权时默认退回
general_no_birth_time,因此完全绕过现有的未校正星盘安全模式。 - 修复:具体填报时间现在自动进入
unverified_birth_time,保留禁止精确应期的安全边界但不禁用个人分析;无具体分钟时直接进入无分钟模式;移除校正 toast、弹窗和每条回答前重复的校正警告,并把生日入口收敛为“知道准确时间 / 不确定准确时间”两个选择。 - 验证:
frontend/tests/birth-time-consultation-consent.test.ts、frontend/tests/consultation-entrypoint.test.ts、frontend/tests/consultation-birth-time-mode.test.ts、frontend/tests/birth-time-intake.test.ts。 - 防复发:咨询路由测试锁定“有效填报分钟无需授权即可使用”;页面契约禁止重新引入生时校正 toast 或阻断式选择。
- 相关记录:BUG-003、BUG-004
- 复发自:无
- 修复版本:本次 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.ts、frontend/tests/session-model-persistence.test.ts、frontend/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.ts、frontend/tests/evidence-audit-panel.test.ts、frontend/tests/consultation-report-export.test.ts锁定聊天消息不再挂载这些内部组件。 - 防复发:聊天消息渲染契约禁止直接展示
techniqueTruth和workflowReceipt;需要运营或调试时使用独立的受控界面。 - 相关记录:BUG-009
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-012 | 自动生时流程重构后再次直出实现层错误
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正自动出题与评分轮询、
use-birth-time-automatic-journey-effects - 用户现象:自动流程失败时可能再次显示底层异常原文,而不是稳定、可操作的中文提示。
- 触发条件:自动出题或评分轮询 Promise 进入异常分支。
- 根因:出生时间流程重构保留了 automatic effect 中直接读取
caught.message的旧分支;原回归测试后来只检查 guided hook,因此没有锁定 automatic hook 的两个入口。 - 修复:automatic effect 的出题与轮询异常统一经过
birthTimeUserError;回归测试同时检查 guided 与 automatic 两个 hook,并禁止两种直接展示caught.message的写法。 - 验证:
frontend/tests/birth-time-user-errors.test.ts。 - 防复发:错误归一化测试按入口文件验证安全不变量,不再依赖单文件精确调用次数。
- 相关记录:BUG-003
- 复发自:BUG-003
- 修复版本:待提交(PR #25)
BUG-013 | 精简生时校正文案后真实 Chromium 测试等待已删除标题
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:
frontend/tests/conversational-rectification-component.test.ts、前端完整质量门禁 - 用户现象:页面已经正确渲染候选时间、经历和输入控件,但测试持续等待并最终报
Timed out waiting for async initial turn。 - 触发条件:运行真实 Chromium 390px 组件回归测试,并把 active turn 注入精简后的生时校正界面。
- 根因:产品把重复的“当前判断”叙事改成“已记录”行动文案后,浏览器测试仍以旧标题作为异步渲染完成信号。
- 修复:测试改为等待候选代表时间与已记录经历两个稳定结构信号,不再绑定可变标题文案。
- 验证:
frontend/tests/conversational-rectification-component.test.ts。 - 防复发:真实浏览器等待条件优先绑定语义结构和状态数据,不用已批准可调整的展示标题充当加载边界。
- 相关记录:BUG-004
- 复发自:无
- 修复版本:待提交(PR #25)
BUG-014 | 能力审计新增案例验证主题后质量门禁仍断言旧主题集合
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:
tests/test_api_server_security.py、Python quick quality gate - 用户现象:能力审计正确返回
Case Validation并将案例验证评为可产品化,但质量门禁仍按旧主题集合和thin等级失败。 - 触发条件:运行能力审计测试,且应用可见主题已包含案例验证入口。
- 根因:案例验证能力进入
_app_visible_topics后,对应主题集合和 UX 等级断言都没有同步更新。 - 修复:把
Case Validation纳入预期可见主题,并把case_validator从thin队列移入excellent断言,保持测试与同一审计规则一致。 - 验证:
tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources;Python quick quality gate。 - 防复发:扩展应用可见主题时必须同时更新能力审计契约测试;精确集合断言继续用于发现意外增删。
- 相关记录:无
- 复发自:无
- 修复版本:待提交(PR #25)
BUG-015 | 分钟校正 safeguards PR 与主线证据契约分叉
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:分钟候选输入身份、公共 holdout 校验、三引擎证据包、PR quality gate
- 用户现象:PR 与
main在五个文件发生冲突,聚焦测试通过但完整 quick quality gate 在 API 安全测试收集阶段失败。 - 触发条件:基于旧基线开发 v1 holdout safeguards,同时主线独立升级到 v2/v3 证据契约并移除旧 API store 导入。
- 根因:PR 复用了旧 holdout 字段和
true node默认,未执行与 CI 相同的 quick quality gate;主线已经采用mean node和更严格的 sealed holdout schema。 - 修复:以主线为准新增兼容的输入指纹、邻近分钟探针和语义哈希;新增 v4 独立审核、日级事件、假分钟承诺门禁及非生产 intake;不恢复旧自动评估循环。
- 验证:分钟校正聚焦回归、脚本直接执行检查、Ruff、Python compilation 和 quick quality gate。
- 防复发:新 safeguards 必须以当前 schema 向前升级;候选身份必须继承产品计算默认;PR 验收必须包含 workflow 实际执行的 quick quality gate。
- 相关记录:BUG-009、BUG-014、ERR-045、ERR-053、ERR-086
- 复发自:无
- 修复版本:d7d9703
BUG-016 | 生时校正首轮被扫描稀疏度或候选分区超时打成 503
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:首页“先完成生时校正”、
POST /api/birth-time-conversation首轮技术包 - 用户现象:点击生时校正后仍停留在普通咨询页,并显示“生时校正暂时无法继续”;接口返回 503。
- 触发条件:候选扫描包含真实但不连续的时点差异,或动态候选分区依赖在 45 秒内没有返回。
- 根因:技术包只承认相邻分钟切换,错误拒绝了稀疏扫描中的真实范围差异;同时首轮把用于改善问题排序的候选分区依赖当成不可降级的硬依赖。生产版本
b981c4e的日志分别记录了RectificationTechnicalPacketRangeError和candidate_differences TimeoutError。 - 修复:稀疏扫描差异改用明确的范围级文案,禁止伪称相邻分钟切换;超过六小时的宽范围首轮跳过分钟级候选分区,较窄范围在候选分区发生已知超时、取消、配置或服务故障时保留服务端扫描证据并继续收集,不再返回 503;未知编程或契约错误仍失败关闭。
- 验证:
frontend/tests/conversational-technical-packet.test.ts、frontend/tests/conversational-rectification-route.test.ts、真实 Chromium 390px 组件回归和npm run build通过;完整前端套件 844 项中 837 项通过,余下 7 项均为沙箱 Chromium / PostgreSQL 端口环境失败,针对性 Chromium 在沙箱外 8/8 通过。 - 防复发:首轮必须区分核心扫描失败与可降级的候选排序依赖;稀疏差异文案必须显式否认相邻分钟切换;生产验收必须核对接口状态与实际进入校正会话。
- 相关记录:BUG-004、BUG-008、ERR-091
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-017 | 候选范围终态继续原问题必定返回 409
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正范围终态、
POST /api/consult、durable 原问题交接 - 用户现象:生时校正安全结束并保存候选范围后,点击“继续原问题”仍提示原问题状态变化,接口返回 409。
- 触发条件:分钟确认发布门禁未通过,校正以
candidate.status=pending_validation完成;页面按未确认分钟选择unverified_birth_time并携带 durable handoff。 - 根因:页面正确区分确认分钟与保存范围,但咨询接口把所有 handoff 硬限制为
verified_chart;范围终态因此在计费和模型调用前被拒绝。 - 修复:handoff 允许
verified_chart与unverified_birth_time两种星盘模式,继续拒绝general_no_birth_time、旧校正入口和不一致 request ID;分钟确认门禁保持不变。 - 验证:生产合成账号复现
409 mode_changed;frontend/tests/consultation-birth-time-mode.test.ts锁定范围终态模式与一般咨询隔离;发布后需复跑 durable claim、正常咨询和聊天删除 smoke。 - 防复发:范围终态和精确确认必须分别覆盖 continuation 模式;不得把“拥有 handoff”错误等同于“已经确认出生分钟”。
- 相关记录:BUG-008、BUG-009、BUG-016、ERR-085、ERR-091
- 复发自:无
- 修复版本:待提交(生产 smoke 待当前精确 SHA 发布后补齐)
BUG-018 | 首次保存未确认出生时间没有写入档案状态
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:账户出生资料保存、未确认星盘咨询、生时校正后继续原问题
- 用户现象:已经保存“大概时间”等出生资料,但继续咨询时仍提示出生时间状态发生变化,接口返回 409。
- 触发条件:档案没有旧的 active minute、legacy minute、candidate 或 case pointer,第一次经账户接口保存未确认出生时间声明。
- 根因:账户写入只在清除旧候选结果时补写
birth_time_status=reported;干净档案和首次 upsert 被提前跳过,留下完整声明但空状态,随后被服务端星盘真值校验拒绝。 - 修复:所有未确认声明变更都原子写入
reported并清空不可沿用的应用结果;首次创建档案时也写入同一状态;确认分钟仍禁止被普通资料编辑覆盖。 - 验证:
frontend/tests/account-api.test.ts覆盖空状态档案与首次档案写入;生产使用认证账户重新保存合法声明后复跑范围终态 handoff、模型咨询、计费和会话删除。 - 防复发:出生声明与
birth_time_status必须由同一次服务端写入建立,不允许依赖后续校正流程补齐状态。 - 相关记录:BUG-009、BUG-017
- 复发自:无
- 修复版本:本次自动滚动修复提交
BUG-019 | 原问题交接租约过期后永久显示处理中
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正范围终态、跨设备/失败重试、durable 原问题 handoff
- 用户现象:一次继续咨询在输出前失败后,刷新仍无法重试,接口返回“原问题状态已经变化”。
- 触发条件:handoff 处于
claimed或executing,两分钟租约已经过期,客户端重新加载交接。 - 根因:数据库 projection 只看持久化 state,把过期租约仍投影为
in_progress;客户端因此不会发起 claim,而执行 RPC 又正确拒绝过期租约,形成永久卡死。 - 修复:projection 将已过期的
claimed/executing租约投影为pending,复用既有加锁 claim RPC 原子接管;未过期租约仍保持in_progress,双设备隔离不变。 - 验证:迁移契约测试锁定过期租约恢复语义;生产制造过期 claim 后重新加载、claim、执行模型咨询和 settle。
- 防复发:所有租约型状态的读取投影必须同时解释 lease deadline,不允许只依赖枚举 state。
- 相关记录:BUG-017、BUG-018
- 复发自:无
- 修复版本:待提交
BUG-020 | 生时校正把语言问答包装成高阻力卡片表单
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:首页生时校正会话、经历录入、候选核对与更正
- 用户现象:每轮同时出现候选卡、领域按钮、年份与月份下拉、经历回顾卡和确认卡;用户需要理解多层控件才能回答一个问题,移动端尤为费力。
- 触发条件:进入生时校正并提交或更正一条真实经历。
- 根因:后端已有自由文本证据契约,但前端仍用结构化表单和多卡片包装;校正叙事 Agent 也未加载 Jyotish Skill 来生成自然、逐问式的取证措辞。
- 修复:改为一段助理提问加一个自然语言输入框;候选状态和证据历史收进可展开进度区;保留明确确认、更正、持久化、计费、计算和分钟验证门禁;叙事 Agent 加载 Jyotish Skill,但服务端技术包仍是候选事实和确认权限的唯一来源。
- 验证:语言优先组件静态与真实 Chromium 390px 回归 8/8 通过;校正路由与叙事契约 23/23 通过;目标文件 ESLint 和 Next.js production build 通过。
- 防复发:语言问答不得重新拆成领域按钮或日期下拉;Skill 只能改进问题策略与措辞,不得生成、重算或确认候选时间。
- 相关记录:BUG-008、BUG-009、BUG-015、BUG-016、BUG-017
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-021 | 自然语言真实事件在分钟评分入口被静默丢弃
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正自然语言抽取、历史事件持久化、分钟评分、首轮与兜底叙事
- 用户现象:用户已经提供带日期的真实经历,系统仍反复索取证据;首轮回答展示完整 D 层技术清单,却没有用一个具体问题推进取证。19 世纪事件、疾病、手术、事故和丧亲尤其容易无法参与评分。
- 触发条件:日期早于 1900 年;事件属于健康或重大压力;累计有效事件超过 6 条;或叙事模型未满足首轮全量技术层合同而进入确定性兜底。
- 根因:前端日期正则写死 1900–2099;健康类关键词落入
other,并在路由中被过滤,尽管后端已有health_pressure与 D30 评分支持;路由另有独立的 6 条截断;叙事校验强制首轮正文列出全部稳定层、敏感层和值。 - 修复:日期抽取支持 1000–2099 的四位历史年份;健康、事故、丧亲映射到既有
health_pressure/D30 合同并贯通持久化、旧数据导入和公开 turn schema;评分截断统一复用 8 条收敛上限;技术包和 validation receipt 继续完整保存,但非最终用户正文只呈现候选范围、未确认边界和一个高信息量问题,确定性兜底也遵守相同语言交互。 - 验证:抽取、叙事、路由聚焦测试 59/59 通过;真实案例回放、编排和端到端契约 65/65 通过;目标文件 ESLint、Python replay/compilation、
git diff --check和 Next.js production build 通过。公开 smoke 中 Steve Jobs、Einstein、Marie Curie 分别保留 4、3、4 条可评分事件,产品过滤结果与结构化 oracle 一致。 - 防复发:新增 18xx 中文与 ISO 日期、健康/事故/丧亲、8 条事件上限和单问题兜底断言;技术层可进入服务端证据包,不得重新进入普通会话正文;family/other 仍是持久化背景,不得伪装成已评分领域。
- 相关记录:BUG-008、BUG-009、BUG-015、BUG-016、BUG-020
- 复发自:BUG-020
- 修复版本:待提交(本地可测)
BUG-022 | 生时校正入口等待首轮请求完成后才切换会话
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:首页生时校正入口、普通咨询中的“先完成生时校正”、侧栏恢复校正会话
- 用户现象:点击生时校正后长时间停留在首页或原咨询 session,直到恢复、建案、计算和会话持久化全部完成才突然跳转,用户容易误以为点击无效并重复操作。
- 触发条件:
start、resume、durable handoff 或 session persistence 任一请求响应较慢。 - 根因:
openBirthTimeRectification虽然先设置了 loading,但专属 session 的插入、激活和校正 surface 的开放都位于首个异步请求之后;surface 还要求首个 turn 已存在,因此 loading 状态无法在目标会话中显示。 - 修复:创建或复用专属 session 后,在任何
await之前立即插入并激活该 session;校正 surface 允许以空 turn 渲染既有“正在建立校正记录…”状态;增加同步 in-flight 门禁防止快速重复点击发出多个启动请求;失败时新建 session 回滚到来源会话,复用 session 则保留可见错误与重试入口。 - 验证:
frontend/tests/consultation-entrypoint.test.ts21/21 通过,锁定 session 插入、激活和 loading surface 必须早于首个异步请求,并锁定单请求门禁;目标文件 ESLint、git diff --check和 Next.js production build 通过;真实应用内 Chromium 从首页点击时,在后台恢复完成前已经显示“正在建立校正记录…”,从普通会话切回校正 session 也立即显示目标会话。 - 防复发:首页卡片、普通 session 建议和侧栏恢复必须继续共用同一启动函数;不得重新用首个 turn 是否返回作为 session 可见性的条件。
- 相关记录:BUG-016、BUG-020、BUG-021
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-023 | 初始化地址保存后的加载提示仍指向生时评估
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:新用户初始化流程、出生地点保存后的全屏加载提示
- 用户现象:用户填完出生地址后,页面实际准备进入首页,但加载提示仍显示正在生成生时评估,造成流程去向与界面文案不一致。
- 触发条件:初始化流程完成出生时间填写,并提交最后一步出生地点。
- 根因:出生时间保存和出生地点保存共用
saving_profile展示阶段;该阶段文案仍按旧流程描述为即将生成生时评估。 - 修复:新增独立的
entering_home展示阶段,仅在saveOnboardingPlace提交地址时使用,显示“正在进入首页 / 出生资料已保存,正在为你准备首页。”;出生时间保存继续使用原saving_profile阶段。 - 验证:聚焦测试锁定地址提交与
entering_home的绑定及目标文案;目标文件 ESLint 与git diff --check通过。 - 防复发:初始化步骤的加载文案必须绑定实际导航结果;不得通过修改共享
saving_profile文案改变出生时间提交阶段的语义。 - 相关记录:BUG-022
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-024 | 侧栏会话标题与更多操作被拆成两块
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:聊天记录侧栏、当前会话选中态与更多操作入口
- 用户现象:会话标题显示在一块选中背景中,右侧更多操作却以独立圆形按钮悬在外侧,看起来像两个不一致的控件。
- 触发条件:侧栏展开并选中任意会话。
- 根因:选中背景只应用在左侧
.session-main,右侧.session-menu-trigger又单独使用50%圆角和悬停背景。 - 修复:把选中、悬停和聚焦背景统一应用到整行
.session-row;子按钮保持透明,并移除更多操作按钮的独立圆形底色。 - 验证:侧栏契约测试锁定整行选中背景、透明标题按钮和非圆形更多操作按钮;目标文件 ESLint 与
git diff --check通过。 - 防复发:会话标题和行内操作必须共享同一个行级状态面,不得分别绘制互相竞争的选中背景。
- 相关记录:无
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-025 | 健康压力追问被数据库误判为校正操作冲突
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正首条自然语言回答、
POST /api/birth-time-conversation、Supabase 对话持久化契约 - 用户现象:用户提交一条带年月的真实经历后等待约一分钟,接口返回
409 action_conflict,页面提示加载最新进度后重试;重复提交仍无法进入下一问。 - 触发条件:技术包为下一轮选择
health_pressure(健康与重大压力)作为待补证据领域。 - 根因:应用层已把
health_pressure作为正式领域,并允许语言问答每轮只追问一个重点领域;durable SQL 既缺少该领域,又要求 evidence request 至少包含两个领域。完整计算和叙事生成完成后,save_conversational_rectification_turn才以conversational_action_conflict拒绝公开 turn。 - 修复:新增向前迁移,将
health_pressure同步加入四个 durable validator 和事件证据表约束,并把 evidence request 基数从 2–4 对齐为应用契约的 1–4;保留现有幂等、版本和候选确认门禁。 - 验证:真实浏览器复现确认只有
conversational_rectification_valid_evidence_request失败,其余公开 turn 子结构、事件、回执和私有候选均通过;迁移应用到测试 Supabase 后,用同一条“2023 年 3 月离家来北京工作”重放,POST /api/birth-time-conversation返回200,日志为actionKind: answer、resultCategory: success,事件规范化保存为2023-03 · 离家来北京工作,页面继续自然追问学业经历且输入框恢复可用;聚焦迁移测试锁定四个 validator 与表约束必须共同支持该领域。 - 防复发:应用 evidence domain 枚举新增值时,必须同时更新 TypeScript schema、公开 recap/request、私有候选、事件表约束和迁移契约测试;不得把正常领域扩展映射为通用 409。
- 相关记录:BUG-007、BUG-020、BUG-021
- 复发自:BUG-007
- 修复版本:待提交(测试 Supabase smoke 通过)
BUG-026 | 首页与会话改版后旧测试阻断生产发布门禁
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:production 发布门禁、首页主题入口、会话输入区、生时校正组件服务端渲染测试
- 用户现象:production 应用构建成功,但完整前端测试有 7 项失败,导致最新
main无法通过发布前验证。 - 触发条件:运行完整前端测试;旧断言仍查找已移除的首页证据预览、固定 180px 模型菜单、无条件输入区类名和旧
profile文案来源;旧手动 workflow 还固定在 Node 20,而当前@mastra/core明确要求 Node 22.13 以上。 - 根因:首页与普通 session 的布局和文案已经更新,测试契约没有随产品行为同步;
Jyotish Skill Tests的 Node 版本也落后于其他 production 门禁和应用依赖声明,使 Node 20 把 GSAP ESM 错误解析成 CommonJS namespace。 - 修复:测试改为锁定当前首页主题选择、内容自适应模型菜单、带首页修饰类的停靠输入区和
profileDraft文案来源;GSAP 使用正式命名导出;手动 production 测试 workflow 与其余门禁统一到 Node 22。 - 验证:6 个相关测试文件、Node 22 完整前端测试、ESLint、production build 与 patch check 全部通过后方可发布。
- 防复发:产品 UI 结构调整时必须在同一提交同步源码契约测试;workflow 的 Node 主版本不得低于已锁定运行时依赖声明的最低版本。
- 相关记录:BUG-022、BUG-024、BUG-025
- 复发自:无
- 修复版本:待提交(production gate)
BUG-027 | 已完成的生时校正会话打开后误建新案例
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正历史会话恢复、范围终态展示、首页继续入口与计费
- 用户现象:范围校正完成后刷新,点击侧边栏原会话会卡在“正在建立校正记录”,或跳到另一个会话并提示加载最新进度;首页同时重新显示开始入口。
- 触发条件:已绑定 case 的生时校正会话对应
completed/abandoned终态,而账户接口只投影当前未完成 case;或者账户另有一个更新的未完成 case。 - 根因:会话打开逻辑只按账户当前 case 决定 resume/start,没有优先恢复所点击会话自己的
rectificationCaseId;历史终态加载后又会覆盖账户当前可恢复 case,异步账户刷新还拒绝接受null或另一个最新 case。 - 修复:已绑定的生时校正会话始终以自身 case id 执行只读 resume;只有首页或未绑定会话才按账户当前 case 复用或创建;历史终态不再覆盖另一个未完成 case,终态后账户刷新接受服务端最新投影。
- 验证:入口契约测试锁定历史会话按自身 case 恢复、首页重新校正仍创建独立会话,以及历史终态不覆盖当前未完成 case;发布后用 production 合成账号复查范围终态、刷新恢复、首页入口与余额不变量。
- 防复发:聊天会话的
rectificationCaseId是历史查看的第一绑定键;账户rectificationCase只代表当前未完成案例,二者不得相互替代。 - 相关记录:BUG-017、BUG-019、BUG-020
- 复发自:无
- 修复版本:待提交(production gate)
BUG-028 | 生时校正收到具体经历后仍重复泛问
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正自然语言追问、事件补充信息合并、会话可见回复
- 用户现象:用户已经描述自己的具体经历,助理没有针对该内容继续追问或反馈,而是重复上一轮的泛化问题。
- 触发条件:用户提供的事件缺少年月并在下一轮单独补时间;或已经回答技术包当前首选领域后,下一轮仍沿用同一领域提示。
- 根因:存在三处断链:会话组件忽略服务端已经生成的针对性
turn.narrative,转而根据公开 evidence request 重组固定问题;用户先说事件、下一轮只补年月时,两轮分别保存为“无日期事件”和“无内容日期”,未形成可评分事实;服务端下一问直接采用技术包首选领域,没有排除本轮已经回答的领域。 - 修复:会话组件改用独立的可见叙事函数,优先承接用户最近一条具体但不完整的经历并只追问缺失项;编排器把下一轮仅含日期或仅含内容的补充合并回最近一条待澄清事件,同时用
correctsEvidenceIds保留 append-only 修订链;下一问从技术包允许领域中优先选择尚未回答的领域,并同步覆盖公开 evidence request。 - 验证:相关自然语言抽取、叙事、编排、路由、公开案例回放与端到端测试共 111 条全部通过;回归覆盖“先说离家工作、再补 2023 年 3 月”后合并为
2023-03 · 离开家去北京开始工作、具体事件缺日期时回复必须复述该事件并询问年月、关系领域回答后下一问切换到事业领域。目标文件 ESLint 与补丁检查通过。 - 防复发:保持“服务端叙事是用户可见回复真相源”的纯函数测试;每条待澄清证据必须测试跨轮补全与修订链;下一领域必须测试排除有效 recap 已覆盖领域;端到端断言不得只绑定泛化提示词。
- 相关记录:BUG-020、BUG-021
- 复发自:BUG-021
- 修复版本:待提交(本地可测)
BUG-029 | 生时校正仍有未回答区分领域时提前结束
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正多领域追问、候选范围停滞终止策略
- 用户现象:用户只回答两轮后,系统仍有财务或关系等可区分领域未询问,却直接保存宽候选范围并结束。
- 触发条件:自然语言单轮抽取出多条可评分经历,使累计数量达到最低门槛;候选范围连续两轮未变化,同时技术包仍建议尚未回答的领域。
- 根因:停滞终止只检查可评分经历数量和候选范围是否连续不变,没有检查当前技术包是否仍存在未回答的区分领域。
- 修复:停滞结束前增加未回答建议领域门禁;仍有可区分领域时继续自然追问,全部建议领域已覆盖后才允许按范围停滞安全结束。证据数量上限和无可区分问题终止保持不变。
- 验证:编排器回归锁定学业、搬迁、事业三轮后必须继续询问尚未覆盖的关系领域,补充关系事件后才保存未确认范围;本地真实流程修复前复现为两轮即结束且范围仍为
04:30–06:30。 - 防复发:停滞终止测试必须同时断言候选范围未变化和技术包建议领域已全部回答,不能只按事件条数结束。
- 相关记录:BUG-019、BUG-028(编号存疑:BUG-019 疑为重复编号合并前的旧编号,可能应指 BUG-021)
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-030 | 职位和管理职责变化未识别为事业证据
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时校正自然语言证据分类、事业领域覆盖判断、下一轮追问
- 用户现象:用户已经说明“开始承担管理职责”或“职位发生明显变化”,系统记录事件后仍继续要求事业经历。
- 触发条件:事业变化使用“职位”“任职”或“管理职责”描述,但没有出现“工作”“升职”“职业”等原有关键词。
- 根因:事业领域分类词表缺少常见的岗位和职责变化表达,导致可评分事件被归入
other,无法计入事业领域覆盖。 - 修复:将“职位”“任职”“管理职责”加入事业领域分类规则,保留既有日期、评分和持久化契约。
- 验证:抽取器回归覆盖三种带年月的岗位与职责变化表达,均分类为
career、保留月份并可参与评分;本地真实流程已复现修复前的漏判行为。 - 防复发:事业领域分类测试必须覆盖入离职、晋升以及不含“工作/职业”字样的职责变化表达。
- 相关记录:BUG-028、BUG-029
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-031 | 生时纠正未收敛仍永久扣费且事件事实未进入评分契约
- 状态:resolved
- 首次发现:2026-07-22
- 最近更新:2026-07-22
- 影响面:生时纠正计费终态、事件评分输入、候选计算指纹、用户结果说明
- 用户现象:用户完成多领域问答后只得到宽候选范围,当前排盘时间没有更新,但已永久扣除点数;同时用户描述的具体经历虽然出现在对话记录中,传给分钟候选评分器时只剩领域和日期。
- 触发条件:公开分钟盲测门禁尚未通过,流程按范围结束或用户主动放弃;以及可评分经历带有具体事件摘要时进入生产评分路径。
- 根因:启动结算在候选计算完成后立即把预留点数标记为
charged,范围终态和放弃终态没有对应退款;前端评分载荷与 Python 输入规范只序列化id/domain/date/precision,丢弃eventSummary,导致不同事实可能共享同一规范输入。 - 修复:新增向前迁移,在范围完成或主动放弃的同一数据库事务中将已收费状态幂等转换为
released、退回点数并更新动作回执;只有通过分钟门禁且用户明确确认的分钟保留收费。评分载荷新增可选summary,Python API 规范化并限制长度,候选输入契约升级为rectification-candidate-input-v2,把具体事件摘要纳入 canonical hash。用户终态文案明确说明未纠正成功、本次不计费、候选代表时间不会替换当前排盘时间。 - 验证:前端生时纠正路由、编排器、存储和旅程引擎聚焦测试 77 条通过;Python 对话数据库契约、事件 API 与候选评分测试 67 条通过。回归明确断言事件摘要变化会改变 canonical input hash,但
can_apply、confirmation_allowed和can_narrow_to_minute仍保持关闭;SQL 契约断言范围结束与放弃均在终态事务内退款,且迁移不写入active_birth_time。 - 防复发:任何新增事件评分字段都必须进入前端载荷、Python 规范化输入和 canonical hash;任何非
confirmed终态都必须有幂等计费回收断言;不得用候选范围中点或代表时间替换用户的当前排盘分钟。真正分钟确认仍依赖独立来源审计、sealed blind replay、准确率/误确认率及 ±1/2/5 分钟稳定性门禁。 - 相关记录:BUG-019、BUG-028、BUG-029(编号存疑:BUG-019 疑为重复编号合并前的旧编号,可能应指 BUG-021)
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-032 | 生时校正覆盖历史回答且完成后无法返回原问题
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正逐轮问答、Agent 可见叙事、完成态原问题交接
- 用户现象:多轮回答后,用户经历全部连续显示在右侧,页面只保留最新一条 Agent 回复;Agent 收到具体事项后仍重复模板式说明;完成后点击“返回原问题”没有可见跳转。
- 触发条件:在同一个生时校正 case 中连续提交两轮以上经历;或从普通咨询问题进入校正并走到完成态。
- 根因:聊天区直接遍历
evidenceRecap生成全部用户气泡,却只渲染当前turn.narrative,因此旧 Agent 回复天然被覆盖;可见叙事层和编排器在 Agent 生成回答后又用固定进度文案覆盖,且 Agent prompt 没有收到最新具体事件;完成态页面会提前自动领取 continuation,handoff 缺失时又错误地把当前校正 session 当作来源 session。 - 修复:controller 为当前 case 维护交错的
assistant/user消息序列,成功提交后原子追加用户原话与新 Agent 回复,聊天区只按该序列渲染;可见层优先采用 Agent 原始 narrative,中间轮 prompt 带入最新事件并要求先具体回应再追问,编排器保留通过校验的 Agent 文本;删除完成态自动 continuation,只在用户点击时领取,并按本地 handoff、持久化 return session、最近普通咨询 session 的顺序寻找真实返回目标。 - 验证:聚焦 controller、Agent narrative、visible narrative、orchestrator 和首页 handoff 测试 89 条通过,覆盖
assistant → user → assistant历史、具体事件进入 prompt、旧模板不覆盖 Agent 回答、仅点击后返回来源 consultation session。组件 SSR 测试在当前 Node 环境仍被仓库既有 GSAP ESMregisterPlugin加载错误阻断,尚未进入业务断言。 - 防复发:主聊天区不得从 evidence recap 反推当前会话消息;任何 Agent narrative 后处理不得抹掉已校验的具体回应;完成态 continuation 不得自动领取,也不得以当前 rectification session 作为来源会话兜底。若需要刷新后永久恢复每轮 Agent 原文,必须扩展服务端 turn/RPC 持久化契约,不能用当前 narrative 冒充完整历史。
- 相关记录:BUG-018、BUG-019、BUG-028(编号存疑:BUG-018、BUG-019 疑为重复编号合并前的旧编号,可能应指 BUG-020、BUG-021)
- 复发自:BUG-018、BUG-028(编号存疑:BUG-018 可能应指 BUG-020)
- 修复版本:待提交(本地可测)
BUG-033 | 生时校正缺少连续事件语义导致模板式跳问
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正中间轮 Agent prompt、事件纠错与追问顺序、聊天区可见回答
- 用户现象:用户已经补充某件经历的原因、主动或被动、结果或后续转折,Agent 没有继续处理当前事件,反而重复要求泛化领域事件;通过校验的自然回答后还会追加“累计条数”“下一领域”“本轮区分重点”等模板。
- 触发条件:同一事件需要两轮以上补齐,历史事件与最新表述存在日期矛盾,或某领域已有证据但当前事件仍缺关键细节。
- 根因:narrative context 只携带本轮抽取结果,没有完整事件账本、原始表述、纠错状态和未决事实;编排器又无条件把确定性进度模板拼到 Agent narrative 后面,因此模型既无法发现跨轮矛盾,也无法决定应先补完当前事件还是切换领域。
- 修复:中间轮 narrative context 新增最新用户原话、最近事件账本、有效/失效纠错状态和未决证据;prompt 明确要求先完成当前事件、核对日期冲突、合并同一事件的原因与结果且不得重复计分,每轮只问一个信息量最高的问题;通过 grounding 校验的 Agent narrative 直接作为可见回答,仅在模型 fallback 时保留确定性进度文案。
- 验证:聚焦测试覆盖完整事件账本进入 prompt、历史日期与最新原话同时可见、连续事件规则进入 output contract、有效 Agent 回答不再追加进度模板,以及 fallback 仍保持安全的一问式文案。测试数据全部为虚构案例,不写入真实用户经历。
- 防复发:不得把“领域是否出现过”当作切换话题的唯一条件;任何新增对话规划信息必须先进入受限 narrative context,并保持
minute_holdout_not_ready、confirmation_allowed: false、can_narrow_to_minute: false等分钟安全门禁不变。 - 相关记录:BUG-028、BUG-029、BUG-032
- 复发自:BUG-028、BUG-032
- 修复版本:待提交(本地可测)
BUG-034 | 生时校正 Agent 降级只记录 200 导致模板回退不可诊断
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正中间轮自然语言回答、服务端可观测性、网页端响应时延
- 用户现象:用户提交明确事件后等待约 53 秒,网页仍显示“当前累计”“下一步”“本轮区分重点”等固定进度模板;接口日志只有成功的 200,无法看出 Agent 已经连续两次生成或校验失败。
- 触发条件:叙事模型调用抛错、输出不是契约 JSON,或输出未通过 packet grounding 校验并在第二次重试后降级。
- 根因:
generateRectificationNarrative会把所有失败收敛为安全 fallback,但此前仅把失败类型写入持久化 validation receipt,没有输出脱敏运行日志;补充日志后真实网页请求确认了两类误杀:其一,校验器机械要求中文必须逐字包含“已经发生/过去、年、月”,导致语义正确的“后来在什么时候”“年份和月份”等自然问法被丢弃;其二,只要一段自然叙事同时提到多个既有年份并出现“还是”,就会被误判为禁止的宽年份选项问卷,即使“还是”实际在询问毕业方式或职业转折类型。 - 修复:fallback 边界保留结构化脱敏日志;日期请求改为识别“发生、后来、开始、毕业、入职、离职”等历史语义和“什么时候、年份、月份”等日期语义,缺少硬边界时只修补内部请求,不再用固定模板覆盖 Agent 正文;宽年份问卷仅拦截明确的年份二选一、A/B 年份选项或年份区间选择,不再因正文包含多个已知事件年份而误杀;当 Agent 的确认段只有说明、没有面向用户的具体问题时,将内部
evidenceRequest.prompt投影到聊天气泡,且避免重复追加制度化提示。 - 验证:聚焦 narrative、orchestrator、route 测试 76/76 通过;新增“多个已知年份 + 非年份还是问句”与“说明需要更多信息但没有直接问题”的回归覆盖;真实网页账号完整保留历史气泡,2017 年入职和 2020 年主动离职均得到针对事件内容的自然确认,后者明确追问“离职后下一份工作或职业转向在何年何月开始”,没有再次落入“当前累计/下一步/本轮区分重点”模板。测试前先释放未确认校正费用并清理校正数据,点数从 207 恢复到 208;新测试正常收取 1 点后为 207。
- 防复发:叙事 fallback 必须同时保留安全用户文案、持久化回执和不含个人数据的运行时诊断;HTTP 200 不得作为自然语言 Agent 正常工作的唯一证据;禁止年份选项问卷的规则必须判断年份之间的选择结构,不能使用全文“出现多个年份 + 任意还是”作为替代。
- 相关记录:BUG-032、BUG-033
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-035 | 生时校正发送错误内容后无法撤回修改
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正文字输入、事件证据提交与对话历史
- 用户现象:用户发现刚发送的经历写错后只能等待 Agent 完成本轮,再通过下一轮更正;输入框在生成期间被锁定,错误内容可能直接进入事件账本。
- 触发条件:在生时校正中提交自由文本回答后立即发现日期或事实写错。
- 根因:校正回答会立即调用持久化命令,界面只有发送和等待状态,没有普通 session 已有的发送撤回窗口;仅中止浏览器请求也不能保证服务端停止保存。
- 修复:复用普通 session 的安全语义,在真正发起校正命令前提供 2.5 秒撤回窗口;发送后立即显示用户气泡和停止按钮,撤回时取消定时任务、移除临时气泡、把原文恢复到输入框并重新聚焦。只有窗口结束后才调用校正接口,因此成功撤回的本轮不会生成、不会写入证据历史,也不计入校正任务。
- 验证:新增真实 Chromium 回归断言,覆盖发送、停止、草稿恢复和延时后未调用
answer;本地登录账号手测中,唯一测试文本撤回后仍保留在输入框,等待超过窗口后未进入“正在核对”、未出现在用户历史消息中。目标文件 ESLint 与git diff --check通过;独立 Node 组件套件当前仍被既有 GSAP Node 导入错误阻断。 - 防复发:撤回必须发生在业务命令发出前;不得把客户端
fetch.abort()误认为服务端事务已取消。 - 相关记录:BUG-018、BUG-032(编号存疑:BUG-018 疑为重复编号合并前的旧编号,可能应指 BUG-020)
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-036 | Agent 化生时校正上线前被旧模板组件断言阻断
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生产发布门禁、生时校正前端组件测试
- 用户现象:最新
main已保留 Agent 自然语言回复与历史气泡,但生产测试门禁失败,无法进入部署。 - 触发条件:运行生产
Jyotish Skill Tests的完整前端测试套件。 - 根因:组件测试仍断言旧版确定性模板会覆盖 Agent 叙事、经历摘要带固定“已记录”前缀,并直接调用无撤回窗口的提交表达式;真实 Chromium 等待条件也仍匹配旧文案。
- 修复:更新 5 条过期断言,使其验证 Agent 原始叙事、折叠进度中的经历摘要、2.5 秒撤回后提交路径和当前移动端文案;不回退现有业务实现。
- 验证:生时校正组件测试、完整前端测试、生产质量门禁与生产部署工作流;以对应提交 SHA 的线上健康检查为最终验收。
- 防复发:对话呈现测试应验证用户可见契约,不再把旧模板句式或内部调用参数写成不必要的固定实现约束。
- 相关记录:BUG-034、BUG-035
- 复发自:无
- 修复版本:待提交
BUG-235 | 连续事件补充回答丢失状态并重新进入日期模板
- 状态:resolved(原始的“状态/首次发现/最近更新”三行在 2026-07-25 的合并提交
3ca30ed7中丢失,此处依据同批次记录与该提交内容补记,非原文) - 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正多轮问答、事件证据账本、Agent 自然追问
- 用户现象:用户先提供“2017年5月参加工作”,Agent 继续确认“正式工作还是实习/兼职”;用户回答“正式工作”后,系统却把这句话当成新事件,重新追问“具体是什么年月/只记得年份也可以”,既重复问题又丢失上一轮的年月。
- 触发条件:Agent 的追问属于已有事件的日期补充或细节补充,但公开 turn 只保存问题文本,没有保存追问目标事件和问题类型。
- 根因:编排器仅根据当前消息是否含日期决定分支;上一轮没有持久化
event_date、event_detail、new_event状态和目标evidenceId,导致无日期的补充回答落入固定非评分模板;后续虽让 narrative Agent 输出了目标状态,进度装饰层仍无条件重建 evidence request 并覆盖为new_event;细节合并还会被事件分隔符错误拆成多个证据。 - 修复:为 evidence request 增加向后兼容的 follow-up 状态机,记录问题类型和目标事件 ID;narrative Agent 被要求在追问已有事实时输出对应状态;进度装饰只维护确定性的下一领域,不再覆盖 Agent 可见问题对应的 follow-up 目标;服务端按状态将日期或细节 append-only 合并到目标事件并保留
correctsEvidenceIds,只有new_event才创建独立事件;细节合并改为单事件解析后覆盖组合摘要,避免标点导致错误拆分。 - 验证:叙事、编排与存储聚焦测试 81/81 通过;真实两轮编排回归覆盖 Agent 先追问已有事件细节并持久化目标,然后“2017-05 参加工作”后回答“正式工作”仍保留
2017-05、形成“参加工作;正式工作”并不再出现日期模板;旧 turn 缺少 follow-up 时仍按兼容路径处理。目标文件 ESLint、相关 TypeScript 检查和git diff --check通过。 - 防复发:每个中间轮必须断言追问类型、目标 evidence ID 和下一轮的事件合并结果;模型回退时默认显式
new_event,不得把已有事件细节当成新事实;跨轮测试必须断言用户可见回复不重复已回答的日期问题。 - 相关记录:BUG-028、BUG-033、BUG-034
- 复发自:BUG-033
- 修复版本:待提交(本地可测)
BUG-037 | 生时校正刷新后把真实对话重建成“已记录”模板
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正历史持久化、刷新/重新进入恢复、Agent 一问一答呈现
- 用户现象:同一轮实时回答时 Agent 会针对经历自然追问,但刷新页面或重新进入生时校正后,旧回复全部变成“已记录这段经历:……”;用户原话也被日期和事件摘要替代,看起来像 Agent 又退回固定模板。
- 触发条件:已有两轮以上生时校正回答后刷新网页、从首页重新进入,或在当前页面同步一个更新的持久化案例。
- 根因:数据库已保存每轮 Agent
narrative,但恢复 RPC 只返回latest_turn;前端为了补齐历史,使用evidenceRecap机械合成用户气泡和“已记录”助手气泡。实时链路使用内存中的原话与真实 narrative,恢复链路却使用另一套有损数据源,导致刷新前后表现不一致。 - 修复:为校正 turn 向前新增受约束的 nullable
user_message,answer 保存事务同时持久化用户原话;新增仅限service_role的 history load/save/completion wrapper RPC,按轮次返回最近 200 轮userMessage + narrative;客户端响应契约支持恢复历史,首次加载和同案例重新同步均直接渲染原始一问一答。旧记录只在有明确原始 evidence 时回填用户文本;无法恢复的旧轮次只显示真实最新 Agent narrative,不再伪造模板。 - 验证:控制器回归覆盖首次恢复、旧数据 fallback、同案例重新同步和“不得出现已记录这段经历”;聚焦校正测试 90/90 通过;本地 PostgreSQL 从头应用全部迁移成功,并验证新列与 history RPC 的
service_role/authenticated权限边界。测试内容均为虚构经历,不写入真实用户资料。 - 防复发:任何对话历史必须从持久化的原始 user/assistant turn 恢复;事件摘要只能用于进度和评分,不得反向伪造聊天内容。网页验收必须同时检查实时回答和刷新后的同一历史。
- 相关记录:BUG-032、BUG-034、BUG-036
- 复发自:BUG-032
- 修复版本:待提交
BUG-236 | 首次进入生时校正长期停留在建立记录
- 状态:resolved(原始的“状态/首次发现/最近更新”三行在 2026-07-25 的合并提交
3ca30ed7中丢失,此处依据同批次记录与该提交内容补记,非原文) - 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:首次创建生时校正 case、首条 Agent 引导消息、进入校正页面的等待时间
- 用户现象:点击生时校正后已经进入统一加载动画,但长时间停留在“正在建立校正记录…”。
- 根因:建档事务在写入首轮和完成扣点前同步等待 Mastra Agent 的结构化输出;模型 SDK 自带重试,叙事校验层也允许重试一次,慢请求和双重重试会把整段时间全部暴露给用户。实测本地星盘扫描约 0.08 秒,不是主要瓶颈。
- 修复:首轮叙事的两次校验尝试共享 10 秒中止信号,并关闭 Mastra SDK 内部重试;10 秒内生成成功仍使用 Agent 自然回答,超时或两次校验失败则使用现有的安全、可评分 fallback 完成建档。中间轮和最终总结不受该首轮时限影响。
- 验证:新增回归断言,确保首轮两次叙事尝试共享同一个
AbortSignal;目标 narrative 测试、ESLint、TypeScript 与生产构建通过后记录最终结果。 - 防复发:首轮建档不得无限等待模型,也不得同时开启 SDK 重试和业务校验重试;耗时预算必须覆盖整个首轮生成,而不是每次尝试单独重新计时。
- 相关记录:BUG-034
- 修复版本:待提交(本地可测)
BUG-038 | 老案例摘要被误标成原始对话且第七条事件后无法继续
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生产生时校正老案例恢复、累计 7–8 条可评分事件后的继续问答
- 用户现象:部署历史恢复修复后,老案例刷新仍显示“已记录这段经历”;继续回答一条明确事件后,页面提示暂时无法继续并把文本退回草稿。
- 触发条件:案例来自原始消息持久化上线之前,且旧事件 evidence 能提供规范化
raw_text;或累计可评分事件超过 6 条。 - 根因:首版迁移把旧 evidence 的规范化摘要回填到
user_message,却没有标记它不是逐字捕获的聊天原文,恢复 RPC 因而把合成摘要当成真实历史;产品收敛上限允许 8 条事件,但 Python 事件评分 API 仍只接受最多 6 条,前端第 7 条后稳定收到 400。 - 修复:为 turn 增加
user_message_captured来源标记,只有 answer 事务当场保存的逐字用户文本才进入可见历史;旧案例没有可靠原文时只显示真实最新 Agent narrative,不再展示伪造气泡。事件评分 API 上限与产品常量统一为 8,并增加八事件回归。 - 验证:迁移从零应用并检查来源标记与 RPC 权限;八事件 API 测试、聚焦对话测试、生产构建与真实生产浏览器继续问答/刷新/重新进入 smoke。
- 防复发:消息历史必须携带来源可信度,事件 evidence 不得默认等价于聊天原文;跨服务的事件数量上限必须由同一契约测试锁定。
- 相关记录:BUG-034、BUG-037
- 复发自:BUG-037
- 修复版本:0850619eaf5002736542463394720b0ab1949ce9
BUG-237 | 生时校正入口旧快照重复 start 导致 409
- 状态:resolved(原始的“状态/首次发现/最近更新”三行在 2026-07-25 的合并提交
3ca30ed7中丢失,此处依据同批次记录与该提交内容补记,非原文) - 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正首页入口、未完成 case 恢复、重复 start 防护
- 用户现象:用户点击进入生时校正时,接口发送新的
start,返回409 action_conflict,页面无法进入校正会话。 - 触发条件:前端账户快照没有包含已有未完成 case,或账户快照在出生声明变更后隐藏了旧 case,但数据库中仍存在该用户的未完成 V3 case。
- 根因:前端只根据内存账户状态选择
start/resume;数据库会阻止同一用户创建第二个未完成 V3 case,旧快照因此被拒绝。 - 修复:首次
start收到 409 后刷新账户状态;如果刷新得到可恢复 case,自动使用最新caseId和turnVersion执行resume,不重复扣点;如果刷新后仍没有声明匹配的 case,则保留原错误,避免未经用户确认自动放弃旧校正记录。 - 验证:
frontend/tests/consultation-entrypoint.test.ts23/23 通过,覆盖 409、刷新账户、使用最新 case 版本恢复;目标文件 ESLint 与git diff --check通过。完整套件仍有既有 CSS 断言和数据库权限测试失败,与本修复无关。 - 防复发:生时校正入口必须把
start视为可恢复的幂等操作;收到 case 冲突时先刷新 durable 状态,再决定恢复或提示用户;不得直接再次扣费、创建第二个 case,或在声明不一致时静默删除旧 case。 - 相关记录:BUG-034、BUG-236
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-039 | 生时校正 Agent 回答等待结束后一次性出现
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正首次进入、回答传输、Agent 生成状态、移动端对话可读性
- 用户现象:首次进入时只显示“正在建立校正记录”,看不到后台生成的第一条 Agent 引导;用户发送经历后也只能看到“正在核对星盘信息”,待模型、校验和保存全部完成后,Agent 整段回答一次性出现,与普通 session 的逐段生成体验不一致。
- 触发条件:任意生时校正
start、resume或answer命令成功返回自然语言 narrative。 - 根因:
/api/birth-time-conversation固定使用Response.json(turn),客户端也完整读取并校验 JSON 后才更新控制器;即使 narrative 已生成,传输层和聊天渲染层都没有增量事件契约。首次入口另由首页直接执行start/resume,没有把生成中的首条 narrative 投影给尚未初始化的校正组件。 - 修复:成功响应在客户端声明支持时改用
application/x-ndjson;服务端只对已经完成技法校验并持久化的 narrative 分块发送delta,最后发送完整 durable turn。客户端逐行解析并即时投影到同一个 assistant 气泡,首次start/resume也把增量引导传入空白校正界面;最终 turn 到达后无缝转为 settled 历史。错误响应继续使用既有安全 JSON,已输出 delta 的中断不自动重放,避免重复文字。代理技法校验、事件事务和分钟安全门禁均保持在流输出之前。 - 验证:新增 route 分块顺序与禁用代理缓冲回归、client NDJSON 增量解析回归、controller 在 durable turn 未到达前发布流式文本回归、组件 streaming 气泡回归;聚焦校正测试、真实 Chromium 390px 组件测试、ESLint、TypeScript 与 production build 通过。
- 防复发:生时校正成功响应不得退回一次性 JSON 作为唯一前端路径;可见增量内容必须来自已校验 narrative,不得直接透传未完成或未通过约束的模型 token。
- 相关记录:BUG-034、BUG-037、BUG-038
- 复发自:无
- 修复版本:本次流式修复提交
BUG-238 | 服务层重复 start 未在扣费前复用同一声明的未完成 case
- 状态:resolved(原始的“状态/首次发现/最近更新”三行在 2026-07-25 的合并提交
3ca30ed7中丢失,此处依据同批次记录与该提交内容补记,非原文) - 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正 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.ts32/32 通过,覆盖同声明重复 start 复用、声明不一致冲突和既有预留重试;后续需补充线上部署后的真实 smoke。 - 防复发:start 幂等性必须同时在入口、编排器和 durable RPC 三层成立;任何新增 actionId 的 start 都必须先检查账户级未完成 case,不能把数据库冲突当成正常流程。
- 相关记录:BUG-034、BUG-236、BUG-237
- 复发自:BUG-237
- 修复版本:待提交(本地可测)
BUG-040 | Agent 活动状态与回答被错误渲染为互斥状态
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:普通 session 与生时校正共用的 Agent 消息行、流式回答反馈、完成态识别
- 用户现象:Agent 尚未产出正文时只显示 orb 活动状态;开始出现正文后状态与答案的关系不连续,完成后活动状态直接消失,用户无法从同一消息位置判断回答仍在生成还是已经结束。
- 触发条件:任意 assistant 消息在
thinking、streaming、settled三种视图状态之间切换。 - 根因:共享
ChatMessageRow使用条件分支把thinking渲染为仅有AgentActivityStatus,又只在streaming时显示 composing 状态;settled分支只保留正文,因此活动状态和正文被实现成了互斥内容,而不是同一消息的连续生命周期。 - 修复:assistant 消息始终在同一气泡内先渲染活动状态,再渲染当前已有正文;thinking/streaming 继续使用 working/composing orb,settled 只把状态文案切换为“回答已完成”,并把 orb 替换为 Lucide 完成图标。普通 session 与生时校正共享该行为,React 消息 identity 与正文内容保持不变。
- 验证:消息视图回归覆盖 working、composing、completed 三态、正文与状态同时存在、完成态 Lucide 图标;相关普通 session 与生时校正契约测试通过,并执行真实浏览器检查。
- 防复发:Agent 活动状态只能描述消息生命周期,不得控制正文是否渲染;完成态必须保留稳定的视觉反馈,不能以卸载整个状态区域代替状态转换。
- 相关记录:BUG-039
- 复发自:无
- 修复版本:
4a9b1dc
BUG-239 | 旧数据库校验器把首条新事件回答误报为 409
- 状态:resolved(原始的“状态/首次发现/最近更新”三行在 2026-07-25 的合并提交
3ca30ed7中丢失,此处依据同批次记录与该提交内容补记,非原文) - 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正首条及后续普通新事件回答
- 用户现象:case 与请求版本同为 0,提交首条经历仍返回
409 action_conflict。 - 根因:应用已为证据请求加入可选
followUp状态,但当前数据库校验器仍只接受旧字段;显式的new_event与旧默认语义相同,却在保存边界被拒绝。 - 修复:保存任何 turn 时都移除语义冗余的
followUp: new_event,兼容旧校验器;event_date与event_detail仍等待对应数据库迁移后持久化。 - 验证:直接 RPC 探针确认旧格式为
true、带followUp为false;新增 store 回归测试覆盖首轮与后续 turn 的兼容投影。 - 防复发:新增可选持久化字段时,默认语义必须能向旧数据库降级;数据库迁移未应用前不得把兼容字段直接写入 durable RPC。
- 相关记录:BUG-235、BUG-237、BUG-238
- 修复版本:待提交(本地可测)
BUG-041 | 生时校正生成回答时消息列表不自动跟随到底部
- 状态:resolved
- 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正消息历史、用户发送后的撤回窗口、Agent thinking / streaming / settled 回答阶段
- 用户现象:用户发送经历或 Agent 开始生成回答后,新内容出现在消息列表下方,但列表停留在旧位置;用户必须手动向下滚动才能看到生成状态和最新回答。
- 触发条件:生时校正已有足够内容使独立消息容器产生纵向滚动,然后发送新经历或接收流式 Agent 回答。
- 根因:普通 session 有独立的底部跟随 effect,生时校正虽然使用可滚动的
.rectification-message-list,但组件没有保存该容器的 ref,也没有在消息、提交状态、流式文本或最终 turn 更新时调整容器滚动位置。 - 修复:为生时校正消息容器增加专用 ref,并在用户消息、生成状态、流式增量、错误及最终 turn 生命周期变化后滚动该容器到底部;生成过程中直接跟随,完成后平滑定位,且尊重 reduced-motion,不调用会牵动外层页面的
scrollIntoView。 - 验证:真实 Chromium 390px 回归制造独立消息容器 overflow,并分别断言初始历史、乐观用户消息、流式 Agent 增量和完成回答都保持在底部;相关聚焦测试、ESLint、TypeScript 与生产 smoke。
- 防复发:生时校正的消息生命周期新增阶段必须进入底部跟随依赖;真实浏览器测试必须验证容器自身的
scrollTop,不能只检查正文是否出现在 DOM。 - 相关记录:BUG-039、BUG-040
- 复发自:无
- 修复版本:本次自动滚动修复提交
- 2026-09-17 语义被 BUG-930 取代:生成中改为定位到本轮开头,不再贴底跟随。
BUG-240 | 非评分回答绕过 Agent 并显示固定澄清模板
- 状态:resolved(原始的“状态/首次发现/最近更新”三行在 2026-07-25 的合并提交
3ca30ed7中丢失,此处依据同批次记录与该提交内容补记,非原文) - 首次发现:2026-07-23
- 最近更新:2026-07-23
- 影响面:生时校正缺少年月、未来事件、换方向和非评分更正后的自然问答
- 用户现象:用户用自然语言补充“化学专业”“后来换了工作”等内容后,页面直接显示“你提到……具体内容我已经记下了。它大致是什么年月?”,与前后 Agent 语气断裂,也可能忽略刚才已经建立的事件上下文。
- 根因:编排器发现本轮暂时没有可评分事件后,直接进入同步
nonScoringTurn();该函数完全没有调用 narrative Agent,而是用固定字符串生成可见回复。Mastra 和模型没有获得生成这轮回答的机会。 - 修复:非评分分支先用当前候选技术包、完整事件账本、最新用户原话和未决证据调用现有 narrative Agent;状态机只在后台固定本轮追问属于
event_date、event_detail还是new_event,不再替代 Agent 的可见措辞。模型或技术包调用失败时仍保留确定性安全文案,避免正常澄清变成 5xx。 - 验证:编排回归 32/32 通过;相关叙事、编排和存储测试 81/81 通过。新增断言确认无年月事件会得到 Agent 针对该事件生成的单一自然问题,并持久化
event_date + evidenceId,不再出现旧固定模板;未来事件、换方向和已有评分事件的行为保持兼容。 - 防复发:任何能继续对话的业务分支都必须优先经过 narrative Agent;状态机可以决定证据目标和可评分性,但不得直接占用正常用户回复。确定性模板只能作为模型或技术计算失败时的技术兜底。
- 相关记录:BUG-033、BUG-034、BUG-235
- 复发自:BUG-033
- 修复版本:待提交(本地可测)
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
- 修复版本:待提交
BUG-241 | 全球地点字段未迁移导致账户余额接口整体 500
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
GET /api/account、首页账户初始化、余额和生时校正入口 - 用户现象:已登录用户访问本地首页时,账户接口返回
500 {"error":"暂时无法读取账户余额"},余额与账户状态均无法加载。 - 根因:账户 GET 为全球出生地点新增了
birth_place_*与timezone_*字段查询,但当前 Supabase 尚未应用对应迁移,PostgREST 返回42703 column profiles.birth_place_label does not exist。PATCH 已有旧表回退,GET 缺少同等兼容路径,并把资料字段错误误报成余额错误。 - 修复:GET 检测缺列或 schema cache 错误后,回退到迁移前的 profile 字段集合;全球地点字段在响应中返回空值,余额、出生时间状态和未完成校正 case 继续正常读取。迁移应用后自动使用完整查询。
- 验证:服务角色直接查询确认修复前错误为
42703;frontend/tests/account-api.test.ts增加旧表回退回归,另运行 TypeScript、目标 ESLint 与本地登录接口 smoke。 - 防复发:向账户初始化查询增加非关键资料字段时,应用发布必须兼容迁移前后的数据库形状;不能让可选地点元数据阻断余额与核心账户状态。
- 相关记录:BUG-237、BUG-238
- 修复版本:待提交(本地可测)
BUG-242 | 全球地点迁移漏掉服务角色列权限导致账户保存 500
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
PATCH /api/account、初始化出生地点保存、全球地点资料更新 - 用户现象:读取账户已经恢复,但提交出生资料返回
500 {"error":"暂时无法核对现有出生资料"}。 - 根因:账户 PATCH 的并发保护和缺失 profile 恢复路径通过服务角色读取、插入及更新
profiles;全球地点迁移只授予了authenticated更新权限,遗漏服务角色对六个新字段的列级SELECT / INSERT / UPDATE,PostgREST 返回42501 permission denied for table profiles。 - 修复:在全球地点迁移中为服务角色补齐六个新字段的最小列级读取、插入和更新权限,不恢复表级宽权限。
- 验证:迁移权限静态回归、生产事务 dry-run、正式授权、字段权限查询和真实登录 PATCH smoke。
- 防复发:扩展服务端账户 upsert 字段时,必须同时审计 authenticated 自助保存和 service_role 并发读取/upsert 两条权限链。
- 相关记录:BUG-237、BUG-241
- 修复版本:待提交(本地可测)
BUG-243 | 选择全球地点后重复搜索且候选列表被卡片裁切
- 状态: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-241、BUG-242
- 修复版本:待提交(本地可测)
BUG-244 | 全球地点资料已保存但初始化接口仍判定未完成
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
POST /api/onboarding、全球出生地点用户进入首页 - 用户现象:用户已填写称呼、出生日期、时间线索和旧金山地点,保存成功后进入首页仍返回
出生资料尚未完成。 - 根因:onboarding 服务端完整性判断仍把中国
province_code + city_code写死为必填,没有识别已经持久化的全球地点标签、经纬度和 IANA 时区。 - 修复:服务端资料投影补齐全球地点字段;完整性判断接受“有效全球地点”或“旧中国行政区地点”,并复用出生资料共享校验。
- 验证:新增旧金山
period_only + timezoneOffset null + America/Los_Angeles回归用例,确认可生成首页初始问题。 - 防复发:读取全球地点的服务端流程不得继续以中国行政区代码作为唯一地点完成条件。
- 相关记录:BUG-241、BUG-243
- 修复版本:待提交(本地可测)
BUG-245 | 全球地点缺少数字时区偏移导致生时校正入口 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-243、BUG-244
- 修复版本:待提交(本地可测)
BUG-246 | 全球出生地点在兄弟接口中被误判、冲突或静默退化
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
GET /api/account、POST /api/consult、POST /api/birth-time-journey、今日星语、合盘与遗留生时评估接口 - 用户现象:全球出生地点已保存且可进入首页,但已有生时校正记录无法恢复并反复触发
action_conflict 409;正式咨询可能在扣点前返回资料不可用;部分旧功能仍要求中国行政区代码,合法全球资料会静默退回模板或 503。 - 根因:全球地点迁移只修复了 onboarding 与 conversational rectification 主入口,多个兄弟读取路径仍各自维护旧合同:强制
timezone_offset为数字、直接比较 Geoapify 七位小数坐标,或把中国省市区中心当作唯一地点真值。账户接口因此看不到数据库中实际存在的未完成 case,前端误判为需要重新创建。 - 修复:抽取共享历史时区解析器,兼容数据库 snake_case 与浏览器 camelCase 资料,并按实际申报/确认时间调用本地 IANA 历史时区服务;账户恢复使用已存 case 的不可变 offset 作为仅用于匹配的 fallback,并把坐标统一到六位耐久精度;咨询在扣点前使用最终 active time 补算 offset;journey 入口和 case loader 在严格解析前补算;今日星语、合盘和遗留评估优先使用真实全球经纬度与时区,中国行政区仅作兼容 fallback。
- 验证:账户 nullable offset + 七位坐标可恢复旧金山已有 case,时区 ID 或地点 ID 不一致仍拒绝恢复;verified consultation 使用最终 active time 解析
-8且解析失败不调用扣点;journey、今日星语、合盘和遗留评估的全球地点回归通过。两组聚焦套件共 84/84 通过,目标文件 ESLint、TypeScript--noEmit与git diff --check通过。 - 防复发:出生地点完成性与计算就绪性必须分层;资料层可保存 IANA timezoneId 且 offset 暂空,但所有星盘计算入口必须统一补算历史 offset。不得在新接口复制中国专用地点解析,也不得用未经规范化的外部坐标直接比较持久化声明。
- 相关记录:BUG-243、BUG-244、BUG-245
- 修复版本:待提交(本地可测)
BUG-044 | 数据库出生地点声明校验落后导致创建校正记录误报 409
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
POST /api/birth-time-conversation、Geoapify 等全球地点用户首次创建生时校正记录 - 用户现象:账户资料完整且不存在未完成 case,点击生时纠正仍返回
409 action_conflict;重试同一个已释放的 action 后可能转为计费失败。 - 根因:TypeScript 持久化声明已经支持
placeId、placeType、provider、timezoneId和timezoneSource,但数据库函数conversational_rectification_valid_declared_birth_input仍只允许旧中国地点字段。合法全球地点声明在create_conversational_rectification_case内被判无效,并被 RPC 统一映射为conversational_action_conflict。 - 修复:新增向前迁移同步数据库地点声明白名单,接受全球地点身份、IANA 时区来源及坐标字段;地点身份允许
city、cityCode或placeId任一存在,同时保留旧中国地点兼容和数值边界校验。 - 验证:数据库迁移单独执行成功;使用新的 actionId 对本地接口真实 smoke 返回
200 active,创建可恢复 case,首轮 narrative 非空;相同 actionId 重放仍返回同一 case,账户积分只从 100 扣至 99;无待交接内容时 handoff 按合同返回204。相关持久化与全球地点测试 28/28 通过。 - 防复发:任何扩展
declaredBirthInput.birthplace的应用字段都必须同步更新数据库 JSON 校验函数并增加迁移文本回归断言;不得把声明校验失败笼统诊断为旧 case 冲突。 - 相关记录:BUG-237、BUG-238、BUG-245、BUG-246
- 修复版本:待提交(本地可测)
BUG-045 | 数据库追问合同落后导致第一条回答误报 409
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
POST /api/birth-time-conversation、已创建校正 case 的第一条及后续自然语言回答 - 用户现象:校正记录能够创建和恢复,但提交第一条人生事件后返回
409 action_conflict;case 的turnVersion保持不变,积分没有再次扣除。 - 根因:应用的 evidence request 已支持带目标事件的
followUp: { kind, evidenceId },但共享 Supabase 尚未执行仓库已有迁移20260723010000_align_conversational_follow_up_request.sql。旧版conversational_rectification_valid_evidence_request拒绝该合法字段,保存 RPC 将校验失败统一映射成conversational_action_conflict。 - 修复:在 Supabase 项目
vtvnfqmonbfuxmqkqdlc单独执行已有向前迁移,使数据库 evidence request 校验与当前 TypeScript 持久化合同一致;未执行其他迁移,也未重新部署应用。 - 验证:对原 case 使用新 actionId 重试第一条回答返回 HTTP 200,
turnVersion从 0 递增到 1,narrative 和目标event_detailfollow-up 正常返回;用同一 actionId 重放返回相同 turn,账户积分保持 99。服务端日志两次均为actionKind=answer、resultCategory=success、billingState=unchanged;持久化与迁移聚焦测试 27/27 通过。 - 防复发:应用扩展 durable JSON 合同时,数据库迁移必须进入环境迁移账本并在发布验收中执行第一条真实写入 smoke;不能只用迁移文件存在或静态测试通过代替远端数据库合同验证。
- 相关记录:BUG-237、BUG-238、BUG-044
- 修复版本:数据库向前迁移已应用;应用代码待提交(本地可测)
BUG-046 | 生时校正刷新丢失首条引导并堆叠历史用户气泡
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正会话首次进入、连续问答及刷新后的历史恢复
- 用户现象:首次进入时 Agent 的引导消息刷新后消失;已录入的人生事件被集中恢复成多条连续的右侧用户气泡,最后只剩一条 Agent 回复,破坏真实的一问一答顺序。
- 根因:新会话没有把完整问答 transcript 持久化到 session;恢复接口只返回最新 turn,前端只能把最新 turn 中累计的
evidenceRecap误当成逐轮聊天历史。数据库实际保存了每轮birth_time_rectification_turns.narrative,用户原文也可通过birth_time_rectification_event_evidence.source_turn_id与raw_text恢复,但旧恢复链路没有读取这些数据。 - 修复:新会话从首条 Agent narrative 开始持久化完整消息序列,每次回答后保存真实的
Agent → 用户 → Agenttranscript;resume 接口按 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-024、BUG-029、BUG-032 本身,还是旧编号体系下实为 BUG-028、BUG-033、BUG-235)
- 修复版本:待提交(本地可测)
BUG-047 | 相对日期缺少上下文解析并触发模板式重复追问
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正连续追问、自然语言事件提取与澄清合并
- 用户现象:Agent 已知用户在
1972年12月正式退学,继续追问彻底离校时间后,用户回答“来年1月份我彻底离开的学校”,系统仍重复询问具体年份和月份。 - 根因:事件提取器把“来年、次年”等相对日期视为无日期文本,但不读取正在讨论的事件年份;澄清合并又只接受不含描述的纯日期回答;非评分分支随后用固定澄清模板覆盖模型已经生成的上下文回答。
- 修复:对
event_date和event_detail追问使用目标事件或最近明确事件作为年份锚点,将“来年、次年、第二年、翌年”和“同年、当年、那年”的月份上下文化后再提取,同时保留用户原始文本;允许带自然描述的日期补充完成原事件,但仅在纯日期、同领域或低信息细节时合并,避免把“搬家”等明确新事件吞入上一条澄清;普通澄清优先显示 narrative Agent 的自然回答,确定性模板仅保留为模型失败兜底。 - 验证:事件提取与编排聚焦测试 59/59 通过,覆盖“2022年12月正式退学 → 来年1月彻底离校”解析为
2023-01、带描述日期补充、相对日期无上下文时不臆造年份,以及不同领域新事件不误合并。目标 TypeScript、ESLint 与补丁检查通过;按用户要求未运行 Chrome/Playwright。 - 防复发:事件状态机只负责持久化目标、证据质量与评分边界,不得替代 Agent 的可见对话;相对日期必须在明确的当前事件上下文内解析,缺少可靠锚点时保持待澄清;澄清合并必须同时判断日期、摘要和领域连续性。
- 相关记录:BUG-033、BUG-235、BUG-240
- 修复版本:待提交(本地可测)
BUG-048 | 生时校正消息更新后没有自动滚动到最新内容
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正聊天区、用户发送、Agent 思考态及回答更新
- 用户现象:用户发送经历后,Agent 开始思考并生成回答,但消息列表停留在原位置,需要手动向下滚动才能看到最新内容。
- 根因:普通 session 在消息列表末尾设置了滚动锚点和
scrollIntoVieweffect;生时校正使用独立的rectification-message-list滚动容器,却没有对应的末尾锚点和消息更新监听。 - 修复:在生时校正消息列表末尾增加滚动锚点,并在消息数量、最新回答文本、思考状态、提交状态、turn version 和错误状态变化时滚动到末尾;生成中即时跟随,完成后平滑滚动,同时尊重
prefers-reduced-motion。 - 验证:组件目标回归测试通过;TypeScript
--noEmit、目标 ESLint 与git diff --check通过。按用户要求未运行 Chrome/Playwright。 - 防复发:新增独立聊天滚动容器时必须同时提供末尾锚点,并覆盖乐观用户消息、生成状态和回答文本更新三类触发源。
- 相关记录:BUG-046
- 修复版本:待提交(本地可测)
- 2026-09-17 语义被 BUG-930 取代:生成中改为定位到本轮开头,不再贴底跟随。
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-033、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-033、BUG-047
- 复发自:BUG-047
- 修复版本:待提交(本地可测)
BUG-052 | 确定性流程模板覆盖生时校正 Agent 的自然回答
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正首轮引导、中间事件追问、方向切换、未来事件、暂停、放弃与确认消息
- 用户现象:模型已经结合上下文生成回答后,网页仍显示“已记录”“当前累计”“下一步”“还差时间定位”等重复话术;部分操作还会新增程序模板气泡,使对话像问卷而不是连续的一问一答。
- 根因:编排层把事件提取结果、
followUpmetadata 和收敛状态当成可见文案的唯一真源,在模型生成之后又根据分支拼接或替换 narrative。原本用于事实安全和失败兜底的确定性逻辑因此越界成了对话作者,并且 metadata 的偶发误判会直接中断正常语义交流。 - 修复:可见文案统一以 Agent narrative 为准;删除首轮候选前后缀、事件保存与计数进度、领域推荐、方向切换、无日期、未来事件、暂停、放弃和确认成功等确定性模板。事件提取、未来事件不计分、去重、更正、候选计算和确认时间原子落库继续作为不可见业务状态运行,提取失败或 metadata 标错不得覆盖模型回答。增加脱敏诊断日志,仅记录阶段、校验问题代码、重试和 fallback 类别,不记录用户原文或完整模型输出。
- 表格边界:稳定层/敏感层、技法审计和事件证据三类表可在中间轮按上下文需要出现;最终确认前必须提供完整汇总。表中候选、范围、分盘、技法和引用只能来自 technical packet,不得编造、泄露私有分数或声称运行了未执行的技法。
- 验证:叙事与编排单元测试覆盖 Agent 原文保留、结构化 metadata 不覆盖自然回答、未来事件后台排除、原因补充合并、暂停/放弃/确认无新增模板气泡,以及最终确认仍更新正式出生时间;合成 E2E 覆盖首轮、换方向、无日期和未来事件均保留模型可见回答。目标 ESLint、TypeScript、测试与补丁检查结果见本轮验证记录。
- 防复发:状态机只能决定允许的动作和持久化状态,不能编写用户可见话术;新增任何确定性文本前必须证明它属于安全错误而非正常对话,并增加“模型输出未被覆盖”的回归断言。
- 相关记录:BUG-046、BUG-047、BUG-049、BUG-051
- 修复版本:待提交(本地可测)
BUG-053 | 首轮结构化结果为空时错误展示确定性引导模板
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正首轮 Agent 引导、Mastra 结构化输出兼容、首轮等待时限
- 用户现象:新建生时校正后固定显示“当前仍在核对……先说一件已经发生的学业事件”,看起来像 Agent 回答,实际没有使用模型生成的自然文案。
- 触发条件:DeepSeek 已完成一次 Mastra 调用,但
result.object未形成结构化对象;提供商返回的文本结果未被继续解析,两次尝试均被记录为 schema 失败。 - 根因:生产叙事适配器只解析
result.object,没有兼容 Mastra/模型组合返回 JSON 文本的情况;首轮失败后又调用fallbackNarrative(),把程序模板伪装成了 Agent 消息。脱敏 receipt 为fallbackUsed=true、schemaValidated=false、issues=["root:invalid_type"],不是超时错误。 - 修复:优先使用通过 schema 的
result.object,为空时把非空result.text交回既有 JSON 提取和 packet 校验流程;首轮连续两次仍失败时返回可重试服务错误并释放预留计费,不再持久化或展示确定性首轮模板;中间轮既有安全 fallback 暂不改变。首轮等待策略的后续修复见 BUG-054。 - 验证:叙事测试覆盖“两次不合格时拒绝而非展示模板”;路由测试覆盖
result.object为空但result.text有 JSON 的兼容路径;目标叙事、编排和路由测试通过。 - 防复发:模型适配层必须兼容结构化对象与 JSON 文本两种合法承载方式;正常首轮不得用程序模板冒充 Agent 成功回答,timeout、schema 和 grounding 失败必须在脱敏日志中分别可辨认。
- 相关记录:BUG-046、BUG-052
- 修复版本:待提交(本地可测)
BUG-054 | 首轮两次生成共用超时信号导致重试立即失败
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
POST /api/birth-time-conversation首轮生成与重新生成 - 用户现象:移除首轮固定模板后,重新生成等待约 30 秒返回可重试的
service_unavailable 503。 - 触发条件:首轮模型生成超过共享的 30 秒 deadline;第二次尝试继承已经取消的
AbortSignal,无法获得独立生成时间。 - 根因:两次尝试在循环外创建并共用一个超时信号;诊断日志又只保留最后一次异常,把首次超时误记为
schema_invalid。 - 修复:每次首轮尝试创建独立的 45 秒超时信号,累计两次尝试的脱敏问题代码,并将
TimeoutError/AbortError明确归类为timeout。 - 验证:本地开发日志复现请求在
30331ms失败;回归测试锁定两次尝试使用不同信号并将超时记录为timeout,目标测试、ESLint、TypeScript 与补丁检查通过。 - 防复发:重试不得复用已取消的信号;超时、schema 和事实校验必须在脱敏日志中保持不同类别。
- 相关记录:BUG-016、BUG-053
- 复发自:BUG-053
- 修复版本:待提交(本地可测)
BUG-055 | 首轮完整技术输出与重复重试放大模型延迟并触发 503
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
POST /api/birth-time-conversation首轮 Agent 引导、Mastra 结构化输出、60 秒路由预算 - 用户现象:本地首轮请求等待约 49–90 秒后返回可重试的
service_unavailable 503;同一模型的轻量探针正常。 - 触发条件:正式首轮把候选状态、时间范围、稳定层、敏感层、引用与完整 expert workflow 全部交给模型重复输出;一次超时后又立即进行第二次完整生成。
- 根因:模型承担了程序已经确定的 packet 字段复制工作,正式 prompt 曾达到约 8.4k 字符;提供商延迟波动时,两次 45 秒调用可超过路由声明的 60 秒预算。一次 26 秒成功结果还曾因旧引用正则误判被丢弃。
- 修复:生产模型只生成
narrative与evidenceRequest,候选状态、时间、范围、分盘层、引用和领域依据由服务端从 packet 确定性补全;首轮 prompt 精简到必要边界和建议领域;第一次纯超时不再触发第二次完整调用;单次等待上限调整为 47 秒,校验失败的短重试最多 7 秒;首轮若只写了介绍但漏掉问题,追加模型自己生成的evidenceRequest.prompt,不使用程序话术模板。 - 验证:正式本地认证请求的 prompt 从 4785 字符进一步降到 1399 字符,provider 在 29066ms 返回,接口 HTTP 200 并生成自然首轮回答;定向测试 66/66、ESLint、TypeScript 与补丁检查通过。
- 防复发:模型结构化输出不得重复由服务器掌握的确定性 packet;生成总预算必须小于路由平台预算;首轮 timeout、schema、grounding 继续使用脱敏分类日志,不记录提示词、用户资料或模型正文。
- 相关记录:BUG-053、BUG-054
- 复发自:BUG-054
- 修复版本:待提交(本地可测)
BUG-056 | 关键词领域分类阻断自然语义并在生成失败后回退到错误领域模板
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正自然语言事件分类、中间轮 Agent 回答与候选评分输入
- 用户现象:带明确年月的研究院实习、研究员经历被保存为
other;同轮模型生成失败后,页面又固定追问学业事件,看起来像 Agent 没有理解刚才的事业经历。 - 根因:确定性关键词分类被当作最终语义判断,事业词表没有覆盖所有自然表达;中间轮叙事连续失败后仍允许
fallbackNarrative()生成固定业务话术,并按技术 packet 的首个建议领域继续提问。 - 修复:单条事件落入
other时,优先调用 narrative Agent 结合最近事件上下文返回允许的语义领域;模型不可用、超时或仍返回other时才保留确定性结果。首轮和中间轮叙事连续失败统一返回可重试服务错误且不保存本轮,只有最终确认阶段保留包含安全边界与三类表的确定性 fallback。 - 验证:新增“2020年4月去石油化工研究院实习做研究员”语义分类回归,确认保存为
career并可评分;新增中间轮不合格输出回归,确认返回service_unavailable、不保存事件、不推进版本且不产生模板消息。事件提取、叙事、编排和路由定向测试 134/134 通过。 - 防复发:正则只负责低成本初筛和模型不可用时的降级,不得覆盖 Agent 对自然语言事件的语义判断;非最终轮生成失败不得用业务模板伪装成成功回答;候选时间、分盘事实、未来事件和重复计分仍由程序校验。
- 相关记录:BUG-033、BUG-235、BUG-047、BUG-053
- 复发自:BUG-053
- 修复版本:待提交(本地可测)
BUG-057 | 中间轮重跑在 Pro 模型延迟波动时直接返回 503
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
POST /api/birth-time-conversation的regenerate与其他非最终叙事轮、Mastra 模型降级、validation receipt - 用户现象:本地生时校正点击重跑后返回可重试的
service_unavailable 503,原回答仍保留但无法得到新的 Agent 回答。 - 触发条件:中间轮技术 packet、事件账本和上下文组成约 5.7k 字符的提示词,首选 Pro 模型在提供商延迟波动时超过 47 秒单次等待上限。
- 根因:BUG-055 为避免两次慢模型调用超过 60 秒路由预算,曾规定第一次纯超时直接结束;这避免了重复 Pro 调用,却也让瞬时延迟直接变成 503。受控本地复现中,同一重跑请求成功时总耗时约 45 秒,其中模型约 32 秒,证明服务和 Mastra 结构化输出可用,但原策略没有快速恢复路径。
- 修复:将叙事预算调整为首选模型 38 秒和独立恢复模型 7 秒;第一次调用使用
deepseek-v4-pro,超时、schema 或事实校验失败后以新的AbortSignal调用deepseek-v4-flash。生成结果携带实际模型 ID,validation receipt 记录真正成功的模型;两次均失败时继续返回 503,不恢复固定业务模板。 - 验证:新增 Pro 超时后 Flash 成功的回归,断言两次调用使用独立 signal、第二次 attempt 标记正确、receipt 写入 Flash 模型且不使用模板;新增 regenerate 双模型均失败的事务回归,断言不推进
turnVersion、不覆盖上一条 Agent 消息、不新增事件、不改变候选和计费。叙事、路由与编排定向测试 110/110 通过。 - 防复发:慢模型与快速恢复模型必须共享明确的总预算而不是各自获得完整路由时限;receipt 必须记录实际成功 provider/model;生成失败只能作为可重试技术错误,不能提交半轮状态或用业务模板伪装成功。
- 相关记录:BUG-053、BUG-054、BUG-055、BUG-056
- 复发自:BUG-055
- 修复版本:待提交(本地可测)
BUG-058 | 私有事件路由与未执行技法清单泄露到 Agent 回答并中断追问
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正中间轮自然语言回答、事件评分路由、Technique Audit 上下文与下一问题生成
- 用户现象:用户提供带年月的真实经历后,Agent 主动解释内部“事件归类”,展示大量
not_evaluated、blocked技法和确认权限,却没有继续提出下一个问题;回答像调试报告而不是连续的一问一答。 - 根因:叙事上下文直接暴露事件的私有
domain、建议领域和完整 expert workflow 状态,模型因此围绕工程元数据组织回答;同时问题修复器只处理首轮或明显截断的句子,语法完整但没有问号的中间回答会原样通过。技法保持not_evaluated的直接原因则是当前有效、受支持且可评分的事件不足三条,路由尚未进入scoreEvents(),并非 Mastra 无法调用技法。 - 修复:新增叙事专用上下文,移除事件
domain、建议领域和内部评分;证据采集阶段只允许暴露已实际used或partial的技法,不再展示未调用清单、阻塞项或确认权限。私有语义领域仍保留在服务端用于事件去重、跨领域路由和候选评分,但不得进入用户可见文案。所有非最终回答必须包含且只引导一个下一问题;模型正文漏问时追加同一次模型生成的evidenceRequest.prompt,不使用固定业务模板。 - 验证:新增回归覆盖事件正文可见但内部领域不可见、建议领域不进入叙事 packet、未调用技法不在采集轮展示,以及完整陈述漏问时补入模型自写问题;叙事、路由和编排聚焦测试通过,目标 ESLint、TypeScript
--noEmit与git diff --check通过。 - 防复发:私有语义路由与用户叙事必须使用不同数据视图;未执行的技术状态不得被包装成已完成分析;每个非最终 Agent 回答都必须断言存在一个明确的下一问题,同时最终轮仍须使用真实技术 packet 生成可审计的三类表。
- 相关记录:BUG-052、BUG-053、BUG-055、BUG-056、BUG-057
- 修复版本:待提交(本地可测)
BUG-059 | Flash 恢复窗口过短导致模型波动时偶发 503
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:
POST /api/birth-time-conversation非最终叙事轮、Pro 超时后的 Flash 恢复请求 - 用户现象:同一条合法回答偶发返回可重试
service_unavailable 503,随后原 action 幂等重试又能正常生成回答。 - 根因:首选 Pro 模型等待上限为 38 秒,但恢复模型只有 7 秒;提供商延迟稍高时,两次尝试都会在接口 60 秒总预算之前被主动中止。原请求幂等重试约 43 秒成功并正常推进一轮,排除了数据库、case 版本和技术 packet 故障。
- 修复:保留 38 秒 Pro 窗口,将独立 Flash 恢复窗口从 7 秒放宽到 14 秒;最坏模型等待仍为 52 秒,为鉴权、技术计算和持久化保留约 8 秒,不引入第三次请求或固定模板回退。
- 验证:使用原 action 幂等重试返回成功并生成自然下一问;叙事、路由和编排聚焦测试、目标 ESLint、TypeScript
--noEmit与git diff --check通过。 - 防复发:两次模型尝试的总预算必须显式小于路由
maxDuration;恢复模型窗口不能短于其实际常见尾延迟;非最终轮仍不得用确定性业务模板伪装生成成功。 - 相关记录:BUG-055、BUG-057、BUG-058
- 复发自:BUG-057
- 修复版本:待提交(本地可测)
BUG-060 | 生时校正模型选择器显示 GPT-5.5 但请求实际使用默认 DeepSeek
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正首次引导、用户回答和重跑的叙事模型选择,以及失败操作的幂等身份
- 用户现象:生时校正输入框下方明确选择 GPT-5.5,但接口请求没有携带模型标识;Agent 回答的延迟和实际调用模型与界面选择不一致。
- 根因:普通会话会把当前会话的
modelId发送给/api/consult,生时校正控制器和首页首次start请求却没有把同一字段写入命令;服务端叙事生成器因此只能使用RECTIFICATION_NARRATIVE_MODEL_ID,未配置时默认选择 DeepSeek。模型选择器此前只改变了会话 UI 状态,没有贯通生时校正 API。 - 修复:为会生成 Agent 文本的
start、answer、regenerate命令加入可选modelId;首页首次启动使用新建或恢复的生时校正会话模型,控制器每次发送时读取当前选择。路由从既有服务端模型目录解析所选模型,合法的 GPT-5.5 优先于配置默认模型,原恢复模型仍作为第二次尝试;过期或不可用模型以安全的model_unavailable 409返回,且不扣点。操作身份同时纳入模型 ID,避免失败后切换模型仍复用旧 action fingerprint。 - 验证:增加请求序列化、首次启动、回答、切换模型后重跑、路由模型解析、不可用模型拒绝和服务分发回归;receipt 继续记录实际成功的 provider/model,而不是只记录界面选择。
- 防复发:任何带模型选择器的会话入口都必须证明 UI 会话模型、发出的命令和服务端解析结果一致;新增生成命令时必须同时更新 schema、幂等身份、客户端和路由测试。
- 相关记录:BUG-055、BUG-057、BUG-059
- 修复版本:待提交(本地可测)
BUG-061 | 同版本恢复响应被丢弃导致本轮双方消息同时消失
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正回答成功后的消息同步、冲突恢复与持久化对话历史
- 用户现象:接口已经成功返回新的自然语言回答和完整
conversationMessages,但页面没有显示本轮 Agent 回答,本轮用户气泡也在请求结束后消失。 - 触发条件:请求在途时,父级状态先同步到与成功响应相同的
turnVersion;临时用户气泡随旧版本结束而移除,随后控制器的同版本保护又直接丢弃成功响应。 - 根因:控制器把“同版本业务 turn 不应被晚响应覆盖”扩大成了“同版本响应的全部内容均不可采用”,没有区分业务状态与更完整的持久化消息 transcript。
- 修复:更高版本响应仍优先、较旧响应仍丢弃;同版本响应只有在携带更完整的持久化历史,且其最后一条 Agent 文案与当前轮一致时,才只补齐消息列表并保留当前 turn。新版本响应也优先采用服务端持久化历史,避免本地推断覆盖耐久真源。
- 验证:新增在途回答期间父级先同步到版本 8、随后恢复响应携带完整
Agent → 用户 → Agent历史的回归;确认本轮用户与 Agent 气泡均恢复,同时原有“外部同版本 turn 不被晚响应覆盖”测试继续通过。controller、client、route 聚焦测试 61/61 通过。 - 防复发:同版本保护必须分别比较业务 turn 与消息 transcript;完整且与当前 narrative 一致的耐久历史可以修复 UI,但不得用晚响应覆盖同版本外部业务状态。
- 相关记录:BUG-046、BUG-048
- 复发自:BUG-046
- 修复版本:待提交(本地可测)
BUG-062 | 两位年份与模型领域标签导致合法经历被降级为未完成分析
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正自然语言事件提取、非最终轮叙事校验与恢复模型调用
- 用户现象:用户用“21年”“23年”描述一段连续经历后,系统没有正常理解时间线,而是显示“这次分析暂时没有完成”的固定失败提示。
- 根因:事件提取器只识别四位年份,两位年份会被视为缺少日期;同时,首选模型虽然成功生成了完整回答,但旧版结构化输出中的下一问题领域标签不在技术 packet 当前建议集合内,整段回答因此被判为
ungrounded_evidence_domain。恢复模型随后超时,编排层最终保存了确定性失败提示。 - 修复:两位中文年份根据当前日期展开为最近且不晚于当前年的四位年份;旧版完整模型输出继续校验候选时间、范围、分盘和引用等事实字段,但下一问题的内部领域只保留与 packet 建议集合的交集,交集为空时才使用 packet 建议,不再因无害的领域措辞差异丢弃自然回答。
- 验证:新增“21年元旦回家备考、21年底封控、在家到23年”的提取回归,以及旧版模型选择非建议领域的叙事回归;事件提取、叙事和编排聚焦测试共 109 项通过。
- 防复发:自然语言年份解析必须覆盖常见缩写表达;服务端安全校验只约束可核验事实,不应让模型的对话选题标签覆盖或中断已通过事实校验的回答。
- 相关记录:BUG-052、BUG-057、BUG-059
- 修复版本:待提交(本地可测)
BUG-063 | 精确日期追问收到月日后重复确认同一节点
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正事件日期追问、上下文日期补全与 append-only 事件修订
- 用户现象:Agent 已询问 2026 年 7 月上旬决定开公司的具体日期,用户回答“7 月 10 号”后,Agent 又询问是否指 2026 年 7 月 10 日及其对应节点。
- 根因:日期提取器只直接识别带年份的中文日期;编排器只会补全“来年/同年”等相对年月,并且补全逻辑仅支持空日期,不支持把已有
2026-07提升为2026-07-10。简短回答因此被保存成新的未定日期事件,而不是目标事件的精度修订。 - 修复:仅在明确指向既有事件的
event_date追问中,从目标事件继承年份或年月;允许兼容的年到月、月到日精度提升,并继续以correctsEvidenceIds保存修订链。普通新事件不猜测缺失年份。 - 验证:新增
2026-07 · 决定开公司 → 7 月 10 号回归,断言生成2026-07-10修订、保留原事件、沿用原摘要且不再次询问日期归属。 - 防复发:无年份月日只能在定向日期追问且存在可靠目标日期时补全;日期精度提升必须与目标已有年份或月份前缀一致。
- 相关记录:BUG-028、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:07–05:08且 VedAstro 调用次数为 0,测试失败;修复后同一 fixture 进入外部验证,模拟官方事件扫描严格区分首选与次选后返回confirm_minute。活动事件评分、API 与技法合同相关测试 42/42 通过;前端相关 95 项中 94 项通过,唯一失败为既有“最多十条事件”兼容测试,与当前已取消最大事件条数的产品规则冲突,不属于本次改动。 - 防复发:测试必须同时覆盖“本地低置信但已形成窄候选”和“VedAstro 未执行或未区分时仍禁止确认”;研究稳定性诊断不得再次被用作外部验证的前置开关。
- 相关记录:BUG-051、BUG-055、BUG-058
- 修复版本:待提交(本地可测)
BUG-065 | 真实 VedAstro 事件扫描请求乘法导致生时校正数分钟无响应
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:生时校正进入外部候选区分后的同步响应时间、VedAstro
SearchEvents调用量 - 用户现象:本地候选已经缩窄并开始真实 VedAstro 验证后,请求运行超过五分钟仍未完成,网页表现为长时间等待或最终服务不可用。
- 触发条件:高严谨生时校正包含多条月级或年级事件,并且至少有两个候选分钟需要外部比较。
- 根因:每个候选都会扫描全部可映射事件,而 VedAstro adapter 又把月级事件拆成约十二个采样日;十二条事件、两个候选时最坏接近 288 次同步 HTTP 请求。mock 每次立即返回,因此原回归只证明进入了 VedAstro,没有暴露真实网络延迟。
- 修复:生时校正专用调用层按 VedAstro 原生
career、wealth、marriage领域各选择一条最强事件;直接映射优先于代理映射,日级优先于月级和年级,同精度选择较新事件。月级和年级事件只取范围中点作为代表日,两个候选最多发起六次外部事件扫描;响应明确记录符合条件事件数、实际选中数和选择策略,不再暗示全部事件均已外部验证。通用 range scan 保持不变。 - 验证:长对话回归断言十二条符合映射的事件只产生六次扫描,两个候选各覆盖事业、财富、关系三领域,所有扫描均为单日;相关 Python 测试 42/42 通过。使用真实 VedAstro key 重跑同一十二事件 fixture,首次完整命令约 9.7 秒、缓存后约 3.6 秒,官方响应状态为
official_verified。真实SearchEvents对05:07与第二候选05:21返回相同的三领域命中与 36 分信号,因此正确保持vedastro_candidate_not_discriminated,没有把无法区分的外部结果伪装成分钟确认。 - 防复发:任何真实外部服务回归都必须同时锁定请求数量与代表事件选择,不得只用零延迟 mock 证明“已调用”;新增事件或采样策略时必须评估候选数 × 事件数 × 日期采样数的乘法上限。
- 相关记录:BUG-055、BUG-064
- 复发自:无
- 修复版本:待提交(本地可测)
BUG-066 | SearchEvents 被误作最终分钟裁判且未比较官方分钟敏感层
- 状态:resolved
- 首次发现:2026-07-24
- 最近更新:2026-07-24
- 影响面:高严谨生时校正候选比较、VedAstro 官方验证与最终分钟确认
- 用户现象:本地事件评分已经得到首选与次选分钟后,系统仍依赖 VedAstro
SearchEvents的事件命中差异决定能否确认;两个候选即使 Ascendant、宫位、D9、D10 或 Dasha 技术状态不同,只要事件搜索指标相同就无法继续收敛。 - 触发条件:进入 VedAstro 外部验证的两个窄候选在
SearchEvents中返回相同事件数量和 signal lift,或反过来事件搜索指标不同但官方分钟敏感快照相同。 - 根因:旧外部验证合同把事件搜索指标当成候选分钟的最终判别器,却没有从 VedAstro 官方完整快照提取并比较 Ascendant/宫位边界、D9、D10 和 Dasha 边界身份。
SearchEvents适合补充事件背景,不足以独立证明某个出生分钟正确。 - 修复:为两个排名最高的候选生成不含原始响应的 VedAstro 官方分钟快照,分别对 Ascendant 与宫位、D9、D10、Dasha 边界生成安全 fingerprint;至少一个官方分钟敏感层存在差异时才通过候选身份区分 gate。候选排序继续以本地已发生事件评分为准;
SearchEvents改为event_background_validation,明确used_for_decision=false。KP cusp/sub-lord 因当前已验证官方接口没有可审计结果而保持 unsupported,不以IsPlanetInHouseKP冒充。 - 验证:官方快照适配器测试锁定四个分钟敏感层均生成非空 fingerprint、KP 明确 unsupported,且响应不泄露 API key 或 raw response;活动事件集成测试锁定 SearchEvents 指标完全相同时仍可由分钟敏感层差异完成区分,并锁定 SearchEvents 即使明显偏向首选、分钟快照完全相同时也必须保持
can_apply=false和vedastro_minute_sensitive_layers_not_discriminated。相关聚焦测试通过。 - 防复发:任何最终分钟确认都必须区分“本地事件排序”“VedAstro 官方分钟技术身份验证”和“SearchEvents 背景验证”三种职责;两个官方快照有差异只证明候选技术状态不同,不得描述为 VedAstro 已独立证明本地首选分钟正确。
- 相关记录:BUG-055、BUG-064、BUG-065
- 复发自:无
- 修复版本:待提交(本地可测)
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,不能只跑单元测试或tsc;App Routerroute.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 校验中持久化
prompt、answerMode与proposedDate。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-235、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 <= 5且margin >= 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 持久化、日期候选确认与数据库约束
- 用户现象:代码已写入
prompt、answerMode和proposedDate,但生产迁移工作流不会上传或执行对应 migration;生产数据库仍可能按旧 JSON 合同拒绝新 turn。 - 触发条件:从 GitHub Actions 手动执行生产生时校正 migration 的
check或apply操作。 - 根因:新增
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_no和followUp.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-v3case,首轮候选已经包含rangeStart/rangeEnd,但可见 narrative 没有显示它们;旧生产版本还会调用模型生成同类泛化开场。 - 根因:BUG-072 的快速首轮正确保留了候选范围,却只把范围写入结构化
candidate;首页不单独渲染该字段,固定 narrative 仍沿用了不含范围的旧提问。 - 修复:复用首轮已经计算出的声明范围,在确定性 narrative 中明确显示当前待核对边界,并说明它不是已确认分钟;继续只问一件带年月的重要经历,不恢复零证据模型调用或分钟扫描。
- 验证:Orchestrator 回归断言首轮正文包含真实
rangeStart–rangeEnd、未确认边界和单事件提问;聚焦测试、目标 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:26–05:30,无空问题死状态,原active_birth_time不变且未主动保存前acceptedRange为空。静态浏览器验收确认日期修订输入框、范围保存按钮、非确认分钟文案和 390px 无横向溢出;真实登录态 UI 因隔离服务未连接认证配置,本轮未声称已验收。 - 防复发:生时校正业务真源必须是 V4 Case 和 append-only 事件台账,不得从聊天正文反向恢复;任何公开结果只能显示通过稳定性门的范围,单分钟峰值不得成为可保存结果;证据不足必须生成下一题或明确暂停,不能进入
awaiting_answer + currentQuestion=null;V4 路径不得写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 所在的 Composedefault网络。 - 根因:
rectification-v4-worker只加入app网络;api只在default网络。Worker 虽正确配置JYOTISH_API_BASE=http://api:5200,但 Docker DNS 无法跨网络解析和连接api。 - 修复:让 staging Worker 同时加入
default与app网络,不改变 production compose,也不暴露 API 端口。 - 验证:部署合同测试固定 Worker 的内部 API 地址和双网络配置;聚焦测试、Compose 渲染、目标 ESLint、
git diff --check及 staging 登录态 smoke 另行记录。 - 防复发:需要同时访问内部 API 与 staging PostgreSQL 的服务必须在 Compose 合同中显式加入两个现有私有网络。
- 相关记录:BUG-082
- 修复版本:本次修复提交
BUG-085 | 生时校正回退为独立面板和固定领域问卷
- 状态:closed_obsolete(2026-09-26 对账:V5 主链
8ade6ed5已于f3946eaa(2026-08-07,retire legacy rectification runtime)随rectification-agent/rectification-v4整体删除,现行校正走rectification-agentic;本记录描述的面板、问卷控制流与修复对象均已不存在) - 首次发现:2026-07-27
- 最近更新:2026-07-28
- 影响面:生时校正聊天 Surface、事件语义、后台 Job、候选计算、诊断、Reasoner、Renderer 与持久化主链
- 用户现象:进入生时校正后看到独立的校正面板、证据区域和固定问题;交互不像普通 session,领域也不再根据用户刚讲的经历动态选择。
- 触发条件:旧 V4 既在界面层使用独立校正结构,又让
question-planner.ts和question-author.ts直接决定领域顺序与问题文案;模型只负责写下一问,后台没有形成完整 Agent 决策闭环。 - 根因:产品状态被压缩成“下一问字符串”,事件语义、候选特征、诊断结果、问题机会、模型决策和公开消息之间没有受约束的 durable contract;因此即使替换提示词,系统仍会沿用问卷式控制流,且无法审计模型为何选题或安全重放已完成 Job。
- 修复:删除旧
question-planner.ts与question-author.ts,将回答处理重构为完整 V5 主链:保存回答并创建后台 Job → Evidence Reconciliation → Candidate Engine / Feature Snapshot → Diagnostics → Opportunity Builder → Bounded Reasoner → Decision Validator → Renderer → Atomic Job Completion。可见层继续复用普通 session 聊天 Surface;Reasoner 只能选择服务端生成的 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-engine、identity-auth-integration、onboarding-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
- 修复版本:
8ade6ed5(本地 V5 重构随后以此提交);已被f3946eaa下线,不再适用
BUG-086 | 模型下一问可绕过当前事件而跳成领域问卷
- 状态:closed_obsolete(2026-09-26 对账:V5 主链
8ade6ed5已于f3946eaa(2026-08-07,retire legacy rectification runtime)随rectification-agent/rectification-v4整体删除,现行校正走rectification-agentic;本记录描述的面板、问卷控制流与修复对象均已不存在) - 首次发现: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 policy;Renderer 根据 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
- 修复版本:
8ade6ed5(本地 V5 重构随后以此提交);已被f3946eaa下线,不再适用
BUG-087 | 语义机会仍退化为固定文案并在拒绝后重复追问
- 状态:resolved
- 首次发现:2026-07-29
- 最近更新:2026-07-29
- 影响面:生时校正 Agent 的证据协调、问题机会、Reasoner 上下文、Renderer、候选范围提示与 Skill 合同
- 用户现象:对话会把月份已经明确的经历继续机械追问具体日期,按固定领域顺序轮询;用户表示不知道、跳过或换方向后仍可能回到同一事件,Renderer 还会重复套话和未变化的候选范围。
- 触发条件:Question Opportunity 直接持久化最终
prompt,Renderer 再用服务端问题覆盖自然生成结果;缺失领域按固定数组选择,日期策略把所有非日精度事件视为未完成,且证据协调未完整区分拒绝、未知、换向和回答了另一事件。 - 根因:机会合同混合了“为什么问、要补什么字段”和“最终怎么说”,导致 Reasoner 看不到最近对话语义、Renderer 无法安全自然表达;同时 target disposition 和重复追问预算不完整,固定领域与日期控制流绕过了信息增益、隐私成本和稳定性诊断。
- 修复:升级为兼容旧
prompt的semantic-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 测试未归一化 PostgreSQLDate联合类型。 - 根因:测试夹具落后于现有生产合同;这些报错不来自 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.txt与pyproject.toml仅声明mcp>=1.0;CI 在 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.txt和pyproject.toml,防止任一入口再次放宽到 MCP 2.x;Python 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 显式按createdAt、eventId、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_ENABLED、RECTIFICATION_AGENT_V5_SHADOW、RECTIFICATION_AGENT_V5_CANARY_PERCENT;因此selectRectificationDeploymentMode()把新 Case 持久化为v4_legacy,Orchestrator 必然调用 Legacy Projector。 - 修复:受控 staging rollout 现在原子写入 V5 Agent 开关,public 与 smoke rollout 使用
v5_agent、100% canary,并重建 web/worker;staging 中唯一满足 V6 版本、未完成、无 open Job 条件的错误 Case 已原位升级为rectification-evidence-v5/v5_agent,历史 Turn、Event、Job 与 Agent Run 保持不变。 - 验证:rollout 脚本测试断言三项 V5 选择器只写一次,并在成功前核对 web/worker 容器实际读取的
enabled、shadow、canary;staging 活跃 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_eventOpportunity 排序、问题验证、Renderer telemetry 与 staging 用户可见下一问 - 用户现象:用户提交“2020 年 4 月去石油化工研究院实习做研究员”后,系统仍显示“承接……请再说一件……哪次搬家、离乡或长期迁居……”,像固定问卷。
- 根因:Builder 将最近六轮答案拼接为当前主题,使较早教育事件中的“离家/外地”再次提升 relocation,同时未覆盖领域奖励在已经满足最小领域数后仍占主导;Renderer 对自然
new_dated_event问法使用过窄词面校验,校验失败后静默替换为 Builder 固定 fallback,telemetry 仍记为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 投影到对应 Turn;UI 的 thinking 状态只是客户端进度动画,不是模型 reasoning,也不是服务端执行收据。 - 修复:将“分析过程”定义为与 Turn 关联、可持久化和刷新后可恢复的服务端执行收据;只投影实际发生的阶段、工具调用和 allowlist 技法。供应商显式返回的 reasoning 内容只有通过服务端来源校验与安全过滤后才可作为可选摘要,缺失或不安全时直接省略,不伪造且不读取 hidden chain-of-thought。
- 安全边界:不公开分数、权重、贡献矩阵、内部 ID/字段、候选分钟、工具参数或原始结果、Prompt、模型内部信息和用户敏感原文;D60 不展示且不驱动结论;未执行、不可用或仅供参考的技法不得显示为已执行。
- 兼容边界:历史无收据记录继续读取;
v4_legacy与v5_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返回为 JavaScriptDate,且历史资料的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 / rendering;Case 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 layer,
KP_cusps为 optional、D60 为 reference-only、未知层失败关闭;事件 provenance 仅用于审计且不参与加权。 - 验证:本工作树的聚焦合同覆盖跨午夜分钟枚举、LODO 低于
0.8拒绝公开范围、required layer 缺失阻塞、KP_cusps/D60 缺失不阻塞,以及旧 Snapshot 兼容;本记录不把这些回归误写成独立 holdout 验证。 - Partial / deferred:per-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,没有保留 canonicalevent_kind。 - 根因:Agent 只在服务器预先枚举的机会中选择,无法基于完整 Case Dossier 自主理解当前访谈焦点并生成下一句;公开回复失败时继续由领域正则模板接管。与此同时 Python legacy request 把领域值当作事件种类,抹平关系事件的 start/end/change 语义。
- 修复:新增 Director 两阶段合同:服务器提供完整 Case Dossier,Director 可在一轮提出多个 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 继续传递 canonicalevent_kind,并为 relationship start/end/change 保留可验证的最小区分。 - 验证:TypeScript 相关套件 114/114 通过,其中 Director 专项 7/7;TypeScript
tsc --noEmit与修改文件 ESLint 通过;Pythontests/test_active_rectification_events.py14/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写入账本。 - 触发条件:
reviseproposal 引用真实 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_review;V5 completion 增加 ownership/target/replay 安全的 Pending resolution,并且date_unresolved只在日期确实变化后关闭;relationship_end强制pending_review,迁移清除由旧 scoreable 关系结束事件支持的最新 Snapshot,Dossier 与 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、或没有currentQuestion的awaiting_answerCase;pending_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_start与relationship_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_diagnostic,Dossier 没有 revision、Observation 和候选假设;Runtime 只累计旁路诊断数组,前端没有把既有 Job phase 投影成稳定的分析步骤。 - 修复:新增服务器拥有的
case_read、candidate_scan、evidence_gap、diagnostic_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 | 开放问题收集年份事件后先切换新事件、下一轮再回访旧事件
- 状态:resolved(staging pending deployment)
- 首次发现:2026-07-31
- 最近更新:2026-07-31
- 影响面:V8 Director 最终规划、确定性 fallback、年份/季度精度事件的访谈连续性
- 用户现象:开放问题收到一件只有年份的本人事件后,系统先用固定句式要求另一件经历;收到第二件月份明确的经历后,又突然回头追问第一件事件的月份,表现为事件焦点来回跳转。
- 触发条件:上一问没有
questionTargetEventId,本轮新建的可评分事件只有year或quarter精度,同时 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 会锚定该事件并请求
month;Orchestrator 回放断言开放问题收到年份事件后,下一问立即补月份且targetEventId指向新事件。相关 Director/分析轨迹测试、TypeScript 和 ESLint 均通过;完整前端测试 1157 项通过。 - 防复发:事件连续性由服务器的
currentTargetEventId + targetDisposition约束,不能只靠 Agent prompt;新事件的必要日期精度应在切换话题前闭合,用户明确“不知道/跳过/换方向”时才解除目标。 - 相关记录:BUG-092、BUG-097、BUG-106
- 修复版本:local / pending release
BUG-108 | V8 丢失事件承接说明且服务器领域模板覆盖 Agent 自主选题
- 状态:resolved(staging 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/52;TypeScript、ESLint 通过;前端全量测试 1164/1164。
- 防复发:回归测试覆盖模型伪造候选结论、手动 regenerate 以及历史
selectedOpportunity持久化路径。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-110 | 月份简答未继承目标事件年份导致重复追问并暂停
- 状态:resolved(local)
- 首次发现: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-09revision、无 pending、下一问不再绑定原事件且状态保持awaiting_answer;Director 测试确认记录fallback_rejected:question_repeated。 - 防复发:单元测试分别锁定确定性日期继承、Evidence 阶段边界、fallback 原因和完整两轮回放。
- 相关记录:BUG-104、BUG-107、BUG-109
- 复发自:无
- 修复版本:local / pending release
BUG-111 | Director 校验器覆盖 Agent 公开回复且复合事件缺少公开语义
- 状态:resolved(local)
- 首次发现: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 生时校正
- 状态:resolved(staging 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.ts、frontend/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_rectificationSession,页面保持空白;网络中没有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:00–23: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 重试循环
- 状态:resolved(local)
- 首次发现:2026-08-03
- 最近更新:2026-08-03
- 影响面:
POST /api/rectification/agent、首页账户资料重新加载、Session 自动恢复、GET /api/account请求频率 - 用户现象:账户接口已返回完整出生日期、时间线索和地点,Agent opening 仍返回
profile_incomplete;页面随后重复请求/api/account和/api/rectification/agent。部署首轮修复后,刷新页面还会错误回到“先完成出生资料”。 - 触发条件:数据库驱动把出生日期投影为
YYYY-MM-DDT00:00:00.000Z,同时当前活动 Session 是birth_time_rectification。 - 根因:Agentic profile loader 和首页
readProfile()都把持久化日期直接交给只接受纯YYYY-MM-DD的资料完整性校验;首轮只兼容了账户 JSON 中的 ISO 字符串,但 staging 自托管 PostgreSQL 数据层在 Agent loader 内实际返回 JavaScriptDate,因此服务端仍误判missing_birth_date。失败回调清空rectificationSessionId却未设置现有的自动恢复暂停状态,resume effect 又会立即重新挂载聊天并再次发送 opening。 - 修复:共享持久化日期规范化函数统一接受合法
Date、ISO 字符串和纯日期字符串,由 Agent loader 与首页账户重新加载共同复用;服务端资料失败时先设置rectificationError暂停自动恢复,资料成功保存后再清除暂停状态。 - 验证:staging 只读诊断确认目标账户的
birth_date在服务端为Date,时间、地点和误差字段类型均有效;回归覆盖 PostgreSQLDate、数据库 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_rectificationSession,成功回复先原子更新完整消息再发送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 | 用户采纳最强候选后无法保存为平台排盘时间
- 状态:resolved(local)
- 首次发现:2026-08-04
- 最近更新:2026-08-04
- 影响面:Agentic 生时校正候选结果、个人资料出生时间、后续咨询排盘时间
- 用户现象:
04:55已是最强候选,用户多次明确表示“就用 04:55”,但 Agent 因唯一分钟确认门未通过而拒绝保存,个人资料和后续排盘仍未使用该时间。 - 根因:系统把引擎候选、用户采纳和引擎唯一确认压缩成单一
confirmed状态;没有可持久化的候选身份和用户采纳边界。 - 修复:引入
candidate / accepted / confirmed三态;服务端持久化候选身份、相对支持度、Session 所有权和 Profile 基线;新增 service-role 原子采纳 RPC;前端展示候选卡并允许用户采用;accepted接入个人资料和全平台排盘。 - 数据边界:相对支持度仅表示本次候选间的归一化比较,不是统计概率;保留
reported_birth_time;accepted不冒充引擎唯一确认。 - 安全边界:RPC 校验用户、Session、结果身份、有效期、候选成员、最新结果和 Profile 基线;出生申报资料变化使旧候选失效;采纳不计费。
- 验证:TypeScript 通过;聚焦测试 90/90;完整测试 1221/1221;lint 0 error、3 个既有 warning;production build 通过;本地 PostgreSQL 验证
04:55写为accepted、保留05:00reported time、重复采纳幂等,并验证出生申报时间变化会使结果失效且拒绝再次采纳。 - 相关记录:BUG-113、BUG-114、BUG-115、BUG-116
- 修复版本:待提交与发布
BUG-118 | 候选已落库但确认卡不显示,VedAstro 未执行被误述为未通过
- 状态:resolved(local,pending 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_evaluated与fail,不得把诊断项描述为硬阻塞。 - 验证:真实 local PostgreSQL business client 回归覆盖未过期候选读取;Agentic 工具回归覆盖
not_evaluated映射为external_validation_invoked=false;Session、entry、candidate persistence 聚焦测试通过。 - 防复发:self-hosted query builder 新增 Supabase/PostgREST 链式操作时必须由真实 PostgreSQL fixture 覆盖;公开文案必须按
external_engines.status区分未执行、失败和通过。 - 相关记录:BUG-116、BUG-117
- 修复版本:待本次 staging 修复提交与部署验收
BUG-119 | 候选采用覆盖兼容出生时间且个人资料不显示双时间记录
- 状态:resolved(local,pending 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()只刷新账户对象,没有同步个人资料展示使用的独立profilestate。 - 修复:新增向前迁移解除
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:55、birth_time_status=accepted、reported_birth_time=05:00、birth_time=null。 - 相关记录:BUG-117、BUG-118
- 修复版本:待提交与发布
BUG-120 | Agent 仍在追问事件时过早显示候选采用卡且采用后无法改选
- 状态:resolved(local,pending 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:00、birth_time=null;TypeScript、聚焦 ESLint 与 production build 通过;桌面端三列和移动端横向滚动截图已完成视觉检查。 - 防复发:任何非唯一候选卡必须由显式选择阶段开启;同一 Agent 回复不得既索取新证据又提供采用操作;候选采用测试必须覆盖幂等、改选、过期结果和 Profile 基线漂移。
- 相关记录:BUG-117、BUG-118、BUG-119
- 修复版本:待提交与发布
BUG-121 | 月份与区间事件在确认工具中被序列化成错误日期格式并耗尽 Agent 步骤
- 状态:resolved
- 首次发现:2026-08-04
- 最近更新:2026-08-04
- 影响面:Agentic 生时校正结束收集、V5 评分/诊断、旧确认门适配、无回复退款兜底
- 用户现象:用户明确表示不再补充事件后,接口返回“生时校正没有生成有效回复,本次不会扣除点数,请重新发送”,没有展示最终候选或后续选择。
- 触发条件:历史证据同时包含
year、month或range精度;Agent 在结束收集时调用rectification-confirm。月份或年份事件被发送成完整日期,区间事件又可能使用/、to等自然分隔形式。 - 根因:共享事件 schema 只检查字符串长度;V5 转换只识别
..区间;toV3Event()又把标准化后的YYYY-MM-DD与month/year精度一起发送给只接受YYYY-MM/YYYY的旧确认端点。引擎持续返回event date does not match its precision,Agent 在 8 个工具步骤内反复修正和重试,最终没有剩余步骤生成公开文本。该问题是 BUG-116 的输入契约残余变体,提高步骤数只能延后失败。 - 修复:事件日期统一复用严格日历范围转换;V5 保留年月日和区间的
date_start/date_end,并兼容..、/、to、中文范围符和紧凑年月范围;旧确认端点按精度发送严格的YYYY、YYYY-MM、YYYY-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 - 用户现象:身份库已持久化
admin或viewer角色的用户登录主站后,账户菜单不显示后台入口;即使显示旧入口,主站/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,仅admin或viewer可见入口,且不使用ADMIN_EMAILS替代角色授权;GET /api/account在服务端解析身份配置并返回AUTH_ADMIN_ORIGIN + /admin/codes,Supabase 模式继续返回/admin/codes;账户与侧栏类型透传该 URL,并将文案改为“后台管理”。后台独立登录和requireAdminSession保持不变。 - 验证:
frontend/tests/admin-contracts.test.ts、frontend/tests/admin-users-contract.test.ts、frontend/tests/account-api.test.ts、frontend/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 session;
identity.users.role的持久化admin是唯一后台授权事实。Better Auth 插件的/api/auth/adminendpoint 继续在主站 fail-closed404,未知 Host 继续421。 - 修复:删除活动运行时后台 origin/secret 与
services.admin,服务端数据 client 和requireAdminSession统一读取 user session;后台 layout 增加服务端 gate,匿名转/login、非 admin 不渲染;所有后台 API 保留独立 guard,payments/packages 改用requireAdminSession;isAdminUser、账户入口和 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_ORIGIN、BETTER_AUTH_ADMIN_SECRET或浏览器 admin auth service;viewer对后台入口、页面、读 API 和写 API 均必须为403;入口可见性不能替代 route guard。 - 相关记录:BUG-010、BUG-083、BUG-084、BUG-122
- 复发自:BUG-122
- 修复版本:
435e628806390e7ae138363491e7bae63ee801d4,staging 已验收
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_packages、payment_orders权限,也未添加对应 RLS 策略;因此数据库健康且新 SHA 已部署,后台 SQL 仍被 PostgreSQL权限门禁拒绝。同日还确认 staging 迁移与部署工作流错误地要求目标 SHA 属于main历史,使完全独立的测试分支被生产分支阻塞;该控制面耦合导致为恢复 staging 而误合并生产 main。 - 修复:支付记录改为通过
queryAdminRows执行参数化 SQL,联表public.payment_orders、public.payment_packages和identity.users,以窗口计数保留分页合同并用独立聚合 SQL输出统计;不再使用 Supabase builder 或 Admin Auth。后台在支付管理之后新增独立“套餐管理”资源和页面,套餐新增、编辑、停用、错误重试及原字段保持完整,支付页只保留概览、Z-Pay(易支付)渠道配置和支付记录。配置读取补回chat_enabled与chatEnabled。创建订单完成登录、开关、配置、SSRF、套餐和订单校验后,直接返回带sign/sign_type的标准submit.php收银台 URL,不服务端请求网关、不返回商户密钥;对话页用浏览器打开该 URL,套餐加载异常显示安全错误,正常enabled=false仍静默隐藏。复发修复将 Z-Pay 配置改为默认收起的 Ant DesignCollapse,展开后才显示表单和操作;为 AdminApp 增加admin-app-shell的100dvh独立纵向滚动边界而不改聊天全局规则;套餐 CRUD 全部改用queryAdminRows参数化 SQL、UUID 校验、returning与 404;易支付读取仅在 PostgreSQL42P01时回退环境变量,保存直接参数化调用public.admin_save_epay_settings并使用函数返回行,保留原子审计和脱敏响应。2026-07-30 新增前向迁移20260730010000_admin_payment_permissions.sql,向admin_runtime最小授予套餐读写、订单只读、易支付配置读取及保存函数执行权限,并为启用 RLS 的支付表补齐角色策略;不授予订单写入或删除权限。Gitea 与 GitHub 的 staging 迁移、部署和测试环境运维工作流统一 checkoutstaging,删除 staging SHA 属于main历史的要求;生产工作流保持不变。误合入 main 的 PR #1 已由 PR #2 的 revert 恢复,恢复后 main 内容树与合并前提交43581ac0f75e7f157032503475e878bd53ad161d完全一致。针对 run 1309,Gitea 两条远端工作流的 previous-SHA 探测与 registry login/logout 保持sudo -n docker;脚本只接受受控的docker或sudo -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=docker,DOCKER_CONFIG仍指向 incoming 的.docker;cleanup 使用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_runtimegrant/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_settings的admin_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 | 个人报告入口对不可用出生时间状态错误开放
- 状态:resolved(local,pending staging deployment)
- 首次发现:2026-08-06
- 最近更新:2026-08-06
- 影响面:首页个人报告 CTA、
POST /api/reports出生时间门槛 - 用户现象:资料流程已经完成、但出生时间仍为
reported或candidate的用户会看到“生成个人报告”,点击后服务端必然返回422 birth_time_not_usable。 - 触发条件:用户有咨询会话和消息,
profileComplete=true,但当前排盘时间尚未被用户采用或引擎确认。 - 根因:首页只用资料完整度判断入口可见性,没有镜像报告 API 的
accepted/confirmed + 有效 active time门槛;UI 与服务端各自正确但组合后形成误导入口。 - 修复:首页复用既有
isBirthTimeReadyForConsultation(profile),只有accepted或confirmed且当前排盘时间有效时才显示个人报告入口;服务端门槛保持不变,不把候选范围或填报时间伪装成已采用时间。 - 验证:
frontend/tests/personal-report-entry.test.ts15/15 通过,新增回归直接覆盖reported=false、candidate=false、accepted=true、confirmed=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、GiteaStaging 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 run1459在完整 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 全迁移验收、GiteaStaging Backend Quality Gate - 用户现象:quality gate run
1456的 1409 项 frontend 测试中 1408 项通过;真实 PostgreSQL fixture 成功创建public.personal_reports后,精确表集合断言因预期值缺少该表而失败。 - 触发条件:在完整 Docker/PostgreSQL runner 中应用全部迁移并枚举
publicschema 表。 - 根因:个人报告迁移契约覆盖了双迁移语义、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 env;root 控制脚本验证一个由
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 校验两个 env;backup 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 run1465在完整 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
- 复发自:无
- 修复版本:
f7a615a5bf11ed95b3a6c7e6d28dfe8150a825ef;staging migration/deploy 与安全验收完成
BUG-129 | staging trusted-main checkout 无界 fetch 导致自动部署长期占用 mutation queue
- 状态:resolved
- 首次发现:2026-08-06
- 最近更新:2026-08-06
- 影响面:Gitea staging deploy/migration 控制器的 trusted-main checkout;production 与 staging 应用数据面未受影响。
- 用户现象:exact-SHA quality gate run
1473成功后,自动 deploy run1474在git fetch --no-tags origin main "$DEPLOY_SHA"长时间没有日志进展;fetch 后续自行恢复,run 最终于 18 分钟成功部署02cc483b7c303e6cc0f26fb31462c50adb007f12。第一轮 bounded-retry 修复合入后,run1480的 3 次 120 秒 fetch 全部在服务端压缩 16,093 个对象时耗尽并 fail closed;SSH/远端 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 == stagingcontroller,手工旧版 rollback 也不得执行旧 controller。refs 与 forward/rollback 关系通过有界 Gitea API 和完整 commit-DAG 路径证明,字段缺失、分页不完整、头不一致或证据冲突均 fail closed。 - 验证:第一轮 bounded retry 的本地 workflow contracts 31/31、PR gates
1475/1477与 staging gate1479成功;run1480证明 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 gate1481、staging gate1483成功。自动 deploy1484在 1 分钟内成功部署e59f15d352787f3d05425ba8c459d092e9801a20,日志 mutationgit fetch=0、controller hash check 存在、SSH secret 遮蔽且无私钥材料;main/staging/public/state精确一致,5 个容器 restart count 均为 0,health、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 第一轮修复未覆盖对象范围
- 修复版本:
e59f15d352787f3d05425ba8c459d092e9801a20;gate-attested controller bundle 已完成 staging exact-SHA 验收
BUG-130 | self-hosted 计费查询与订单领域调整缺少并发和审计边界
- 状态:resolved(local)
- 首次发现: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 不支持的关联 select,adapter 又缺少
lte/like参数化能力;一次性限制没有由不可变订单快照和数据库唯一约束共同承担;订单状态和余额缺少统一的管理员领域函数、幂等请求、乐观版本及审计合同;旧兑换码 RPC 不接收 reason。 - 修复:商品及权益改为两次简单查询后在服务端组装,adapter 增加参数化
lte/like;订单创建写入不可变oneTimePerUser商品快照,结算与人工补偿统一通过(user_id, product_code)唯一兑换记录原子阻止重复发放;新增admin_adjust_order,只允许retry_grant、compensate、record_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.ts与tests/database-local-business.test.ts通过;计费 route/reauth/contract 测试 38/38 通过;目标 ESLint 与补丁检查通过。全仓 TypeScript 当前被共享工作树中非本任务的tests/identity-auth-integration.test.ts:412类型错误阻断。 - 防复发:self-hosted 支付查询不得重新引入 nested PostgREST select;LIKE/范围条件必须参数化;一次性权益必须同时依赖不可变快照和数据库唯一约束;订单与兑换码后台不得直接更新余额或订单状态,所有写入必须经过带 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 Gatevalidate/publish 的 exact-SHA checkout;staging mutation controller、应用数据面与 production 未受影响。 - 用户现象:docs-attestation staging gate run
1485在 validate 的首步失败;Gitea 已枚举/压缩 3,246/2,895 个 shallow objects,但客户端传输降速后触发curl 28 Operation too slow、early EOF。publish 被依赖关系跳过,自动 deploy 未触发;公网继续健康运行e59f15d352787f3d05425ba8c459d092e9801a20。 - 触发条件:quality gate 的 exact-SHA
--depth=1fetch 只有单次调用,并把低速失败设为连续 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 gates1519/1523、staging push gate1525均成功;最终 gate 对 exact SHA6c1dcbe857006ec6ae7463b57b2b7d5947da4851完成 validate/publish,artifact ID12成功上传,deploy1526成功。 - 防复发:质量门禁和 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 gate1525与 deploy1526均成功。 - 防复发:任何
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 gate;customer handler 权限和业务逻辑未受改动。 - 用户现象:PR gate run
1493的 Python 路由回归和 frontend 1472/1472 均通过,但next build报Next.js can't recognize the exported runtime field in route. It mustn't be reexported,publish 被跳过,自动 deploy 未触发。 - 触发条件:legacy
/api/admin/usersRoute 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、默认 Turbopacknext build、next build --webpack、全 route segment config re-export 扫描与git diff --check已通过;Gitea PR gate1499及最终 PR/push gates1523/1525均完成 production build,deploy1526成功。 - 防复发:Route Handler 的
runtime、dynamic、revalidate等 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 run1502;应用容器、业务数据库和 production 未被修改。 - 用户现象:staging gate
1500已成功验证并发布 SHA9a3d0d440f43deab66c1f8a4a08cdbfc6f9d73eb的 immutable artifact,但自动 deploy1502在远端 env 校验时报invalid staging selector: ADMIN_USER_ORIGIN并 fail closed;公网继续健康运行旧 SHAe59f15d352787f3d05425ba8c459d092e9801a20。 - 触发条件:包含双 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 未在发布前同步新增的非密钥 selector;quality gate 验证仓库合同,不读取主机 secret/env,因此直到 mutation 前远端校验才暴露漂移。
- 修复:已在共享 staging mutation lock 下,仅向原文件原子补入公开
ADMIN_USER_ORIGINselector,保留全部既有内容、deploy:deployowner 和0600mode;未输出、复制或重写其他 secret 值。随后完整 validator 暴露独立的 service runtime 漂移,转由BUG-135处理;仍待 exact-SHA artifact 重新部署。 - 验证:脱敏只读检查先确认
.env.staging为deploy:deploy 0600、AUTH_USER_ORIGIN精确且唯一、ADMIN_USER_ORIGIN计数为 0;原子修复后ADMIN_USER_ORIGIN精确且唯一,正式 validators 与 deploy1526通过。最终 user host admin paths 为 404;admin 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
- 复发自:无
- 修复版本:
6c1dcbe857006ec6ae7463b57b2b7d5947da4851(staging 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_runtimelogin 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_rolemembership 和数据库 CONNECT;将 raw password 仅原子写入.env.staging.database,percent-encoded URL 仅原子写入.env.staging,两文件保持deploy:deploy 0600。随后撤销admin_runtime的service_rolemembership;未重启容器、未输出凭据、未改 production。已应用的20260806000000_personal_reports.sqlchecksum 与仓库一致,保持历史迁移不可变。 - 验证:两个正式 env validator 均通过;
service_runtime真实密码登录、service_rolemembership 和 CONNECT 均通过;最终审计再次确认service_membership=true、service_connect=true、admin_service_membership=false、admin_bypassrls=false、service_can_login=true。两份 env 均为deploy:deploy 0600,migration1516、push gate1525和 deploy1526成功。 - 防复发:非空数据卷不能依赖
/docker-entrypoint-initdb.d自动重放;每次新增 runtime role 或 host-managed URL 都必须有兼容性 role repair、脱敏 pre-deploy presence 检查和真实登录/role-membership smoke。admin_runtime永不得继承service_role;service writes 只能使用独立SERVICE_DATABASE_URL。已应用迁移不得为修正文案而改 checksum。 - 相关记录:BUG-128、BUG-134、ERR-093、ERR-098
- 复发自:无
- 修复版本:
6c1dcbe857006ec6ae7463b57b2b7d5947da4851(staging role/env 与 exact-SHA 验收)
BUG-136 | staging quality gate frontend build 无界卡住并耗尽 45 分钟 job
- 状态:resolved
- 首次发现:2026-08-06
- 最近更新:2026-08-06
- 影响面:Gitea
Staging Backend Quality Gatevalidate job、staging artifact publication;应用代码、staging host 和 production 未被本次失败修改。 - 用户现象:push gate
1507对 reviewed SHA6fd22921197715e065d0d137fbd7ea5a82a188e4完成 frontend1472/1472、ESLint0 error,Next.js 输出Compiled successfully in 38.2s后约 44 分钟无 further output,45 分钟 job 超时,publish 被跳过;公网继续运行旧健康 SHAe59f15d352787f3d05425ba8c459d092e9801a20。 - 触发条件:质量门禁执行
npm run build --prefix frontend没有命令级 bounded timeout;Turbopack 在编译后静态生成/收尾阶段无输出卡住时只能等待 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/35;PR gates
1511/1519/1523和 staging push gates1514/1521/1525均在 600 秒 command deadline 内完成 production build;最终 gate1525publish 与 deploy1526成功。 - 防复发:所有可能长时间静默的编译、镜像构建和外部网络步骤都必须有命令级 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与 migration1516成功后,deploy1517、1518均启动目标 web/API image 并达到容器 healthy,却在约 3 秒后的公网 verification 返回非零,随后成功恢复旧 web/worker;公网和.state/deployed-revision均保持旧 SHAe59f15d352787f3d05425ba8c459d092e9801a20。 - 触发条件: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/35;deploy
1522的脱敏摘要证明 bounded verifier 等待到目标 public SHA 后才报告独立 admin-host 问题;后续 PR gate1523、push gate1525、artifact ID12和 deploy1526均成功,最终公网/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.chatTLS 握手失败。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 gate1525、artifact ID12和 deploy1526成功。Caddy 被 force-recreate,容器内配置包含 admin host;公网 user host/admin与/api/admin/session均 404,admin host/为 308/admin、/admin为 307/login、session API 为 401;Caddy 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 形成无限循环
- 状态:resolved(local candidate,pending 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=1、auth_users=1、bootstrap_eligible_admins=1,但active_admin_users=0、owner_assignments=0、identity_admins_missing_rbac=1,因此当前唯一 active identity admin 没有 RBAC Owner,真实授权结果为 403,而不是 cookie/host 隔离或数据库不可用。 - 修复:后台 layout 对 401 仍转
/login,403 使用 Next.js forbidden interrupt 返回明确 403 页面;503 以 307 转到 admin layout 外的独立/admin-unavailableroute,由该 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,其余未知基础设施异常统一转换为后台服务暂时不可用503,APIadminErrorResponse保留该最终状态。Owner 恢复 migration 保留在frontend/db/migrations,但在任何表查询前用to_regclass/to_regprocedure检查 identity/auth 表、RBAC 表与关键函数;identity-onlyMIGRATIONS_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-card、daily-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
- 状态:resolved(local candidate)
- 首次发现:2026-08-07
- 影响面:self-hosted staging 的 profile/consultation 读取;production 未受影响。
- 根因:本地 PostgreSQL adapter 将
DATE查询结果保留为 JavaScriptDate,下游业务合同要求无时区的YYYY-MM-DD。 - 修复:按列类型将查询结果中的
DATE归一化为本地日历字符串,timestamp 仍保持Date;同步删除 legacy rectification 后的 account stale test。 - 验证:focused 回归 37/37、完整前端 1017/1017、local quick quality gate 291 passed/1 skipped;TypeScript、lint(0 error)、production build 与
git diff --check通过。 - 修复版本:本次 staging-only 集成候选
BUG-142 | Gitea staging publish job Run 1540 post-job timeout
- 状态:resolved(local candidate)
- 首次发现:2026-08-07
- 最近更新:2026-08-07
- 影响面:Gitea
Staging Backend Quality Gatepublish job;validate、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 页面未同步能力审计精确路由集合
- 状态:resolved(local 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-126(reports 页面)、BUG-132(后台 14 页面)同类精确路由集合防复发模式第三次复发。旧防线失效的直接原因是会员页本地交付矩阵只运行了 frontend tests(
membership-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,保留精确完整集合,不放宽为子集/包含断言。 - 验证:本地目标节点
<home>/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 迁移失败
- 状态:resolved(local candidate,远端 gate 待 follow-up 更新)
- 首次发现:2026-08-07
- 最近更新:2026-08-07
- 影响面:
frontend/db/migrations/20260807020000_redeem_security.sql(已删除)、frontend/tests/redeem-orders-contract.test.ts、frontend/tests/database-redeem-security.test.ts;staging quality gate run1552frontend 1054 passed / 2 failed,Python quick 291 passed / 1 skipped,publish / deploy 均未发生。 - 用户现象:gate run
1552数据库真实失败,publish 被跳过,自动 deploy 未发生。 - 触发条件:新增依赖业务 schema(
public.redemption_codes、public.credit_transactions)的 redemption 安全迁移时,同时在frontend/db/migrations与frontend/supabase/migrations各放一份;database-local-business聚合迁移(两目录全量)通过,而只应用frontend/db/migrations的 identity-only fixture 中业务 schema 尚不存在。 - 根因:
database-self-hosted-identity与identity-auth-integration只应用frontend/db/migrations(identity 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.sql;contract 测试只审该单一 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_security;SQL 内容未作任何变更。 - 验证:本机无 Docker,无法执行 identity-only 与 aggregate DB 实测;运行
npx tsx --test tests/redeem-orders-contract.test.ts(7 passed,原 6 项 + 新增 existsSync 防复发守卫 1 项)、tsc --noEmit(clean)与git diff --check通过;目标 identity DB 实测(database-self-hosted-identity、identity-auth-integration)与 aggregate DB 测试(database-local-business、database-redeem-security)留待远端 gate 确认,本记录不提前声称远端收口。 - 防复发:
frontend/db/migrations只放 identity foundation 独立可执行迁移;依赖业务 schema(public.redemption_codes、payment_orders等)的迁移只进frontend/supabase/migrations;任何新增/删除迁移必须在本变更中同时跑 identity-only 两测试(database-self-hosted-identity、identity-auth-integration)与 aggregate DB 测试(database-local-business等)。 - 相关记录:BUG-127、BUG-143
- 复发自:无(独立根因,非 BUG-127 / BUG-143 复发)
- 修复版本:待 follow-up commit / gate
BUG-145 | staging Web health 被空模型目录错误阻断
- 状态:resolved(local 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_unavailable或database_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 时报告
degraded;database_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.ts(12 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,未新增同义 env,staging 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.ts(34 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 模型供应商保存成功但列表为空
- 状态:resolved(local candidate,staging migration/deploy 待验证)
- 首次发现:2026-08-08
- 最近更新:2026-08-08
- 现象:POST 保存成功但 GET providers 为空。
- 触发:self-hosted
admin_runtime直查五张 model 表:model_providers、model_configs、model_config_versions、model_publish_events、model_connection_test_evidence。 - 根因:表启用 RLS 且有 SELECT grant,但缺少
admin_runtimeSELECT policy;SECURITY DEFINER写成功、读被静默过滤。 - 修复:
20260808020000migration 为model_providers、model_configs、model_config_versions、model_publish_events、model_connection_test_evidence增加仅 SELECT policy。 - 验证:focused 7/7;full frontend 1071/1071;lint 0 errors、3 warnings;build 通过;
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.connect以lookup选项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 读取、密钥解密、/modelsURL、响应解析和错误翻译均不是本次失败根因。 - 修复:抽出
pinnedAddressLookup供共享 HTTPS 请求使用;options.all=true时返回单元素已验证地址数组,其他模式继续返回address, family。仍只使用已通过完整公网校验的固定地址,不重新解析、不跟随重定向,也不删除任何 SSRF/信任边界校验。 - 验证:
npx tsx --test tests/model-provider-encrypted-credentials.test.ts的真实本地 TCP 回归在修复前稳定失败为ERR_INVALID_IP_ADDRESS(4 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 分钟黑洞
- 状态:investigating(2026-09-26 安装卡住复发;既有 deadline 生效,底层卡点待 runner 证据)
- 首次发现:2026-08-09
- 最近更新:2026-09-26
- 影响面:Gitea staging quality gate 的依赖安装阶段、
manman-linuxrunner 与 Gitea/runner 控制面可用性;测试、lint、build、exact-SHA checkout/publish/deploy 合同未改变。 - 用户现象:Run 1618(SHA
ffbe505c)在 Python pip 完成后进入 digest-pinned Node 容器执行npm ci;步骤无后续 npm 输出,直到 45 分钟 job timeout,publish/deploy skipped。 - 触发条件:self-hosted runner 在受限 Node 容器中执行 frontend
npm ci,但安装命令本身没有 fail-closed deadline,npm 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.ts32/32 passed;git 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
2026-09-26 诊断补充(未修改 workflow / 未重跑部署)
- 版本:远端 staging 与隔离诊断 worktree 均为
40d7930ab4c8c44e504f3a6caaee351ed262f172。Gitea run2937(显示序号1522)、job6464在xiaoxinrunner 的Install dependencies失败;Python pip 成功,测试步骤与 publish 跳过。 - 原始错误:
frontend npm ci failed with status 137 inside bounded Node container。npm 最后一条警告时间为2026-09-26T08:28:41.857Z,失败时间为08:44:11.716Z,约 930 秒,吻合现有timeout --signal=TERM --kill-after=30s 900s。本地 GNU timeout 缩时验证:子进程忽略 TERM 后被 KILL,退出码确为 137。直接退出路径高度符合超时强杀,但仅凭该退出码不能证明或排除 runner OOM。 - 对照:此前成功 run
2934/ job6456在07:05:36Z输出added 859 packages in 40s。两次的 workflow、frontend/package.json与 lockfile 完全相同;两次都出现posthog-node@5.41.0要求 Node^20.20.0 || >=22.22.0、实际22.16.0的 EBADENGINE 警告,因此不能把该警告认定为本轮直接失败原因。 - 历史防线:900 秒安装 deadline、资源上限与 fetch timeout 均仍存在,阻止了旧 45 分钟黑洞;workflow 仅对 124 输出超时专用文案,137 落入通用失败分支。既有
staging-backend-workflows.test.ts只锁定命令文本与边界,未验证 TERM 不退出后 137 的诊断语义,也不能防止外部安装链路卡住。 - 未证实部分:npm 下载/解压/postinstall 的具体卡点及内存/PID 状态。容器使用
--rm,npm 日志位于容器 HOME/tmp且未持久化;现有 Actions 日志不足以区分镜像网络、安装脚本或资源问题,不虚构具体故障包。 - 后续最小排查:由产品负责人授权后,为安装容器持久化 npm debug 日志并保留退出/OOM 证据,在相同 SHA 重跑;不要仅调大超时或禁用安装脚本。Node engine 版本告警需另行对齐,不冒充本轮根因修复。
- 运行态:只读健康检查返回
status=ok,web/API SHA 均为此前成功版本f74825a27cca881af3373eb0c77f8f52135da378,本轮最新代码未部署。未执行 push、migration 或 workflow dispatch。
2026-09-26 本地诊断修补(未推送 / 未部署,原始卡死仍 investigating)
- 授权与范围:产品负责人要求修复上述 workflow;仅修改 backend quality gate 的 npm 安装诊断及其回归,不升级 Node、不更换 registry、不增加重试、不放宽 900 秒 deadline 或 CPU/memory/PID 边界。
- 已确认缺陷:GNU timeout 的 TERM 后强杀可能返回 137,旧分支只识别 124;
--rm和容器内临时 HOME 丢失 OOM 状态及 npm debug 日志。它们是诊断缺陷,不是已证实的 npm 卡死根因。 - 本地修补:使用独占 cidfile 和 EXIT trap 清理本次容器;先读取
State.OOMKilled,只有 124 或「137 + timeout 实际发送信号的日志」才报告超时,OOM/未知失败仍 fail closed。用PIPESTATUS[0]保留 Docker 的失败码,不让 tee 掩盖失败。mode-0700 临时目录挂载 npm debug 日志并保存安装输出,成功删除、失败保留在 runner 本地;日志未经脱敏不发布。开启 foreground scripts 与 HTTP 进度用于定位下载或 postinstall 卡点。 - 回归:直接提取 workflow 中的安装 shell,以 Docker stub 执行成功、124、强杀 137、OOM 137、未知 137、容器启动失败 125 六种场景,验证分类、退出码、日志保留与定向清理。修补前强杀场景复现通用 137 失败,修补后通过;使用主 checkout 的 Python venv 提供 PyYAML,
node --test frontend/tests/staging-backend-workflows.test.ts为 44/44 passed;bash -n与git diff --check通过。系统 Python 缺 PyYAML 的首次检查失败属于本机工具环境,不是 workflow YAML 错误。 - 真实容器验证:本机 Docker Linux/amd64 使用 CI 同一 pinned Node 镜像、同一 lockfile 和资源限制,修改后安装 shell 成功安装 859 包(约 2 分钟)。将验证副本的期限缩至 1 秒 + 1 秒、命令替换为忽略 TERM 的进程,真实 Docker 返回 137、
OOMKilled=false,workflow 报告超时并返回 124;容器由 trap 删除。此人为强杀实验只验证退出诊断,不复现 npm 卡死。 - 剩余边界:本机无法复现 xiaoxin 的原始卡死;未在真实 runner 验证修补版本,未执行 push、workflow dispatch、migration 或 deploy。BUG-149 保持 investigating,下一步是获准推送后以新 SHA 的 runner 日志判断下载、安装脚本或资源卡点,不能将本次诊断修补称为安装问题彻底解决。
BUG-150 | staging publish 的 Webpack 镜像构建耗尽共享 Gitea 资源
- 状态:resolved(local 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 路径切换到高开销 Webpack;Gitea 与 runner 共享资源时因此拖垮控制面。直接重跑旧 revision 会重复相同故障。
- 修复:恢复默认
npm run build为next 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.ymlRun1638;旧生产、Supabase、DNS 和新生产应用均未被切换。 - 用户现象:候选 SHA
9235ee66f6d889e6c27e4b85411448889dd7d37d已通过 staging gate 并在公网 staging 运行,但手工 Release Gate 在 1113 个前端子测试中出现 17 个失败。失败均从docker compose --project-name或docker compose --env-file返回unknown flag开始。 - 触发条件:完整 release profile 在
xiaoxinrunner 执行 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 Run1636已报告 Docker Compose2.40.3并成功完成质量门;本地聚焦 workflow 测试 4/4 通过。仍需新 SHA 的手工 Release Gate 成功后再视为完整关闭。 - 防复发:任何执行 Compose 集成测试的 runner 必须在昂贵依赖安装和测试前显式验证 Compose v2;Docker Engine 可用不能替代 Compose 能力证明。
- 相关记录:BUG-128、BUG-129、BUG-136、ERR-095、ERR-099、ERR-103
- 复发自:无
- 修复版本:待新 SHA 的 Release Gate 验证
BUG-152 | 订单返回套餐后再次返回会重新进入订单页
- 状态:resolved(local 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 | 个人报告页面无法下滑且命盘与打印版式失真
- 状态:resolved(local 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=936、scrollHeight=3466,滚动后scrollTop=980并可到达底部,附录展开后高度增至 5219;参考图与实现图完成同画布视觉比对。聚焦 ESLint、生产构建和git diff --check作为最终门禁重新执行。 - 防复发:聊天根滚动锁与报告阅读滚动必须分层;报告回归测试不得删除
100dvh/overflow-y:auto合同。任何命盘 occupants 改动必须验证至少两个不同宫位 transform、clipPath 与 12 项密集输入。打印回归必须覆盖长摘要、长主题、最小宽度表格和无滚动容器的 A4 输出;PDF 入口的隐藏/恢复必须同步更新明确的能力与 ready-state 合同。 - 2026-09-24 产品修订:
TASK-report-reader-actions-20260924D2 明确撤下打印按钮与专用调用,不是此 Bug 复发;A4、壳层解锁、表格分页以及 beforeprint/afterprint 展开/恢复保留,浏览器 Ctrl+P 仍可用。本地候选只更新入口合同,不把受阻完整测试、真机打印或部署记为通过。 - 2026-09-29 关联观察(investigating,未修复):星盘展示单真实 Chrome 在 390px 的既有 D1 golden 发现个别边缘 glyph 的 bbox 超出 SVG;基线/实现坐标相同,可见中心均能触发正确说明。本轮未改布局坐标/clipPath,不能认定为本轮新增,也未证实与本记录原根因相同。旧 transform/密集裁切合同不等于所有真实字体边界都不裁切;后续布局单应补真实字体 bbox 与可见区域检查。证据见
PROGRESS-chart-surface-polish-20260929.md的vedicVisibleAudit;本轮不扩改报告/校正布局算法。 - 相关记录:BUG-126
- 复发自:无
- 修复版本:本地候选(未 push / deploy)
BUG-154 | 个人报告错误绑定会话且创建请求同步阻塞
- 状态:resolved(local candidate,待 staging gate/deployment)
- 首次发现:2026-08-09
- 最近更新:2026-08-09
- 影响面:个人报告入口、
/reports信息架构、POST/GET /api/reports、报告生成等待体验;报告证据合同、用户归属与每日限额不放宽。 - 用户现象:只有进入一条已有消息的咨询 session 后才看得到“生成个人报告”,但实际报告使用的是账户出生资料而非该次对话;点击后 HTTP 请求会等待完整排盘与模型生成,用户必须停留并感知长时间阻塞,也没有集中查看历史报告的位置。
- 触发条件:聊天页用 active session、消息数和临时 workflow receipt 控制报告 CTA;
POST /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 登录后密码状态接口误报未登录
- 状态:resolved(local 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,但允许
user与admin两个已配置 surface;未知 Host 仍返回 401,Cookie 继续保持 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 报告创建被未实现的数据库过滤器直接打断
- 状态:resolved(local 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-hostedLocalPostgresQueryBuilder只实现了现有的.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-154(staging 发布前遗漏 self-hosted 适配能力核对)
- 修复版本:本次报告接口修复提交
BUG-157 | 侧栏“我的报告”按钮缺少 flex 布局导致图标与文字分离
- 状态:resolved(local candidate,待 staging gate/deployment)
- 首次发现:2026-08-10
- 最近更新:2026-08-10
- 影响面:展开状态的全局侧栏“我的报告”入口;导航行为、折叠态 tooltip 与 accessibility 语义不变。
- 用户现象:报告图标贴在按钮左上方,文字单独位于中间,整体未与上方“新对话”按钮对齐。
- 触发条件:侧栏展开并渲染
.report-nav-button的图标与文字。 - 根因:样式设置了
justify-content和gap,但没有启用 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/python33.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/python3.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 晚于证据包生成
- 状态:resolved(local candidate,待 staging gate/deployment)
- 首次发现:2026-08-10
- 最近更新:2026-08-10
- 影响面:个人报告 consultation workflow、Technique Audit、VedAstro official evidence、
modules.ashtakavarga与modules.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;相关聚焦测试与开工预检通过。 - 防复发:本地计算器存在不等于已进入报告 modules;machine 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 | 管理端商品权益枚举未本地化且登录、套餐与充值返回状态出现前端闪烁或陈旧状态
- 状态:resolved(local candidate,待真实登录态浏览器验收)
- 首次发现:2026-08-11
- 最近更新:2026-08-11
- 影响面:管理端商品与权益列表/编辑表单、主站登录跳转、首页 composer、套餐页兑换码弹窗、支付后首页点数余额
- 用户现象:商品类型、计费周期、权益类型和重置周期直接显示英文枚举;未登录进入
jyotisha.chat时登录页接管前短暂出现“暂时无法进入 Jyotisha”;首页 composer 展示过多状态提示;套餐页默认打开兑换码弹窗;充值成功返回首页后仍显示旧点数。 - 触发条件:管理端读取数据库枚举;首页并行账户/会话请求返回 401;composer 业务流程写入 notice;账户菜单携带
redeem=1进入套餐页;支付页返回已存在的首页历史记录且该记录未按普通pageshow刷新账户。 - 根因:商品与权益 UI 直接渲染数据库英文值;401 跳转后继续抛出普通错误并被首页 bootstrap 写入
accountError;composer footer 集中渲染所有 notice;套餐页用 URL 参数初始化redeemOpen,首页账户菜单默认写入该参数;余额同步事件只在套餐页当前窗口发出,而首页已卸载,首页又只在 BFCachepageshow.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 与前台排盘在模型调用前超时
- 状态:resolved(local 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_role对consultation_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 与服务器排盘工具
- 状态:resolved(staging 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|enabledruntime 开关;新路径由带 Jyotish Skill 的 Mastra Agent 调用请求级run-jyotish-consultation,工具参数只允许 question/theme,出生资料始终由服务器上下文绑定,并以请求内 Promise 保证重复调用只计算一次。服务端把fullStream清洗为 NDJSON,仅公开安全的 run/Skill/tool/activity/answer 事件;Skill 和主工具合同未完成时最多重试一次,仍失败则释放预留点数且不保存成功消息。Web 按 content-type 保留 legacytext/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 的完整 metadata;partial metadata 不得通过删除plan_version降级为 legacy,且 plan 只能收紧、不能授予精确应期权限。ConsultationPlan在创建时对对象和嵌套数组执行深冻结,确保扣点后的 workflow 不能改写边界。模型 evidence packet 不再接收任意对象后依赖 denylist 清洗,而是分别对natal_foundation、domain、timing、validation与evidence_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_time、lng、utc_offset、account_balance、entitlements、access_scopes六类恶意嵌套别名不进入模型 packet,同时保留本命、Shadbala、Ashtakavarga、Dasha、Narayana 与 evidence status。目标 ESLint、Pythonpy_compile与git 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 且并发会重复创建
- 状态:resolved(local 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):
- 新增独立 Skill
skills/jyotish-birth-time-rectification(SKILL.md ≤200 行 + 5 个 references),方法源只在 Skill,系统提示词不再复制完整方法。 - 新增 V9 领域 contracts:Case 状态机(10 状态 + resumable/terminal 谓词 + 单向终态)、Evidence kind/date precision/append-only lineage、public receipt/activity allowlist、open request/response schemas;
supersedeActive=true被 schema 与 RPC 双重禁止。 - 新增向前业务迁移
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_turns(pending/completed/failed/retryable,禁止 reasoning)、agentic_rectification_tool_receipts(只存 fingerprint/phase/tool/status)、agentic_rectification_open_ledger(requestId 幂等);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 或权限决定。 - 一次性幂等 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()。 - 新增 Case Service + 五个 API:
POST /api/rectification/cases/open(homepage resume-or-create / session 精确恢复 / new 安全冲突)、GET entry-summary(首页 CTA 真值)、GET cases/[caseId](脱敏投影,绝不返回 baseline_birth_snapshot)、POST close、POST upgrade-skill;profile 不完整不建案;错误不泄露身份、出生资料或 DB 原文。
- 新增独立 Skill
- 验证:新增
rectification-v9-contracts.test.ts(12)、rectification-v9-case-service.test.ts(14)、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 warning;git diff --check通过;database-local-business.test.ts精确 public 表清单同步新增 5 张 v9 表并断言新迁移 applied(BUG-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(编号存疑:BUG-033 无法判定指向原记录还是 BUG-236)
- 复发自:无(新 Agentic Domain 主链;旧防线缺失的直接原因见下)
- 修复版本:本地 staging 候选(未 push / deploy)
后续推进(2026-08-13 · stream agent execution + entry routing lane)
- 状态:resolved(local 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 turn(pending→completed)、没有 fullStream→allowlist NDJSON 映射、没有 Case-ref 工具、没有 Skill 真实加载证据、没有 DB 驱动的 runtime selector。
- 修复:
- 重构
agentic-rectification.ts:系统提示词压缩为约 25 行高优先边界(不再复制 gate→scan→score→diagnostics);固定加载skills/jyotish-birth-time-rectification;maxSteps按 action 有界(opening/read-only 6、evidence 8、rescore 12、accept/confirm 6)+ 硬上限 16 + 重复工具调用检测(>3 次相同调用中止)。 - 新增十个 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-case;input 只含 caseId/sourceTurnId/evidenceId/resultId/candidateId/quote/proposedKind 等最小引用,绝不接 userId/出生资料/range/events[]/分数/权限开关;每个工具走 RLS 仅 service_role 的 RPC(evidence ledger、指纹缓存、receipt、幂等);accepted与confirmed由 RPC 分列,confirm 需要 confirmation gate + 用户原话 consent quote 原文匹配;candidate 相同 evidence/range/engine 指纹复用缓存。 /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 输出 NDJSON(run.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 传播释放。- 新增向前业务迁移
20260813010000_agentic_rectification_v9_agent_api.sql:case 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_versionfeature flag(published、100%,config version=v9 legacy_mode=readonly),DB 驱动 selector,两个 runtime 不可能同时写 profile/扣费/confirm。 - 前端:
page.tsx删除hasRectificationSession与sessions.find(sessionType==='birth_time_rectification');新增openRectificationFromHomepage/openRectificationSession(sessionId)/startNewRectification,全部请求服务端 Case open API 并使用返回的 exact sessionId/caseId;首页 CTA 由 entry-summary 驱动(开始/继续上次/再次校正);侧边栏校正 Session 点击走 intent=session + 精确 sessionId(terminal 只读 + “再次校正”);shouldStartOpening只来自服务端;聊天组件改为 caseId/sessionId/readonly/shouldStartOpening 初始化并从持久化 Turns 恢复,候选卡来自 Candidate Snapshot API,活动展示来自真实 NDJSON + 持久化 receipt(可折叠“本轮做了什么”,不显示 reasoning),删除本地 timer 模拟与隐藏 sentinel。 - 新增
GET cases/[caseId]?sessionId=返回持久化 turns/evidence/latest_result/逐轮 receipt 支持刷新恢复;新增POST cases/[caseId]/candidates/accept(UI 候选卡直连,不扣费、幂等、case-scoped)。
- 重构
- 验证:新增
rectification-v9-entry-routing.test.ts(13)、rectification-v9-evidence.test.ts(11)、rectification-v9-agent.test.ts(12)、rectification-v9-stream.test.ts(9)、rectification-v9-status-security.test.ts(11);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 error;git diff --check通过。 - 防复发(补充):任何新 rectification 前端逻辑不得再根据消息数/候选存在/session 排序推断 Case 状态;
/api/rectification/agent只能消费 fullStream 并输出 allowlist NDJSON;工具输入 schema 必须 strict 且只含最小引用;首轮必须保留真实 skill 加载证据;billing request identity 只能绑定 caseId;DB 驱动 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)、候选采用重放幂等、确认重放返回字段
- 现象:
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/domain(education_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_candidates或engine_http_error,V9 无法产出任何候选。accept_agentic_rectification_candidate_for_case的幂等重放分支位于 profile 基线校验之后;第一次 accept 写入active_birth_time后,基线快照与 profile 必然分歧,重放/双击/断线重试返回candidate_profile_changed而不是idempotent=true(旧accept_agentic_rectification_candidate是先重放后校验,语义回归)。confirm_agentic_rectification_birth_time幂等分支在v_result载入前引用v_result.id,confirmed 重放响应的result_id恒为 null。
- 触发条件:任何进入 compare-candidates 的真实运行;accept 成功后同一候选再次 accept;confirmed 后同一 confirm 重放。
- 根因:
- 新增
engine-client.ts从未与真实引擎做契约测试(既有测试全部 mockrunV9CandidateScore,未覆盖真实响应形状);V9 evidence 领域模型与引擎粗粒度评分词汇之间缺少 kind/domain 翻译层。 - accept/confirm RPC 的重放语义被基线保护逻辑错误地前置/后置,未对齐旧实现的先重放后校验顺序。
- 新增
- 修复:
engine-client.ts对齐真实引擎契约:新增toEngineScoreableEventkind/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_pressure;family/other 留在账本但不再进引擎);readCandidates由time+score按分数降序推导 rank、同分 tied_minute_count、相对支持度归一化;representative_time=top1;selection_allowed=有候选;confirmation_allowed只来自引擎can_confirm_exact_minute(真实引擎当前为 false,confirm 门诚实关闭);margin_percent取自 diagnostics;confidence 由 margin+retention 推导;无 scorable 事件/无候选时 fail-closed(no_scorable_evidence/engine_no_candidates)。20260813010000_agentic_rectification_v9_agent_api.sql:accept 重放分支移到 profile 基线校验之前,重放分支校验 profile 与已采用时间一致(对齐旧语义);confirm 幂等分支补select id into v_result.id,result_id不再为 null。- 移除
rectification-v9-tools.tscompare-candidates 中双分支同 throw 的死代码。
- 验证(真实执行,非 mock):
- 本机安装 PostgreSQL 17(brew),按
deploy/postgres/001-bootstrap-roles.sh建角色,从空库全量应用 95 个迁移 ×2(second run 95 already applied,--checkexit 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 backfill(confirmed/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 test1256(1232 pass / 18 Docker ENOENT 环境失败,与基线6d7a9a97同因,worktree 实证);tsc --noEmit仅剩 6 个既有未触碰测试文件错误;ESLint 0 error;next build通过;git diff --check通过。
- 本机安装 PostgreSQL 17(brew),按
- 防复发:引擎适配器必须有真实响应形状的契约测试;新增任何映射层必须对照
scripts/rectification/contracts.py:SCOREABLE_EVENT_KINDS;RPC 重放/幂等语义以“先重放后校验、重放校验已落库状态”为唯一实现顺序;迁移修改必须在真实 PostgreSQL 上从空库全量应用并重跑。 - 相关记录:BUG-163、BUG-112
- 修复版本:本地 staging 候选(未 push / deploy)
BUG-165 | 合并咨询 Agentic runtime 后生时校正执行步骤复用共享 activity 字段导致构建失败
- 状态:resolved(local candidate,待 staging gate)
- 首次发现:2026-08-11
- 最近更新:2026-08-11
- 影响面:
RectificationAgenticChat、共享ChatMessageView、Next.js production type-check - 用户现象:生时校正与最新 staging 的咨询 Agentic runtime 单独测试均通过,但合并后
next build在rectification-agentic-chat.tsx报string[]不能赋给结构化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-stream、rectification-agentic-entry、chat-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-11(Gitea staging gate run 1732)
- 最近更新:2026-08-11
- 影响面:
frontend/tests/rectification-v9-database.test.ts、frontend/src/lib/db/local-postgres-client-core.ts的按 URL 连接池生命周期 - 用户现象:staging gate 的 Docker DB 套件恰好三个 V9 测试失败。
- 触发条件:在 CI(Docker fixture)中运行
rectification-v9-database.test.ts。 - 根因(脱敏):
- 两个测试创建了
createLocalPostgresDataClient的 service 客户端后没有关闭其全局按 URL 缓存的连接池;fixture.stop()先销毁 PostgreSQL,池的异步终止随后触发 57P01(terminating connection)类报错。不能全局关闭所有池(node:test 的 DB 用例可能并发),必须按各自 service URL 关闭,且任何情况下fixture.stop()都要执行。 - 最后一个 “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 进入候选查询。
- 两个测试创建了
- 修复:
- 审查并保留新增的
closeLocalPostgresDataPool(connectionString):先按 key 从全局缓存删除再pool.end(),未知 key 与重复关闭均为安全 no-op,删除后再注册同名 key 可创建新池,与全局closeLocalPostgresDataPools并发/先后调用无冲突(end 幂等)。 rectification-v9-database.test.ts中所有创建 service 客户端的测试(open / profile gating / evidence / backfill / agent api,共 5 个)在finally中先await closeLocalPostgresDataPool(service_url)再fixture.stop(),嵌套 try/finally 保证池关闭失败时 fixture 仍会停止;纯迁移测试无需关闭。- 最后一个测试先经合法的
identity_runtime播种identity.users,再由 postgres admin 更新触发器自动创建的 profile 并播种其它public.*行;同时改用模板中的既有 fingerprint,并按 RPC 真实合同断言 consent grounding 失败。保留生产最小权限,不改任何 grant、不改迁移。 - 新增非 Docker 单测
tests/local-postgres-pool-close.test.ts(4 项):未知 key no-op、按 key 关闭互不影响、关闭后同 key 可重建、全局关闭后再按 key 关闭 no-op。
- 审查并保留新增的
- 验证:
local-postgres-pool-close.test.ts4/4 通过;本地 Docker PostgreSQL fixture 的rectification-v9-database.test.ts6/6 通过;目标 ESLint 0 error;git diff --check通过。Gitea gate 仍需对最终提交复跑。 - 防复发:任何测试创建本地数据客户端必须在
fixture.stop()之前按自身 service URL 关闭连接池;测试不得用identity_runtime向public.*播种 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 - 用户现象:已登录且具备账号管理权限的管理员执行账号重置时,仍需额外发送并输入邮箱验证码,增加不必要的操作步骤。
- 触发条件:在用户列表打开重置弹窗并提交账号重置。
- 根因:账号重置复用了面向角色变更、账务调整等操作的
requireHighRiskAdminMutation与reauthPermission,把一次性邮箱复核错误扩展到了已有登录、权限、原因、可信来源及数据库审计保护的重置路径。 - 修复:该路由改用既有
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已是published、enabled=true、rollout_percentage=100。 - 触发条件:self-hosted Web 通过
ADMIN_DATABASE_URL以admin_runtime查询启用了 RLS 的public.feature_flags。 - 根因:
20260806050000_operations_feature_flags.sql给admin_runtime授予了表级 SELECT,但启用 RLS 后没有创建对应 SELECT policy。PostgreSQL 因此不报权限错误而是返回零行;loadRuntimeFeatureFlags将缺失记录安全降级为 disabled,路由遂返回“生时校正服务暂未开放”。 - 修复:新增向前迁移
20260811030000_feature_flags_admin_runtime_read_policy.sql,保留最小 SELECT grant,并为admin_runtime创建feature_flags_admin_readRLS SELECT policy;不放宽匿名、普通用户或其它运行时角色权限。 - 验证:Docker PostgreSQL 回归先在修复前稳定得到空结果,新增迁移后要求
admin_runtime能读取true:100:published;staging 还需验证迁移账本、角色可见性及真实 Agent 接口不再返回 runtime disabled。 - 防复发:任何对启用 RLS 的表新增 runtime grant 时,必须同时测试对应运行时角色的真实可见行,而不能只断言
has_table_privilege=true;feature 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_rectificationSession 后立即发送 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_rectificationSession 的路径都必须在同一事务内得到可解析的持久化模型与版本;前端显示的默认模型不能替代数据库绑定。 - 相关记录: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-ndjson,Skill 与 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/agent的opening与read_only免费 Turn,以及回答完成后的 Turn 最终状态与 assistant message 持久化。 - 用户现象:Agent 已加载 Skill 与正确 Case,并输出完整回答,但 NDJSON 最终事件仍为
run.failed;数据库 Turn 为retryable且没有持久化 assistant message。 - 触发条件:免费
opening或read_onlyTurn 正常生成回答并进入成功收尾。 - 根因:路由的
billing.reserve()对免费 Turn 不创建usage_reservations,但billing.complete()仍调用complete_usage;数据库因找不到 reservation 返回request_missing,Agent runner 将已成功回答降级为usage_settlement_failed。 - 修复:免费 Turn 在 settlement adapter 中直接成功返回;只有
messageTurn 才执行 reservation 与 settlement,保留原有付费消息的计费、幂等与失败保护。 - 验证:staging 数据库确认目标 Case 没有 usage reservation,直接调用同一结算函数稳定返回
request_missing;新增合同回归要求免费 Turn 同时绕过 reservation 与 settlement。部署后需以真实 opening 确认最终run.completed、Turncompleted且 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-evidence与rectification-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_message与assistant_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 tree;staging 需继续验证 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;用户无法保留旧记录并另开一段校正。
- 触发条件:存在
draft、collecting_evidence、candidate_ready、candidate_accepted、needs_rebaseline或pausedCase 后点击首页生时校正卡片。 - 根因:V9 初始设计把首页
homepageintent 定义为 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 + Session,session仍按精确 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 Database;production 发布门禁未修改。
- 用户现象:staging 作为测试分支需要领先或偏离 main 时,旧部署工作流仍要求 main 与 staging 同一 SHA,且
workflow_run从默认 main 加载控制器,导致 staging-only 变更无法按自身已测试工作流发布。 - 触发条件:
staging推送了尚未进入main的测试提交并完成 quality gate。 - 根因:staging 发布把 production 的 reviewed-main 收敛约束复用到了测试环境,同时依赖默认分支的
workflow_runcontroller;即使删除 SHA 相等检查,旧 main controller 仍可能继续执行旧门禁。 - 修复:将 staging gate 重命名为
Independent Staging Quality Gate,使默认 main 上遗留的workflow_run监听器不再匹配并抢占staging-mutation并发组;staging push gate 在发布同一 exact-SHA 的不可变镜像与 allowlisted controller bundle 后,显式从refs/heads/stagingdispatchDeploy 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 SHA;staging 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-suggestions;Prompt/Skill 只限制“最多一个问题”,未明确完整回复可以零问题、内部执行不得进入正文、用户没有更多事件时不得继续轮换领域,也未划清候选卡与 Agent 正文的内容所有权。 - 修复:新增客户端候选快照解析模块,按 Case API camelCase 外层字段读取结果并规范化候选内部字段;候选卡独占时间、排名、相对支持度、采用动作和选中状态,近乎并列且未开放确认门时不标记“当前推荐”;生时校正改用只提取正文与标题的解析入口,移除建议状态、持久化和
composer-suggestions;Prompt、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/confirm;
rectification-read-case从服务端 Dossier 派生有界的evidence_context与conversation_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_methods的tool.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-hero与product-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-display、font-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_stream、stream_aborted或stream_unfinished失败并自动重试;用户对 active question 作简短承接回复;会话超过近期窗口;一条消息包含多件明确经历;或注册表 active Skill 版本在 Case 创建后升级。 - 根因:旧运行器把
skill.bound、case.loaded主要写在 Prompt 约定中,没有 durable attempt ownership 和成功 attempt 投影;ConversationFocus 由 Assistant 文本正则倒推,没有服务器持久化的 target evidence/domain/kind;Case dossier 缺少 durable conversation summary 和批量 evidence 幂等合同;运行时按全局 active Skill 解析,而不是严格使用 Case 已绑定的 immutable package identity。 - 修复:新增 additive V10 migration,建立
agentic_rectification_run_attempts、agentic_rectification_conversation_focuses与agentic_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.settled、run.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 skipped(Docker 场景由独立真实数据库测试覆盖);真实 Docker PostgreSQL 从空库应用全部 migration 并完成业务测试 1/1,覆盖 request replay/mismatch、legacy RPC 撤权、terminal attempt 取回与新 attempt 拒绝、completed Turn 单调性及 superseded attempt;目标 ESLint 0 error/0 warning;
git 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;失败/重试不得重复扣费或重复 evidence;focus 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_exit、relationship_commitment/relationship_separation等生命周期事件进行候选评分;展示候选卡或 Activity;采用代表候选;或调用旧的 time-based candidate RPC。 - 根因:Python 事件合同只接受少量粗粒度 kind,TypeScript adapter 承担了有损映射、排序/tie/置信度/permission 和 technique 推断;结果表与 RPC 只保存调用方提供的布尔值和候选 JSON,没有版本化 decision receipt、真实 execution ledger 与服务器 candidate UUID 边界;历史 accept RPC 仍可把 accepted 与 confirmed 混合。
- 修复:引入 Event Contract v2 与 Decision Policy v2,由 Python 返回版本化
candidate_decisions、decision_receipt和真实execution_ledger,日期精度进入服务器评分;Web 只做严格安全投影,不再自行推断 rank、tie、margin confidence、selection gate 或已执行技法。forward-only migration 保存政策与执行凭证,以服务器生成的 candidate UUID 绑定 result,并将 representative candidate 重写为持久化 UUID;accepted 与 confirmed 使用独立 RPC 和状态门禁,精确确认必须引用已接受候选、有效 consent/source turn,旧 time-based RPC 对service_role撤权。Case/Dossier 恢复路径同时投影完整 v2 receipt、ledger、selected candidate 与 display gate。 - 验证:Python 服务回归 36 passed;TypeScript 核心合同 49 passed、0 failed;migration 与 PR-4 真实 PostgreSQL 场景 75 passed、0 failed;V9/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 warning,Python 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 出生时间生成报告。 - 根因:报告路由把所有完整报告折叠到单次
generalworkflow,旧 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.jsafter(),改为由数据库原子创建的持久化 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_role;writer 与单次 repair retry 均接收 worker AbortSignal。accepted 状态改为accepted_directional_only,Bundle 不生成candidateRange;缺失 D2/D11 等证据只能生成blocked + executed=falsereceipt。Web renderer 继续只消费结构化文档和真实 chart data,并使用浏览器原生打印,不接受模型生成 HTML/CSS/SVG。 - 验证:个人报告合同、planner/writer、API、entry、job state/service/worker、renderer/export、migration 与 Skill registry 完整聚焦回归 247 passed、0 failed;Python 报告合同与 Skill package 41 passed、0 failed;目标 ESLint 0 error/0 warning,
git 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/consensus;Agent 不得接收 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 closed,annual普通咨询不等于annual_report。UI 保留 answer delta 期间的真实 Activity 与失败 receipt,只从服务端 event/receipt 渲染执行状态,并严格区分“已采用(未确认)”和“已确认”。另建独立 product registry 与 feature flags;个人报告、合盘、生时校正写入/计算 API 在鉴权后执行 fail-closed 产品 gate,报告中心同时保留既有环境变量开关。 - 验证:TypeScript focused 矩阵 178 passed、0 failed;Python consultation/thematic/API focused 70 passed、0 failed,
py_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或 verified;accepted 不得升级为 confirmed,Activity 不得从 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绕过,accepted与confirmedminute 显式分离且后者只在明确 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 availability,
requiresEvidence不能关闭事实/时间证据规则,accepted 不得升级为 confirmed;Agent 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
/sflag 且假设 PATH 中存在可导入 PyYAML 的python。因此不能安全提交、推送或发布 staging。 - 触发条件:在 PR-7 十域 registry、PR-4 V2 candidate decision contract、生产迁移列模型与独立 staging workflow 合同合并后运行全量
tsx --test tests/*.test.ts、tsc --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 gate;billing 测试分别锁定 reserve/complete/release 的免费 turn 短路与付费调用;数据库测试保留 legacy RPC 撤权断言,并恢复persist_agentic_rectification_candidate_v2到accept_agentic_rectification_candidate_for_case_v2的真实纵向链路,覆盖服务端 candidate UUID、首次接受、幂等重放、切换候选、profile 落库、reported time 保留及基线变化后的 expired 拒绝;生产迁移 fixture 补齐 identity/data type 元数据;YAML 检查改用PYTHON、VIRTUAL_ENV、仓库.venv与 PATH fallback,quality gate 通过os.environ.setdefault("PYTHON", sys.executable)向前端测试传递解释器,并移除 worktree 层级假设。 - 验证:聚焦非数据库测试 70 passed、0 failed;本地 PostgreSQL 全迁移与业务链路 1 passed、0 failed;全量前端 1532 passed、0 failed;
tsc --noEmit通过;ESLint 0 errors、4 个既有 warnings;Next 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 cases(3/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 漏提交上游 .agents,clean 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.sh和run-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,使创建和幂等分支统一返回完整
focusrow 与idempotent;set-focus 最终失败时 turn fail closed 为 retryable、不得发送完成回答或结算;turn receipt 按选定 attempt 聚合每个工具最新 terminal 状态并公开安全的tool_activities,客户端使用同一 reducer 恢复 failed tool 与 methods。 - 验证:聚焦回归覆盖 RPC 返回合同、失败后不得 completed、同 attempt 重试成功、receipt attempt 隔离、failed tool 持久化及刷新前后 Activity 一致性;与初始化、Case 和 migration 回归合并运行 203 passed、0 failed。远端 migration 尚未应用,仍需 staging 登录态验证最终 NDJSON、持久化 turn/receipt 与免费 opening 计费不变量。
- 防复发:数据库 RPC 返回结构必须与 TypeScript parser 共用合同测试;提出用户可见主问题前必须成功持久化对应 focus;完成凭证不得只记录成功工具或从 Agent 文本反推执行状态。
- 相关记录:BUG-176、BUG-181、BUG-185、BUG-186
- 修复版本:本次功能分支提交(精确 SHA 以提交、远程分支与 staging 发布结果为准)
BUG-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_incompletefail 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且不自动 confirmed;period/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_source为period_only或unknown时,以homepage/newintent 创建 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:00–03:59;unknown使用00:00–23:59。缺少出生日期、地点、时区,或选择period_only却没有合法时段等真正不完整组合,仍在调用创建 RPC 前 fail closed。 - 验证:回归测试先证明旧实现对 period/unknown 稳定抛出
profile_incomplete,修复后锁定 morning08:00–11:59、late-night23:00–03:59、unknown00:00–23: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_offset为null。 - 根因:资料表单和账户保存合同允许用 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:00–22: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_source为period_only或其他没有具体分钟的状态,首页以general_no_birth_time发起daily_starlanguage请求。 - 根因:BUG-200 只修正了首页任务式文案,没有闭环服务端能力合同:前端无分钟请求主动丢弃
daily_starlanguageentrypoint;General Agent 又只允许百科知识,并把所有 forecast 一律拒绝。初始化保存的出生时段不能安全替代具体分钟,因此也不能直接走个人命盘日运链路。 - 修复:无分钟请求保留受限的
daily_starlanguageentrypoint,并在服务端确认最终咨询模式后将其展开为“公共日历趋势”问题。新增服务器公共 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。 - 用户现象:
main与staging已同步且 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 只保留CHOWNcapability,却按“父目录在前、lock 子文件在后”的顺序处理;Run 1873 已先把.state改成deploy:deploy 0700,再因没有DAC_OVERRIDE无法遍历到仍未修复的 lock,留下半修复状态。Run 1877 虽改为 child-first,但 helper 启动时父目录已经是不可遍历的0700 deploy,所以仍无法通过原宿主路径到达 lock。四次失败都发生在pg_dump前,没有生成可用恢复证明,也没有执行 schema migration。 - 修复:恢复脚本先对
.state、backups和既有mutation.lock做类型与非符号链接校验,取得当前运行 PostgreSQL 容器的不可变本地 image ID,再复用既有sudo -n docker边界启动一次性所有权修复容器:--pull never、无网络、只读根文件系统、no-new-privileges、删除全部 capability 后只保留CHOWN。除绑定.state与backups外,把已经验证为普通非 symlink 文件的 lock inode 直接绑定到容器/mutation.lock,先通过该直接 mount 恢复 lock,再恢复两个目录 mount point 到当前deployUID/GID。这样无需遍历半修复的 mode-0700父目录,也不需要增加DAC_OVERRIDE;不递归改动历史备份、不删除或替换 lock inode,随后仍用同一个flock -nfail-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 couldn’t load,只能点击 Reload 或手工刷新;同一登录会话刷新后报告列表恢复正常。 - 根因:生产 Next.js Web 构建未设置
deploymentId。旧标签页仍运行上一发布的客户端 runtime,在新镜像切换后进行客户端导航时请求了当前发布无法匹配的静态 chunk,触发ChunkLoadError并落入 Next.js 通用错误页。报告 API、登录态和报告数据本身没有失败。 - 修复:Web Docker build stage 接收并设置
NEXT_DEPLOYMENT_ID;Gitea 主发布链与 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-id,JS/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.md、deploy/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)参数校验前就创建并缓存calculationPromise。首次非法调用产生的 rejected Promise 被永久保留;后续合法调用命中缓存后直接复用旧拒绝,导致 workflow 调用数保持为零,最终由既有合同门禁判定runtime_contract_incomplete。 - 修复:在读取或写入计算缓存前同步完成域计划校验,非法参数不启动、不计数也不污染缓存;实际计算 Promise 拒绝时仅清除仍指向该 Promise 的缓存,使后续调用可以重新执行。成功 Promise 继续保留,合法并发调用仍共享同一次服务器计算。未移除运行合同门禁,未伪造 workflow receipt 或成功状态。
- 验证:新增 invalid
domains + theme→ validtheme: 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、ReasonActionModal、ConfirmActionModal、相关Popconfirm/Modal.confirm与 pending-confirm 状态。真实的数据录入 Modal 继续保留,但点击表单的“生成 / 保存 / 发布”等主按钮即直接执行;无额外参数的撤销、重试与开关动作由原按钮直接执行。客户端不再提交手工reason,服务端按动作注入固定审计标识并继续传给数据库 RPC 的非空审计字段。 - 安全边界:保留 request ID、数据库 actor 身份校验、领域 RPC、细粒度权限、可信 Origin、append-only 审计和最后一位 Owner 保护;保留账户级 Better Auth TOTP MFA 与一次性恢复码;普通用户登录、注册和找回密码的邮箱 OTP 不受影响。
- 验证:全局源码扫描确认管理业务中不存在
ConfirmActionModal、Popconfirm、Modal.confirm、reason-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 身份校验。 - 验证:本地使用项目实际
pgserializer 对比确认显式序列化后保持合法 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
pg的jsonb参数必须显式 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_boundfail closed。attempt 内的活动与文本在成功前统一缓冲,因此该合同错误在公开流中折叠成只有run.started → run.failed。 - 根因:Skill 的真实性与版本已经由服务器加载和校验,但运行合同仍把“是否完成绑定”交给模型是否主动选择
skill工具,形成服务器事实与模型行为之间的不一致;首步 Case 读取同样没有由服务器强制。该缺陷可确定性复现用户现象,但在缺少 staging 运行日志时不据此断言某个具体 provider 一定返回了直接文本或特定工具序列。 - 修复:要求
agent.getSkill()返回非空指令,并将其作为本 attempt 的服务器 system bootstrap 注入;在 provider 执行前持久化唯一 Skill receipt 和skill.boundphase,并将 Skill 标记为已绑定。通过 MastraprepareStep把 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--noEmit与git diff --check通过。部署同构 Dockerbuildtarget 成功,镜像内@mastra/core为1.50.1。 - 防复发:服务器已经确定的 Skill 身份、指令和首个事实读取步骤不得再依赖模型自动选工具;所有 provider 调用前必须完成可审计的 Skill 绑定,首步工具面保持最小化,并继续以最终
run.completed、持久化 Turn 和计费不变量作为部署后验收标准。 - 相关记录:BUG-177、BUG-198、BUG-206
- 修复版本:本次 staging 发布候选(精确 SHA 以远端 staging 与健康检查验收为准)
BUG-247 | 用户选择准确出生时间后仍停留 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_time与birth_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-onlyfrontend/db/migrations。 - 验证:账户回归覆盖新建、既有 reported 原样重存、已 accepted 分钟修改、confirmed/legacy confirmed 保护及所有非严格准确来源,13/13 通过;账户、出生时间 intake、报告 API 与报告入口聚焦测试 82/82 通过。PostgreSQL 全业务迁移测试实际执行新增 migration,验证严格 0/0 记录得到
05:00:05:00:accepted,10 分钟误差记录保持active null + reported,1/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_blocked;workflow 返回后applyBirthTimeModeToWorkflowContext()又无条件把can_answer_precise_timing改为false。最终输出 guard 根据被强制阻断的 receipt 删除年月日,而不是根据计算证据是否完整决定。 - 修复:有具体分钟的
verified_chart与unverified_birth_time统一使用server_evidence_required,精确应期权限由服务器计算证据决定;未校正模式继续保留birth_time_confidence=unverified_reported_time和candidate_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.domains和agentExecutionReceipt.workflow.domains,前端在下一轮生成前 PATCH 完整消息数组。 - 根因:公开 Agent 事件使用的 canonical
workflowReceiptSchema已允许可选domains,聊天写入合同却重复维护了一套.strict()旧 schema,导致messages[].workflowReceipt.domains被 Zod 判定为unrecognized_keys;嵌套 execution receipt 使用新版 schema,因此同一业务对象在两个位置具有不同合法字段。 - 修复:聊天写入合同直接复用
consultation-agent-events.ts导出的workflowReceiptSchema和WorkflowReceipt类型,删除重复字段定义;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,其直接子节点却是嵌套的 ReactTypography.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_products与product_entitlementsSELECT,并分别创建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.completed(55ms)。 - 根因:合同门禁
contractReady()要求consultationToolCallCount === 1,而该计数在createConsultationTools()中对每次未命中缓存的执行递增,失败尝试同样计入。BUG-205 的修复让被拒绝的计算缓存可以释放、从而允许重试,但门禁仍按总尝试次数判定,导致只要发生一次瞬时失败,计数就永久大于 1,之后无论计算是否成功都不可能满足合同。服务端补跑无法降低计数,因此纯属浪费,最终以runtime_contract_incomplete结束并丢弃已经算出的结果。 - 修复:新增
consultationToolSuccessCount,仅在工作流真正成功时递增;门禁改为判定成功次数为 1。失败尝试继续记入consultationToolCallCount供观测使用,但不再影响合同。未放宽单次计算边界:请求级缓存保留成功 Promise,后续调用一律复用,因此每个请求仍最多执行一次计费计算;两次真实成功计算依然判定为违约。 - 验证:新增修复前失败的回归,复现“两次失败 + 一次成功 + 补跑命中缓存”序列并断言
run.completed且回答正常输出;新增“两次成功仍然违约”边界回归以锁定单次计算约束;工具层补充断言失败重试后consultationToolCallCount=2而consultationToolSuccessCount=1。回退门禁到旧实现可确认新回归失败。frontend/tests/consultation-agentic-runtime.test.ts17/17 通过。 - 防复发:运行合同门禁只能依据成功语义的计数,不得用包含失败尝试的总调用次数;任何允许重试的缓存改动,必须同步检查下游门禁是否仍按尝试次数判定。合同类回归必须覆盖“失败后恢复”与“重复成功”两个方向。
- 相关记录:BUG-205、BUG-186、BUG-189
- 修复版本:本地未提交候选
待跟进
前两次 calculation_failed 的服务端原因尚未定位(工作流超时为 90s,两次失败均在 20s 内,可排除超时)。本条修复只保证瞬时失败可恢复,不替代对失败本身的排查。
排查所需的可观测性已随本批补齐:calculation_failed 是 safeToolError() 的兜底码,除中止与超时外的一切失败都会被压成它,而上游真实错误文本属于 provider payload,按 agent-observability.ts 的封闭契约不得进入日志。因此改为按封闭机器码分类:runConsultationWorkflow 抛出带 code 的 ConsultationWorkflowError,按 HTTP 状态区分 workflow_rate_limited(429)、workflow_bad_request(400)、workflow_queue_full(503)、workflow_server_error(5xx) 等,并单独标识 workflow_contract_invalid(HTTP 通过但响应未过 consultationWorkflowResponseSchema,此种情况 Python 侧日志显示成功,仅凭访问日志无法发现)。该码记入运行步骤的 failureCode,经 agentObservabilityToolCallSchema 的新增受控可选字段进入观测日志。同时把 request_id 透传给 Python API,用于与其访问日志交叉对齐;此前两侧无任何关联标识,只能靠时间戳猜测。公开回执改由 publicConsultationRuntimeSteps() 按白名单构建,内部 failureCode 不出现在对外契约中——executionStepSchema 是 strict,若直接透出会让成功运行在解析回执时报错。
BUG-215 | CSS 契约测试取错规则块,假阳性阻断 staging 发布
- 状态:resolved(本地修复,未提交、未发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
backend-quality-gate的前端契约测试;间接影响该 gate 上所有等待发布的改动。 - 用户现象:quality gate 以
makes SidebarContent the only sidebar scroll owner失败,报[data-sidebar="content"]缺少min-height: 0,但该声明实际存在且未被改动。staging 因此停留在7050f7ee,bced1b9d与其后两批后端修复均无法部署。 - 根因:
cssBlock()以globalStyles.indexOf(selector + " {")取首个匹配规则。bced1b9d在媒体查询中新增了一条同名规则[data-sidebar="content"] { -webkit-overflow-scrolling: touch; },位置早于第 333 行的基础规则,helper 因而返回媒体查询块。该 helper 在sidebar-contract与membership-page两个文件各有一份副本,且自身没有任何测试。修复过程中又暴露两个同源缺陷:CSS 注释写在规则上方时会被计入捕获的选择器文本(.membership-page因此找不到);调用方会把整个选择器组当作 key 传入(".membership-plan-card, .membership-credit-card"),旧实现仅靠字面量子串匹配碰巧生效。 - 修复:抽出共享
tests/css-contract-test-support.ts的cssDeclarations(),先剥离注释再逐条规则解析,按逗号拆分并归一化空白后做精确或后代组合匹配,支持以整组作为查询,并返回所有命中规则声明的并集而非首个。并集使assert.match语义为“任一规则声明即可”、assert.doesNotMatch为“任何规则都不得声明”,后者比原实现更严格,也更贴近“唯一滚动容器”这类断言的本意。未放宽任何既有断言,未改动globals.css。 - 验证:新增 9 个 helper 回归,覆盖媒体查询先于基础规则、注释引入的规则、跨行选择器、选择器组查询、后代组合、以及选择器缺失时必须响亮报错;
sidebar-contract与membership-page合计 78/78 通过;全量非数据库套件 1577/1578,唯一失败为需要真实 Postgres 的 v9 迁移测试。 - 防复发:被多处断言复用的测试辅助函数必须有自己的回归,不得以副本形式散落在各测试文件。以字符串匹配近似 CSS 语义时,必须按规则解析并覆盖同名选择器的全部声明;
indexOf式首个匹配不可用于可能重复出现的选择器。 - 相关记录:BUG-214
- 修复版本:本地未提交候选
BUG-216 | 咨询中断、取消、归档等 44 条提示文案计算后从未显示
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/主对话页的断线恢复、取消回答、模型下线切换、会话重命名与删除失败等全部提示通路。 - 用户现象:网络中断、刷新后恢复、点击停止、删除会话失败时,界面只有一个转圈或静默无反应,用户无法知道回答仍在后台生成、是否已取消、失败原因是什么。
- 根因:
frontend/src/app/page.tsx将提示状态声明为const [, setComposerNotice] = useState(""),解构时丢弃了状态值,JSX 中也没有任何渲染点;44 处setComposerNotice(...)调用的文案全部写入一个永不读取的 state。恢复轮询逻辑本身正确,缺的只是出口。 - 修复:新增
frontend/src/lib/chat-notice.ts,把提示按语义分派到既有 sonnertoast.success/toast.error/toast;根布局早已挂载<Toaster />,无需改动。以固定id复用同一条 toast,避免 1750ms 恢复轮询把同一句话堆成几十条;空字符串走 dismiss 而不弹空 toast。44 处调用点与全部中文文案逐字未改。 - 验证:新增
frontend/tests/chat-notice-and-scroll-contract.test.ts锁定状态不再被丢弃、提示进入 toast、空串不弹窗、轮询防刷屏与分级语义;tsc --noEmit、eslint清洁;与改动文件相关的 43 个测试文件共 386 条断言全绿。 - 防复发:禁止以
const [, setX]形式声明用户可见文案状态;任何面向用户的提示必须有可断言的渲染出口,合同测试需覆盖“文案确实可达 UI”而不仅是“文案存在”。 - 相关记录:BUG-211
- 修复版本:本地未提交候选
BUG-217 | 个人报告页轮询 120 秒后静默停止,界面仍显示“生成完成后页面会自动显示”
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/reports/[reportId]报告详情页的生成等待态。 - 用户现象:报告生成超过两分钟后,页面永远停在“报告正在生成中,请稍候…”的转圈上,即使报告已经在后台完成也不会刷新;页面同时声称“生成完成后页面会自动显示”,与实际行为矛盾。
- 根因:
personal-report-page.tsx以MAX_POLLS = 40×POLL_INTERVAL_MS = 3000限制轮询次数,达到上限后 effect 直接return停止轮询,但state.phase仍保持"generating",UI 继续渲染等待分支,没有任何超时态或失败态承接。 - 修复:在
ReportLoadState中新增客户端专用的timed-out相位(classifyReportEnvelope不产出该值,服务端语义未变)。轮询改为 8 分钟墙钟预算配阶梯退避(首分钟 3s,之后 6s/10s/15s,总请求数由 160 降到约 48)。预算耗尽后进入timed-out屏:说明生成仍在后台继续,提供“继续等待”重置时钟并立即重取,保留“返回报告中心”。等待中与超时后均显示“已等待 X 分 Y 秒”。 - 验证:新增
frontend/tests/report-polling-contract.test.ts,除文本合同外还对导出的pollIntervalForElapsed/formatWaitedDuration做真实单元断言(退避边界、单调性、请求数上限);既有personal-report-*测试全部通过。 - 防复发:任何有次数或时间上限的轮询,达到上限时必须切换到显式终态并给出用户可执行的下一步;等待文案承诺“自动刷新”时,必须由测试保证该承诺在整个等待窗口内成立。
- 相关记录:BUG-217 无前序同类记录
- 修复版本:本地未提交候选
BUG-218 | 流式回答期间强制滚到底部,用户无法向上翻阅历史
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/主对话页的会话滚动容器。 - 用户现象:长回答生成过程中向上滚动查看此前内容,会被立即拽回底部,无法停留;也没有任何“回到最新”的入口。
- 根因:
page.tsx的自动滚动 effect 依赖数组包含activeStreamingText,每个流式 token 都会触发一次无条件scrollTo(scrollHeight),未判断用户当前是否已在底部附近。 - 修复:新增
frontend/src/hooks/use-conversation-scroll-anchor.ts,以 rAF 合并的 passive 滚动监听维护锚定状态:用户向上越过约 96px 阈值即解除锚定,回到阈值内自动恢复;仅在锚定时执行自动滚动。切换会话与用户自己发送消息仍强制滚到底部。解除锚定时显示“跳到最新”按钮(真实<button>、aria-label、可见焦点环、44×44 触控区),点击后滚到底并恢复锚定。沿用既有prefers-reduced-motion处理。 - 验证:同 BUG-216 的合同测试覆盖锚定守卫、有意跳转与无障碍跳转控件;
tsc、eslint清洁。浏览器内的视觉位置未经人工目视确认。 - 防复发:聊天类自动滚动必须做底部锚定判断,禁止把流式文本直接作为无条件滚动的依赖项。
- 相关记录:BUG-218 无前序同类记录
- 修复版本:本地未提交候选
BUG-219 | 应用根级缺少错误与 404 边界,渲染崩溃时落到 Next.js 默认页
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/、/membership、/login、/admin/*等除/reports/[reportId]外的全部路由,以及所有未知 URL。 - 用户现象:主对话页等发生渲染异常时,用户看到的是 Next.js 默认错误界面,无中文说明、无重试入口、无返回路径;访问不存在的地址得到默认 404,与产品界面割裂。
- 根因:
src/app/下从未创建error.tsx、global-error.tsx、not-found.tsx,全应用唯一的错误边界位于src/app/reports/[reportId]/。 - 修复:新增三个根级边界。
error.tsx为客户端边界,提供“重试”调用reset()与返回入口,并以克制方式展示error.digest供用户报障引用,不暴露堆栈。global-error.tsx自带<html lang="zh-CN">与<body>,零依赖并使用内联样式配var(--color-*, 字面回退),因为它替换根布局时globals.css不可达。not-found.tsx为服务端组件,文案兼容login/page.tsx在未识别域名下主动notFound()的既有行为。 - 验证:新增
frontend/tests/root-error-boundaries-contract.test.ts6 条断言,覆盖文件存在、客户端指令、global-error自带文档骨架、role="alert"、标题层级与中文文案;tsc、eslint清洁。未做浏览器目视验证。 - 防复发:新增顶层路由段时必须同步确认错误与未找到边界覆盖;
global-error不得依赖根布局引入的全局样式。 - 相关记录:BUG-219 无前序同类记录
- 修复版本:本地未提交候选
BUG-220 | 管理端 antd 缺少 SSR 样式提取与 React 19 适配,首屏闪烁无样式内容
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/admin/**全部 18 个后台页面;以及所有用户端路由的首屏 CSS 体积。 - 用户现象:进入后台页面时先闪现一屏无样式内容再套上 antd 样式;同时聊天、登录、会员等用户端页面也要下载解析只有后台会用到的 antd 覆盖样式。
- 根因:其一,admin 是
"use client"子树并使用 antd v5 CSS-in-JS,但项目从未接入AntdRegistry/@ant-design/cssinjs服务端样式提取,也未应用 antd v5 + React 19 的官方渲染适配,服务端输出 0 字节 antd 样式。其二,globals.css由根布局引入,却在尾部包含 37 行.admin-app-shell .ant-*后台专用规则。 - 修复:新增
frontend/src/components/admin/admin-antd-registry.tsx,在 admin 子树内完成按请求的样式提取(createCache+extractStyle+useServerInsertedHTML,输出data-rc-order="prepend"保证服务端样式先于客户端注入),并用 antd 5.29.3 公开导出的unstableSetRender完成 React 19 适配,不新增 registry / patch 包。仅显式声明已随 antd 安装的@ant-design/cssinjs@^1.24.0(版本未变、锁文件仅增 1 行)。后台 37 行样式移入frontend/src/app/admin/admin.css由 admin 布局引入;移除与 antd reset 重复的 refine reset。另在next.config.ts增加experimental.optimizePackageImports,既有outputFileTracingIncludes等设置逐项保留。 - 验证:
npx next build退出码 0;构建产物显示用户端 CSS chunk 184,968 字节且不含任何后台规则,后台规则独立成 5,358 字节 chunk 且仅被.next/server/app/admin/下 18 个 client-reference-manifest 引用。以react-dom/server配ServerInsertedHTMLContext实测 registry 服务端输出 90,132 字节含主题色#85432f的<style id="antd-cssinjs">(修复前为 0)。npm ci --dry-run报告锁文件同步。standalone 产物仍包含 Python skill 资产。未能以真实管理员会话做端到端 HTTP 验证(需 Postgres 与登录态)。 - 防复发:引入 CSS-in-JS UI 库时必须同时接入 SSR 样式提取;路由段专用样式不得写入根布局引入的全局样式表。
- 相关记录:BUG-213
- 修复版本:本地未提交候选
BUG-248 | 七个设计 token 类名编译不出任何 CSS,报告页文字颜色长期未生效
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
text-ink、text-ink-secondary、text-ink-tertiary、text-danger、text-warning、bg-canvas、hover:bg-canvas-muted共 7 个类名,30 处调用点,分布在personal-report-page.tsx(17 处)、reports/[reportId]/的 error、loading、not-found 页、generate-personal-report-button.tsx与app/page.tsx。 - 用户现象:个人报告页与报告子页的次级说明文字、错误提示、警示文字全部沿用正文墨色,本该更浅或本该是红色、琥珀色的层次完全没有出现。因为页面并未报错也没有明显错版,问题从未被当成 Bug 报上来。
- 根因:Tailwind v4 只从
@theme构建工具类命名空间。globals.css把 32 个--color-*设计 token 定义在普通:root里,而顶部的@theme inline块只暴露了--color-background、--color-foreground、--color-muted-foreground等另一套 shadcn 别名。于是text-ink这类按 token 名书写的类名不生成任何规则,静默失效——类名拼写正确、编辑器不报错、构建也不报错。 - 修复:在既有
@theme inline块内追加 32 行,把全部:root调色板 token 以同名别名暴露出来(--color-ink: var(--color-ink);)。未改动任何 token 的取值,未新增或删除:root声明,未改动任何.tsx。沿用inline而非新开裸@theme:块内既有的--color-background: var(--color-canvas)依赖inline才能转发到调色板而不是被 Tailwind 接管,拆成两个块只会多出一套结构。 - 验证:以
@tailwindcss/postcss编译真实样式表前后对比,产物 216,466 → 218,532 字节、311 → 318 条工具类规则,diff -u显示新增 57 行、删除 0 行,新增的正是上述 7 个类且无一遗漏,既有text-primary、text-muted-foreground、bg-background输出逐字未变。另以 headless Chrome 取getComputedStyle实测:修复前 13 个探针有 9 个取到错误值(四个文字类都落回正文墨色rgb(29,29,31),三个背景类落到透明),修复后全部为预期值。新增frontend/tests/design-token-contract.test.ts遍历src/下所有颜色工具类并与@theme命名空间交叉核对,已在修复前的样式表上确认该测试会失败。tsc --noEmit清洁;读取globals.css的 25 个测试文件共 253 条断言全绿。 - 防复发:新增设计 token 必须同时进入
@theme,否则按 token 名书写的工具类会静默失效。合同测试须交叉核对"src/中实际使用的工具类"与"@theme暴露的命名空间",仅断言类名字符串存在无法发现这类缺陷。 - 相关记录:BUG-215 同属"断言了文本却没断言真实产物"的一类
- 修复版本:本地未提交候选
BUG-249 | 每敲一个字重渲染 2723 行组件,且刷新丢失未发送的问题
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/主对话页输入框,聊天场景中最高频的交互路径。 - 用户现象:在输入框中打字时界面卡顿,长会话下尤其明显;此外刷新页面或误触返回会丢失已经写了一半的问题,没有任何恢复入口。
- 根因:
draft(输入框文本)是Home()的顶层useState,而Home()单个函数体就有 2723 行、56 个useState、18 个useEffect、47 个内部函数、向子组件传下 62 个函数 prop,且全文件useCallback与useMemo各为 0,React Compiler 也未启用。因此每次击键都会重渲染整个组件、重建全部 47 个内部函数、给所有子组件换一批新的函数引用。草稿仅存在于 React state,没有任何持久化。 - 修复:新增
frontend/src/lib/composer-draft.ts作为模块级草稿 store,新增frontend/src/components/chat-composer.tsx通过useSyncExternalStore订阅它。Home()中draftstate 移除,setDraft/setDraftTheme/setDraftEntrypoint保留为同名函数(前者转发到 store,后两者写 ref),十余处既有调用点逐字未改。选用外部 store 而非useImperativeHandle:输入框在生时校正面板与引导表单打开时会真的卸载,而selectSession、openRectificationCase恰好在这些窗口里调用setDraft(""),ref 句柄此时为 null 会让清空静默失效、旧文本在重新挂载后复活。草稿以jyotisha.composer-draft写入 sessionStorage(沿用jyotisha.pending-consultation命名约定),发送成功即删除键,读取时校验类型、拒绝未来时间戳、丢弃超过 24 小时或超出 500 字上限的值,sessionStorage抛错(隐私模式、配额)时降级为纯内存,绝不向打字路径抛出异常。输入框保持受控,isComposing中文输入法守卫原样保留。 - 验证:
tsc --noEmit、eslint(未新增任何 disable 注释)、npx next build均通过;读取page.tsx的 24 个既有测试文件 268 条断言无需修改任何一条即全部通过;新增frontend/tests/composer-isolation-contract.test.ts5 条。逐项确认发送后清空、停止后恢复原问题、推荐问题填入、咨询恢复回填、发送按钮空态禁用五个行为均未变。未做的验证:未在真实浏览器中实测中文输入法,也未用 React Profiler 量化重渲染减少量——重渲染的消除是结构性的(击键不再改变Home()中任何 state),但没有实测数字。 - 防复发:高频输入状态必须下沉到实际使用它的叶子组件,不得放在大型容器组件的顶层;跨组件读写的输入状态优先用外部 store 而非 ref 句柄,因为句柄在组件卸载期间不可达。
- 相关记录:BUG-249 无前序同类记录
- 修复版本:本地未提交候选
BUG-250 | 首屏强制下载动画引擎与完整 Markdown 管线,多出 73 KB
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/主对话页首屏 JavaScript 体积,影响所有用户尤其是移动端与弱网首次访问。 - 用户现象:打开对话页时需要先下载并解析动画引擎和完整 Markdown 渲染管线,而这两者在用户发出第一条消息、收到第一条回答之前都用不上,首屏可交互时间被拖长。
- 根因:
chat-message-row.tsx静态导入gsap、@gsap/react与thinking-orbs,chat-message-content.tsx静态导入react-markdown与remark-gfm,两者又都被page.tsx静态导入。全代码库此前只有 1 处next/dynamic,其余全部静态。 - 修复:Markdown 抽到
chat-markdown-view.tsx,经import()加模块级缓存加载——刻意不用React.lazy+Suspense,因为 lazy 的 promise 落定后仍需一次重渲染,可能露出一帧 fallback;模块级缓存则保证解析完成后每次渲染都同步出 Markdown,整个生命周期只切换一次。加载前的兜底仅在文本不含任何 Markdown 语义字符时渲染纯段落(此时输出与 CommonMark + GFM 逐字一致),含语法则渲染空而非原文,确保用户永远不会看到裸露的**bold**。GSAP 改为import("gsap"),且只在模块已解析时才执行入场动画——若在首屏后补放会先绘制可见再拉回autoAlpha: 0淡入,那是比不动画更糟的闪烁;prefers-reduced-motion守卫保留并强化(该类用户现在完全不下载 gsap)。移除@gsap/react(它会静态导入 gsap,留着就白拆了),useGSAP替换为等价的 isomorphic layout effect,gsap.matchMedia()/motion.revert()结构不变。thinking-orbs 用next/dynamic+ssr: false,占位符预留精确的 20×20 盒子避免抖动。三个 chunk 在首屏绘制后经requestIdleCallback(Safari 回退setTimeout(300))预取。 - 验证:
next build前后实测。Next 16.2.10 的 Turbopack 已不再打印 First Load JS 列,故改为从预渲染的.next/server/app/index.html汇总其引用的全部/_next/static/**.js并按 gzip -9 计算,两次口径一致:549.5 KB → 476.3 KB gzip(−73.2 KB,−13.3%),raw 1819.4 KB → 1601.4 KB(−12.0%)。两次构建的page.tsxsha256 相同,19 个首屏 chunk 中 17 个逐字节相同,三个新 lazy chunk 合计 74,006 字节 gzip,占降幅的 98.7%,可归因。以最小化产物特征串(gsap 的GreenSock、markdown 的micromark、orbs 的ribbon)确认三者已不在任何首屏脚本中。tsc、eslint清洁;新增frontend/tests/chat-bundle-splitting-contract.test.ts5 条(含对"绝不渲染裸 Markdown"规则的真实单元断言,覆盖**、##、表格、反引号、裸 URL 等);相关既有测试 38 条全绿。未做的验证:无法在真实浏览器中目视确认无闪烁,无闪烁的结论来自代码路径与合同测试而非实际渲染。 - 防复发:仅在首次交互后才需要的重依赖不得静态导入进首屏组件树;延迟加载必须同时给出不产生布局抖动、不闪烁、不泄漏源码语法的兜底,做不到就不要延迟。
- 相关记录:BUG-250 无前序同类记录
- 修复版本:本地未提交候选
BUG-251 | 查看报告与充值走整页跳转,返回后对话状态全部丢失
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/主对话页跳往/reports与会员充值页的 5 个入口。 - 用户现象:从对话中点开报告列表或余额、充值入口时整页刷新,返回对话后需要重新加载全部会话与账户数据;点击充值时刚输入的问题也一并丢失。
- 根因:
page.tsx有 11 处window.location跳转、useRouter使用为 0,文件从未引入next/navigation。每一次跳转都丢弃整棵 React 树、已加载的 JS 与全部客户端状态,再从头启动应用。 - 修复:逐处判断后改软导航 5 处(
/reports侧栏入口、会员入口、余额按钮、以及发送前余额守卫与/api/consult返回 402 两处充值跳转),改为router.push。其中发送前的余额守卫在清空输入框之前执行,软导航因此顺带保住了用户刚输入的问题。刻意保留硬跳转 6 处:5 处/login认证重定向(401 时客户端持有服务端已拒绝的会话,整页加载是可靠的清除方式;signOut()后尤其如此,会话已在服务端销毁,软跳转会让整棵树带着已登出用户的数据继续存活),与 1 处启动失败页的重试按钮(失败数据全部来自挂载时useEffect内的客户端fetch,router.refresh()只重取服务端组件、不会重新挂载客户端树,改了按钮反而会静默失效)。此判断与本分支membership/page.tsx保留认证硬跳转的处理一致。 - 验证:
tsc、eslint清洁;next build通过;新增frontend/tests/chat-navigation-a11y-contract.test.ts13 条,同时锁定"哪些是软导航"与"认证重定向是有意保持硬跳转",未来有人机械改写会被测试拦下。修改既有测试 1 个:personal-report-entry.test.ts原断言window.location.assign("/reports"),随代码更新为router.push并追加assert.doesNotMatch反向断言,强于原状。全部非数据库套件 199 个文件 1592 条断言全绿。未做的验证:未在运行中的应用里实测软导航的实际体感。 - 防复发:应用内同源路由必须走
router.push;认证重定向与启动失败恢复例外,且例外必须在合同测试中显式声明为有意,不能只是"还没改"。 - 相关记录:BUG-251 无前序同类记录
- 修复版本:本地未提交候选
BUG-252 | 回答完成对屏幕阅读器完全无提示,且开始提示很可能也未被念出
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/主对话页依赖屏幕阅读器的用户的全部问答流程。 - 用户现象:提问后只能听到"Jyotisha 正在回答"(且这一条很可能也没被念出),此后没有任何提示——不知道回答何时结束、是否可以开始阅读、内容在哪里。
- 根因:其一,
page.tsx只有开始态的 live region,完成态没有任何播报出口。其二(排查中发现的更严重问题),该 region 位于.message-list内部,而后者带aria-busy={isLoading};aria-busy="true"的语义正是让辅助技术暂缓呈现该子树的变化,因此连"正在回答"很可能都从未被念出。其三,该 region 会随会话首条消息重新挂载,而挂载时即带内容的 live region 本身就不可靠。 - 修复:新增
frontend/src/lib/chat-reply-announcement.ts,把回答生命周期归为六个阶段并为每个阶段指定唯一播报出口,避免重复播报:generating与completed走新的状态区域,recovering/stopped交给既有 sonner(已确认其容器带aria-live="polite" aria-relevant="additions text",BUG-216 接入的 44 条提示本就会被念出),failed交给既有role="alert"。完成播报含序号(「第 N 条回答已显示在对话区末尾」),因为连续两次内容完全相同的播报常被屏幕阅读器抑制。状态区域移出aria-busy子树,改挂在main.chat-app下并跨分支常驻。流式文本本身绝不进入 live region——逐字更新会让屏幕阅读器持续重复播报,完全不可用;合同测试断言播报模块不引用任何流式文本变量。.message-list的aria-busy保留不动,它同时在抑制agent-activity-status那个按阶段变化的role="status"标签,去掉会造成串读。启动失败页从<main>上的aria-live="assertive"改为内层role="alert",同等紧急度但不再覆盖 main 地标,全文件不再出现aria-live="assertive"。 - 验证:
tsc、eslint清洁;next build通过;新增合同测试 13 条(与 BUG-251 同一文件);全部非数据库套件 199 个文件 1592 条断言全绿。同时确认 BUG-217 新增的"跳到最新"控件键盘可达:真实<button>、无tabIndex、标签与aria-label一致、min-h-11 min-w-11、pointer-events-auto。未做的验证:无浏览器与屏幕阅读器,全部无障碍行为均为依据 ARIA 规范推理并以源码合同测试固定,没有任何一条经过实听。特别未经证实的有:NVDA / JAWS / VoiceOver 是否真的念出完成播报、序号是否足以避开重复文本抑制、aria-busy在各家实现中抑制的程度。 - 待跟进:「跳到最新」按钮在跳转成功后立即卸载,焦点会掉回
<body>,下一次 Tab 从文档顶部重新开始。修复需要给滚动容器一个可编程聚焦目标,会牵动 BUG-217 刚落地的合同测试,另开一轮处理。 - 防复发:
aria-live区域不得放在任何可能带aria-busy="true"的子树内,也不得随内容分支重新挂载;流式内容只播报状态迁移,绝不播报增量文本;每个事件必须有且只有一个播报出口,静默必须是显式声明的选择而非遗漏。 - 相关记录:BUG-216、BUG-217
- 修复版本:本地未提交候选
BUG-253 | 27 个 BUG 编号被两条不同记录共用,交叉引用体系失效
- 状态:resolved(编号唯一性已修复;文档更深层的错位问题见待跟进)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
docs/BUG_HISTORY.md全文的交叉引用体系,以及AGENTS.md第 7 节的可执行性。 - 用户现象:无终端用户可见现象。对维护者与 AI agent 而言,
相关记录:BUG-209这类引用无法确定指向哪一条记录,文档最有价值的部分(247 条记录中有 224 条填写了相关记录)因此不可靠。 - 根因:
BUG-018至BUG-043连续 26 个编号加上BUG-209,共 27 个编号各被两条不同记录占用。成因是并行分支各自追加记录、合并时两侧都保留。该失效模式仍在持续:仅在准备本批提交的一小时内,staging就两次抢占了本地待提交记录的编号(先BUG-214后BUG-215),迫使本地记录两轮顺延。 - 修复:保留每对中较早的记录编号,为后加入的一条分配
BUG-221~BUG-247。逐条审计了 78 行指向受影响编号的引用:其中 10 行凭"合并保留了两侧副本、两份仅编号不同"直接判定,13 行凭语义判定(如复发自:BUG-019出现在「收到具体经历后仍重复泛问」中,按保留编号读是「原问题交接租约过期」全然无关,按顺延编号读是「自然语言真实事件被静默丢弃」同属一条提取链),14 行属自引用的全球地点集群,5 行属合并后的模板追问流;36 行确认本就正确未动。另在文档头部使用流程增补一条:追加记录前必须先检索当前最大编号。AGENTS.md第 7 节把「必须完整读取并搜索」改为「必须用报错原文、接口路径、状态码、模块名和用户操作强制检索,并完整读完命中的记录」,并明写「检索不是可选步骤」——该文件已达 3690 行约 150 KB,通读一次约耗 5 万 token,原措辞在实践中不可执行;六条执行要求逐条未改。同步更新tests/test_bug_history_workflow.py的断言以匹配新措辞并追加「检索不是可选步骤」一条,保持约束强度。 - 验证:重复编号 0;记录总数 247 条不变;编号 001–247 无空缺;无悬空引用;无冲突标记;
docs/BUG_HISTORY.md仅因新增的头部说明 +1 行,AGENTS.md行数不变;逐行归类确认改动仅落在 27 个标题行、38 个相关记录行、8 个复发自行,状态/根因/修复/验证/防复发等正文字段零改动。tests/test_bug_history_workflow.py两条断言通过。 - 待跟进:审计中发现一个更早、更严重的缺陷——
BUG-020~BUG-041区间内记录正文与标题整体错位一位,部分标题下只剩 3 行结尾字段而没有状态/根因;且BUG-221~BUG-234本质是BUG-020~BUG-035的重复记录,编号已唯一但内容仍冗余。两者都需要移动正文内容,属内容编辑,未在本轮处理,已由 BUG-254 完成。另有 7 行属"混合编号"(此前有人部分修正过),证据不足以判定,已就地加(编号存疑:…)标注而未改数字。 - 防复发:BUG 编号必须唯一;追加记录前先检索当前最大编号;出现重复时保留较早记录的编号并同步修正指向它的
相关记录与复发自。强制性文档约束的措辞必须与文档实际体量相称,否则会被普遍忽略而失去约束力。 - 相关记录:BUG-253 无前序同类记录
- 修复版本:本地未提交候选
BUG-254 | 22 条记录的正文挂在别人的标题下,其中一条正文被压在另一条记录末尾
- 状态:resolved
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
docs/BUG_HISTORY.md中BUG-020~BUG-043与BUG-221~BUG-240区间的 22 条记录。 - 用户现象:无终端用户可见现象。对维护者与 AI agent 而言,按标题检索到的记录读出来是另一个 bug 的现象、根因与修复——比查不到更危险,因为它看起来完全正常。BUG-253 已修复编号唯一性,但当时判断"需要移动正文内容"而留作待跟进。
- 触发条件:按
BUG-036~BUG-041、BUG-235~BUG-240任一编号检索并阅读其正文。 - 根因:三个缺陷叠在一起,都源自 2026-07-25 的合并提交
3ca30ed7。其一,该提交自身的标题序列与正文序列就已错开一位,从BUG-036起每份正文实际属于前一个标题,末尾BUG-240的正文则被整段追加到BUG-043之后,使BUG-043带了两份完整正文。其二,两条分支各自保留了同一批记录的结尾三行(相关记录/复发自/修复版本),多出来的副本落在下一个标题之下,形成 11 处"有标题无正文、只有三行结尾"的空壳。其三,BUG-221~BUG-234与BUG-020~BUG-035标题逐字相同、共用同一份正文,本就是同一个 bug 的两份记录,BUG-253 为消除编号冲突给后者另分了编号,反而把"重复"固化成了 14 条独立记录。 - 修复:按内容而非位置重新绑定。11 处只剩结尾三行的空壳先行删除——逐条比对确认其中 7 处与所属记录的结尾逐字同义(仅编号体系不同),另 4 处是所属记录结尾的真子集,差集恰为 BUG-253 标注过的
BUG-018/BUG-019存疑残留,删除不丢信息。随后把错位区间内 11 份正文各回退一个标题,BUG-240的正文从BUG-043末尾取回;每一次归属都以正文中的用户现象/根因与标题语义逐条核对,不依赖行号。14 对重复记录合并为一条,保留较早编号,BUG-221~BUG-234退役成空号,23 处指向退役编号的引用回指合并后的编号。BUG-235~BUG-240在3ca30ed7中就缺失的状态/首次发现/最近更新三行按同批次记录补记,并在状态行内标明"非原文",不冒充原始内容。 - 验证:记录数 253 → 239(退役 14 个重复编号);结构不完整的记录 22 → 0;重复编号 0;悬空引用 0;冲突标记 0。以"改动前每一行正文必须在改动后仍然存在"为口径做了全量比对,34 行差异全部落在被重写的引用行与被删除的 11 处重复结尾上,无正文丢失。
tests/test_bug_history_workflow.py通过。 - 防复发:
docs/BUG_HISTORY.md出现合并冲突时,只消除冲突标记不算解决——必须逐条确认标题与其下正文仍然对应,判据是正文的用户现象/根因能否解释标题。两条分支各自追加记录时,标题逐字相同的两条必须合并为一条而不是分配新编号;分配新编号只应对"不同的 bug 恰好撞号"。 - 待跟进:
BUG-163的状态/最近更新/根因/验证/修复版本各出现两次,疑为同一记录两轮更新直接拼接。该缺陷早于本轮、不属正文错位,未处理。 - 相关记录:BUG-253
- 修复版本:本地未提交候选
BUG-255 | 模型把步数预算耗在无效工具参数上,个人咨询只返回兜底文案
- 状态:resolved(本地修复,未提交、未发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/api/consult个人咨询的 Agent 步数预算、run-jyotish-consultation的模型可见参数契约,以及模型运行结束原因的可观测性。 - 用户现象:staging 事业类咨询运行至星盘计算成功,但模型没有产出任何回答文本,用户只看到
ensureFinalResponseText()的兜底句“本次计算已完成,但暂时没有生成可展示的回答”。事件流显示 Skill 已加载、四次run-jyotish-consultation(三次失败、一次在 20278ms 后成功),此后再无answer.delta。 - 触发条件:模型在同一次运行内多次调用排盘工具,其中至少两次同时传入
domains与兼容字段theme;叠加渐进式披露的 Skill 参考读取后,maxSteps: 6在写回答之前耗尽。 - 根因:三层叠加。其一,
consultationToolInputSchema把domains与theme声明为两个彼此独立的可选字段,互斥关系只在canonicalDomainPlan()里以invalid_consultation_domain_plan运行期抛出,工具description也从未提到该约束;更直接的是jyotishInstructions明确写着“Use the legacy theme field only for a single-domain compatibility retry”,等于主动引导模型去用一个会被拒绝的组合。其二,这类无效调用发生在步骤记录 try/catch 之前,既不产生chart-calculation活动也不追加运行步骤,因此每次都白耗一个模型步骤且在回执里不留痕迹。其三,maxSteps: 6与AbortSignal.timeout(110_000)约束同一次运行却分别硬编码:1 次 Skill 加载 + 4 次工具调用已占 5 步,skill_read/skill_search这类渐进式披露工具未映射进公开事件流,第 6 步一旦被一次不可见的参考读取拿走,运行就在没有任何回答的情况下结束。finishReason在整个仓库中没有任何记录点,因此“步数耗尽”只能靠事后数事件推断,无法证实。 - 修复:把互斥关系改为不可表达而非运行期拒绝——模型可见的
inputSchema只保留question与domains,theme从模型契约中移除,.strict()保持不变,使theme在进入工具体之前即被 Mastra 的入参校验拒绝;description补齐“只用一个有序domains数组,省略即接受服务端已选领域,出生资料服务端绑定”的显式契约;jyotishInstructions同步删除引导模型使用theme的那句。canonicalDomainPlan()继续处理单值theme形态并保留invalid_consultation_domain_plan,导出后由直接单元测试覆盖,供不经模型 schema 构造计划的调用方使用。步数预算与时钟预算改为相邻声明的AGENT_MAX_STEPS = 8与AGENT_TIMEOUT_MS = 110_000,并注明二者约束同一次运行、必须一起考虑;运行步骤记录预算随之对齐,避免耗尽步数的运行同时截断自身证据。新增受控观测字段modelFinishReason(封闭枚举,未知取值一律归一为unknown)与modelStepCount,在流中按step-finish计数、以终止finish携带的步骤列表为准,跨重试累计。未放宽单次计算边界,未放宽运行合同门禁,未把任何模型原文或 provider payload 写入日志。 - 验证:新增修复前失败的回归 6 项——模型可见 schema 必须拒绝
theme(单独出现与与domains同时出现)、被拒调用不得推进任何运行状态(consultationToolStarted/consultationToolCallCount/steps全部不变)、完成运行必须记录finishReason与权威步数、以tool-calls结束且无回答的运行必须记下耗尽的步数、重试必须累计步数并归一化未识别的 provider 取值、步数与时钟预算必须成对声明。回退任一源改动可确认对应回归失败。另新增公开回执守卫:agentExecutionReceiptSchema是 strict,modelFinishReason/modelStepCount一旦透出会让成功运行在序列化自身回答时报错,故断言按白名单构建的回执不含这两个字段、直接透出则必须抛错。canonicalDomainPlan()补 8 项直接断言,锁定“单值 theme 不得覆盖路由选定领域”。BUG-205 的缓存不被污染性质改由 schema 合法但注册表非法的输入(domains: ["career", "unknown"])复验,因其原始触发条件已不可达。全量非数据库套件 1586/1586 通过,npx tsc --noEmit0 错误,改动文件npx eslint0 错误。 - 待跟进:Mastra 的
createTool会在调用业务execute之前完成入参校验,校验失败时返回错误对象而不是抛出,因此模型若仍误传theme,本次运行仍会消耗一个步骤,只是拿到的是明确可纠正的提示,而不再是不透明的invalid_consultation_domain_plan,且不会进入计算缓存。该类校验失败同样不追加运行步骤,回执中依旧看不到;是否为“入参被 schema 拒绝”单独记一条受控失败码,留待与failureCode分类一并评估。另记maxSteps取 8 而非更大值的依据是时钟而非步数:单次排盘约 20s,路由maxDuration为 120s、Agent 超时 110s,三次失败计算即会先耗尽时钟;8 步刚好覆盖最长有用形态——Skill 加载、两次渐进式披露参考读取、一次计算加一次重试、一次写回答,继续放大只会在注定失败的运行上多花 token,不会换来更多计算机会。 - 防复发:模型可见的工具参数不得存在两个语义重叠的字段,互斥关系必须由 schema 表达而不是运行期抛出;任何在模型契约中被移除的字段,必须同时从 Agent instructions 中删除,否则提示词会继续引导模型踩坑。步数预算与时钟预算必须相邻声明并在同一处说明彼此关系,不得分散硬编码。凡以“模型没写回答”为现象的问题,必须先能读到
finishReason与实际步数再下结论;新增观测字段只能是封闭枚举或计数,且必须同时验证其不会进入 strict 的对外回执。 - 相关记录:BUG-214、BUG-205、BUG-186
- 修复版本:本地未提交候选
BUG-256 | 多领域排盘把回答契约整块藏进 consultations,模型无据可依只能不说话
- 状态:resolved(本地修复,未提交、未发布)
- 影响面:
/api/consult个人咨询中模型提交两个及以上领域的全部运行;单领域运行不受影响。 - 首次发现:2026-08-17
- 最近更新:2026-08-17
- 用户现象:staging 综合类咨询(问题“未来两年哪些阶段值得把握”)工具执行成功,回执
route: "multi-domain"、domains: ["timing","career","wealth"]、status: "ready"、missingLayers: []、durationMs: 62909,模型却产出零回答文本,用户只看到ensureFinalResponseText()的兜底句“本次计算已完成,但暂时没有生成可展示的回答”。与 BUG-255 不同,本次步数预算{planned:10, used:3, remaining:7}远未耗尽,不是步数问题。 - 触发条件:模型在一次
run-jyotish-consultation调用中提交多于一个领域,且运行成功。单领域调用(Run 1,事业类)在同一批数据上正常输出完整回答。 - 根因:
toModelDomainPlanContext()对单领域与多领域返回两种结构不同的形状。单领域走{ ...consultations[0], domains, consultations },证据包被摊平到顶层,evidence_contract、claim_cards、rectification、route、status、question、packet_version全部可达;多领域只返回{ success, domains, consultations },顶层仅此三键。而jyotishInstructions的硬性输出契约全部以顶层路径表述——evidence_contract.answer_policy作为硬约束、hard_blockers非空才可声称计算失败、rectification.boundary=not_auto_rectified视为终态、把回答政策当作权威依据。多领域形状下这些路径一律解析不到,叠加“服务端证据不支持的内容一律不得陈述”的总政策,模型手里没有任何授权它开口的契约,沉默是它唯一符合提示词的选择。证据并未丢失,只是嵌在consultations[]里,而 instructions 从未提到该路径。反向不对称同时存在:success只在多领域形状里有,单领域形状根本没有该键,因为证据包 schema 里没有这个字段。附带一处冗余:projectNatalFoundation()对每个领域产出完全相同的本命投影,三领域载荷把同一大块本命数据重复三份,零信息增量。 - 修复:为整个领域计划给出一份顶层回答契约,形状与单领域逐字一致,使 instructions 引用的每条路径在两种形状下都能解析。合并一律取最严:
status取 ready > degraded > blocked 中最差的一档;hard_blockers与missing_route_layers取并集;answer_policy中can_answer_*一类许可布尔必须每个执行领域都为 true 才为 true,should_lead_with_limitations一类限制布尔任一领域为 true 即为 true,数组字段取并集,其余字段仅在所有领域取值一致时保留;出现无法合并的分歧时不选边,记入unresolved_policy_fields并强制should_lead_with_limitations = true。available_layers是唯一取并集的许可类字段——某层只要为任一领域真实算出就确实存在,否认它等于否认真实证据,真正约束回答的是缺失与阻断的并集。rectification.boundary只要有一个领域报not_auto_rectified就整体沿用该边界。本命投影在各领域逐字相同时上提为顶层单份并从各领域移除,不同时保持每领域各自携带,不挑一份充当共享。单领域形状继续走摊平分支,逐字不变,另补success与omitted_domains两键消除反向不对称。每领域细节仍留在consultations[],未做删减。 - 验证:新增修复前失败的回归 4 项——多领域结果必须暴露与单领域相同的顶层契约路径(
packet_version/question/route/status/evidence_contract/claim_cards/rectification);一个领域禁止精确时机即强制合并政策同样禁止,且status取最差、缺失层与阻断项取并集;mergeConsultationAnswerPolicies()的直接单元断言锁定“合并只能收紧,不能放宽”,含冲突字段不选边;合并契约必须暴露单领域暴露的每一个政策字段,防止今后新增字段被静默丢弃。第 4 项另覆盖本命投影上提与“领域不一致时不上提”。把多领域分支回退成{ success, domains, consultations }可确认这 4 项全部失败。 - 待跟进:
projectEvidenceContract()只投影available_layers/missing_route_layers/hard_blockers/answer_policy/user_facing_limitation五项,Python 侧在answer_policy顶层给出的deterministic_claims_forbidden_for并不在其中,因此 instructions 里“把answer_policy.deterministic_claims_forbidden_for当作硬性禁止”这句在单领域形状下同样解析不到——这是与本条同源的对称缺口,但属于投影层而非合并层,本轮未改投影范围,仅让合并逻辑对该类禁止列表按并集处理,字段一旦被投影即自动生效。 - 防复发:同一个工具结果不得对不同输入返回结构不同的顶层形状;提示词以顶层路径表述硬性契约时,每种可能的返回形状都必须让这些路径解析得到,否则模型会以沉默满足“无证据不得陈述”。跨领域聚合只允许收紧,任何许可类字段取并集前必须能说清“它为何不是放宽”;无法合并的分歧必须显式暴露并倒向限制,不得择一。以“模型没写回答”为现象的问题,先核对提示词引用的每条路径在实际载荷中是否存在,再怀疑步数或时钟。
- 相关记录:BUG-255、BUG-214、BUG-215
- 修复版本:本地未提交候选
BUG-257 | 领域上限允许提交注定超时的计划,六领域计划在时钟上从不可能完成
- 状态:resolved(本地修复,未提交、未发布)
- 影响面:
/api/consult个人咨询的领域计划上限、run-jyotish-consultation的模型可见参数契约与工具描述,以及领域循环的时钟纪律。 - 首次发现:2026-08-17
- 最近更新:2026-08-17
- 用户现象:staging 综合类咨询(问题“请综合说明我当前最值得关注的主题”)在一次
tool.started与chart-calculation活动之后直接tool.failed code=calculation_failed,没有evidence-validation活动,说明失败发生在领域循环内部;服务端补跑的第二轮模型循环既无工具调用也无文本,最终run.failed code=runtime_contract_incomplete。 - 触发条件:模型提交的领域数乘以单领域实际耗时超过 Agent 级 abort 信号剩余时间。观测口径为单领域 20936ms、三领域 62909ms,约 21s/领域,证实领域循环串行且延迟随领域数线性增长。
- 根因:
MAX_CONSULTATION_DOMAINS = 6,工具描述也照此宣称“up to six allowlisted domains”,但for (const domain of domains)逐个 await 一次 Python 调用,每次约 21s,且所有调用共用同一个AbortSignal.timeout(AGENT_TIMEOUT_MS)(110s)——该 deadline 对整轮运行是累计的,runConsultationWorkflow用AbortSignal.any叠加的 90s 才是每次调用各自的。因此六领域约 126s 永不可能完成,四领域约 84s 也几乎不给模型留下写回答的时间。schema 允许表达一个注定失败的计划,且失败时序(循环内抛出、无evidence-validation)与该推断一致。并发不是出路:Python API 是单进程ThreadingHTTPServer,核心计算受 GIL 约束,/api/consultation_workflow为同步处理,异步作业另有JYOTISH_ASYNC_JOB_WORKERS=2与JYOTISH_ASYNC_JOB_QUEUE_SIZE=8的有界队列,满载即以 HTTP 503ERR_JOB_QUEUE_FULL回绝(前端workflow_queue_full即由此映射)。并行只会把串行等待换成排队与 GIL 争抢,不会缩短总时长,故不并行。 - 修复:上限改为由时钟推导而非选定,与它约束的同一轮预算相邻声明:
CONSULTATION_DOMAIN_DURATION_MS = 21_000(staging 实测)、CONSULTATION_ANSWER_RESERVE_MS = 45_000(三领域运行在 110s 内实际留给写回答的余量口径)、CONSULTATION_DOMAIN_WALL_CLOCK_MS = 110_000 - 45_000 = 65_000,MAX_CONSULTATION_DOMAINS = floor(65_000 / 21_000) = 3。模型可见的domains数组上界随之收为 3,使超预算计划不可表达——Mastra 在进入工具体之前即拒绝,不会启动任何计算、不推进任何运行状态。绕过模型 schema 的内部调用方走executableDomainPlan():截断到上限、把余下领域记为omittedDomains,宁降级不整体失败。循环内另加domainFitsRunBudget(),按已执行领域的真实耗时外推下一个领域是否还装得进循环份额,装不下就停在此处并把剩余领域计入omittedDomains;第一个领域始终执行,否则无从作答。任何截断都会把顶层status压到至少degraded、强制should_lead_with_limitations = true,并通过omitted_domains与回执的omittedDomains同时对模型和调用方披露,使部分回答不可能被当作完整回答呈现。工具描述与jyotishInstructions同步改写为真实上限、串行执行、单领域时钟成本与截断披露语义。未提高AGENT_TIMEOUT_MS:路由maxDuration为 120,110s 已贴近上限。 - 验证:新增修复前失败的回归 4 项——上限必须等于时钟能支付的领域数(同时断言
AGENT_TIMEOUT_MS、循环份额与executableDomainPlan()/domainFitsRunBudget()的边界取值,并显式记录旧上限 6 在 110s 内不可能完成);超上限计划必须不可表达且一次计算都不启动(consultationToolStarted与steps均不变);每领域 40s 的慢运行必须在第一个领域后停止、披露丢弃的领域、status降为degraded,且被截断的结果仍须携带完整顶层契约;工具描述宣称的上限必须与执行的上限一致,且不得再出现 “up to six”。把上限回退为 6 并让预算判定恒真,可确认前三项失败;描述一致性那项针对修复前的字面描述文本失败。 - 防复发:任何领域级并行提议必须先证明后端能承接并发,判据是 Python 侧的进程模型、GIL 约束与有界队列,而非前端看起来能不能同时发请求。串行循环的规模上限必须由时钟推导并与预算常量相邻声明,不得独立选定;超出上限的计划优先“执行装得下的部分并披露丢弃项”,其次才是拒绝,且披露必须同时到达模型与回执,并强制降级状态,使部分结果无法被呈现为完整结果。宣称上限的文案与强制上限必须由同一常量插值,禁止在描述里写死数字或数词。
- 相关记录:BUG-255、BUG-256、BUG-214
- 修复版本:本地未提交候选
BUG-258 | 运行失败时回执不随事件返回,最需要解释的运行反而只剩一个错误码
- 状态:resolved(本地修复,未提交、未发布)
- 影响面:
/api/consult所有以run.failed结束的运行的对外诊断信息,以及在流开始之前就失败的 agentic 运行的服务端观测日志。 - 首次发现:2026-08-17
- 最近更新:2026-08-17
- 用户现象:无终端用户可见文案变化。对调用方与排查者而言,
run.completed携带完整回执,run.failed只有code与一句提示,于是 BUG-255 刚补齐的每步durationMs、步数预算、工作流路由在运行失败时一概拿不到——恰好是最需要它们的时刻。 - 触发条件:其一,任何走到
streamAgentResponsecatch 分支的运行;其二,agentic 运行在streamAgentResponse建立之前失败(计划装配、服务端星盘真值缺失、工具构造等),此时请求级 catch 只调用cancel(),永远到不到onError里的 settle-and-log 入口。 - 根因:
run.failed事件 schema 从设计上就没有receipt字段,catch 分支也从未尝试构建回执;而agentExecutionReceiptSchema是 strict,内部字段不能直接透出,agent-observability.ts又是刻意封闭的非 PII 契约(无自由格式 metadata、无原文、无 provider payload),所以“把内部诊断塞进对外事件”这条路本就不通,最初便被整体放弃,连白名单可透出的部分也一并放弃了。服务端侧则是入口位置问题:settle-and-log 只挂在streamAgentResponse的onError上,更早的失败没有任何路径抵达它,观测事件因此对失败最重的那类运行完全缺席。 - 修复:
run.failed增加可选receipt,内容用既有白名单助手publicConsultationRuntimeSteps()构建,与run.completed走同一条边界,因此每步durationMs、步数预算与工作流路由到达调用方,而内部failureCode、modelFinishReason、modelStepCount仍留在服务端。构建回执本身被包在 try 内:回执构建失败不得把失败事件替换成一次静默关闭,此时照旧发出不带receipt的run.failed。服务端侧由 agentic 装配把 settle-and-log 入口发布为agenticFailure.report,请求级 catch 优先经它上报(内部按toAgentObservabilityErrorCode()归一化错误码),仅在该入口尚未发布时退回裸cancel(),从而保证每条 agentic 失败路径都留下一条封闭观测事件。未放宽任何 strict schema,未新增自由格式字段。 - 验证:新增修复前失败的回归 3 项——失败运行必须携带与成功运行同构的白名单回执(断言两步的
durationMs与stepBudget.used,并断言序列化结果中不出现内部分类与模型循环诊断);回执构建抛错时仍须恰好发出一次不带receipt的run.failed;源级契约断言请求级 catch 必须经agenticFailure.report而非裸cancel()上报。删除receipt透出可确认第一项失败;agenticFailure在修复前不存在,第三项对修复前的源文件必然失败。 - 防复发:失败路径的诊断价值必须与成功路径持平,二者共用同一个白名单构建入口;对外 schema 是 strict 不能作为放弃全部诊断的理由,只能作为“哪些字段留在服务端”的划线依据。诊断信息的构建不得成为失败事件本身的前置条件。凡新增 settle-and-log 类入口,必须确认它覆盖到最早的失败点,否则失败越早、可观测性越差。
- 相关记录:BUG-255、BUG-214、BUG-256、BUG-257
- 修复版本:本地未提交候选
BUG-259 | 两套路由各说各话:问题文本选路与声明领域不一致时,多领域计划里除一个领域外全部 400
- 状态:resolved(本地修复,未提交、未发布)
- 影响面:
POST /api/consultation_workflow携带plan_version的全部产品运行,即/api/consult个人咨询的每一次领域调用;不带计划元数据的研究 / MCP 调用方(mcp_server.py的strict_workflow、scripts/consultation_workflow_service.py)行为不变。 - 用户现象:staging 综合类咨询(问题“我的事业和财运接下来会怎么走,两者之间该怎么取舍”,模型选择 career + wealth 两个领域)连续四次
tool.failed code=calculation_failed,四次完全相同,重试无一次成功;用户只看到兜底文案。同批次里问题文本直接含“事业”而只选事业单领域的运行成功(20936ms,完整回答)。 - 触发条件:一次运行提交两个及以上领域,且问题文本的关键词命中的领域不等于其中某个领域声明的路由。因为每个领域各发一次 Python 调用却共用同一段问题文本,文本只能选出一个路由,所以多领域计划里至多一个领域能对上,其余每个都必然 400;判定完全确定,故重试逐次复现同一结果。
- 根因:服务端存在两个互不知情的路由器。
UnifiedConsultationOrchestrator.resolve_route()以文本优先:先查显式应期词,再按domain_tokens关键词匹配,只有文本一无所获时才回落到themes参数。而validate_consultation_plan_contract()要求文本推出的路由必须落在RouteContract.resolved_routes内,每条契约只允许一个路由,不等即抛ConsultationPlanContractError,在execute_consultation_workflow()里被包成BadRequest→ HTTP 400,前端映射为workflow_bad_request,再被safeToolError()压成calculation_failed。本次现象的落点是词表不对称:“事业”在 career 词表里,“财运”却不在 wealth 词表(财务/财富/投资/房产/收入)中,于是 career 与 wealth 两次调用都被文本判成 career,wealth 那次声明 wealth,必然 400。补齐词表只会把矛盾推到下一个问法上——只要一次运行发出多个领域调用,文本选路与声明路由就在结构上不可能同时满足。frontend/src/lib/consultation-workflow-request.ts里theme === "timing"时给问题加前缀“应期与阶段问题:”,正是为了把“应期”这个词塞进文本让文本路由同意声明路由,是本 bug 只对一个领域打过的绕行补丁;同批 timing 运行成功恰恰因为它带着这个前缀。 - 修复:让服务端自己签发的声明路由成为权威,两套路由不再可能互相矛盾。
consultation_plan_contract新增declared_workflow_route():只在计划元数据完整、版本受支持、路由在服务端白名单内时返回该路由,缺一即抛;无任何计划元数据时返回None。execute_consultation_workflow()先取声明路由,再以resolve_route(question, themes, declared_route=...)解析,声明存在即直接返回该路由定义,声明为None时文本启发式逐字不变——这保证遗留调用方行为不变。路由包新增route_source(declared_plan/question_text),使响应能自证由哪套路由决定;routing在前端是z.record+ passthrough,新增键不破坏契约。执行面同步正确:question_type/primary_theme/focus_techniques都取声明领域的RouteDefinition,因此runtime_planner的同步步骤、consumer_context.route的必需层、machine_evidence_packet、real_case_calibration以及前端据routing.primary_theme选取thematic_report.themes[primary_theme]的领域证据,全部落在声明领域上,不会出现“声明 wealth 却拿到 career 证据”。契约检查保持 fail-closed 且一处未放宽:白名单外的路由在declared_workflow_route()就被拒(unsupported consultation workflow route),根本进不到执行;themes与契约不一致、必需层 / claim boundary / requested domains / 证据类别 / depth / horizon / precision boundary 任一不符仍逐项拒绝;原先那条resolved_routes检查保留,语义从“两套路由仲裁”变为“断言执行路由确实等于声明路由”。既然声明路由已经权威,timing 前缀所修的 bug 不再存在,故删除:它会把一句合成前缀塞进模型据以作答的问题文本,而它对路由不再有任何作用;核对过前缀不影响_build_consumer_context的任何领域正则与precise_timing_requested判定(应期不在这些正则里,timing 路由本身已置该标志),也不影响 prashna 之外的question_text用途。 - 验证:新增修复前失败的回归——(1) Python 端复现 run 4:声明
strict_workflow_route: "wealth"、问题文本文本路由到 career 时必须成功且执行 wealth 路由(修复前抛BadRequest: consultation plan route mismatch,即线上那个 400);(2)tests/test_consultation_workflow_domains.py补齐它一直缺的计划元数据——十个规范领域各带完整计划、共用同一段文本路由到 career 的问题,逐个断言routing.question_type/primary_theme/consumer_context.route等于声明领域,且thematic_report.themes[primary_theme]就是该领域证据(修复前除 career 外九个全部 400,这正是此前 bug 从未被测试发现的原因:该文件原本不带计划元数据);(3) 编排器直接断言声明路由压过关键词、无声明时文本路由逐字不变、每个规范领域都可作为声明路由执行、未知声明路由必须拒绝而不是静默回落文本;(4)declared_workflow_route()的白名单与完整性断言,含strict_workflow_route: "free_script"与篡改必需层经 API 仍为BadRequest,确认计划无法夹带不受支持的路由。前端回归改为断言问题文本逐字送达(前缀存在时必然失败)。测试结果:tests/test_consultation_plan_contract.py、tests/test_consultation_workflow_domains.py、tests/test_unified_consultation_orchestrator.py、tests/test_consultation_consumer_context.py、tests/test_api_server_security.py、tests/test_mcp_strict_workflow_career.py、tests/test_runtime_import_boundaries.py、tests/test_historical_event_backtest.py共 275 项通过;scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime通过,ruff门禁文件全通过(改动文件相对修复前无新增告警),py_compile、commercial_privacy_artifact_scan(findings 0)、python -m build均通过;前端npx tsc --noEmit0 错误、改动文件npx eslint0 错误、非数据库套件 1664 项中 1660 通过,4 项失败全部是本机并发 Postgres 容器争抢(另有工作树同时在跑数据库测试,本机同时存在 11 个测试用 Postgres 容器),逐个单独重跑后onboarding-route、rectification-v9-database、admin-database、identity-auth-integration均通过,model-configuration-security的database ...一项单独重跑仍以Connection terminated unexpectedly失败,该文件不引用本轮任何改动模块。 - 待跟进:run 2(问题“请综合说明我当前最值得关注的主题”的那次
calculation_failed)不由本机制解释——该文本不含任何领域关键词,每个领域都会回落到自己的themes并对上,本轮未能找到独立证据说明它为何失败,不认领。同一问题文本的一次失败已在 BUG-257 记为领域数乘单领域耗时超出时钟预算,但本轮没有该次运行的领域数与耗时证据可核对,因此既不视为已解释也不视为复发。另记:领域循环里任一领域抛出即让整次工具调用失败,BUG-257 已有omitted_domains这条降级披露通道,把单领域失败也接入该通道属独立改动,本轮未做——本次修复消除的是那个确定性的失败源。 - 防复发:同一个决定必须只有一个权威来源。凡服务端自己签发的受控元数据已经声明了执行参数,就不得再由请求文本的启发式重新推导一遍并要求两者相等——这类“契约允许了服务端不接受的东西”的自伤矛盾在 fail-closed 门禁下必然表现为确定性 400。文本关键词只能作为没有声明时的兜底,且必须在返回值里标明本次由哪套规则决定。为了让文本路由同意声明路由而改写用户问题(如注入关键词前缀)不是修复而是绕行:它只覆盖被打补丁的那一个领域,还会污染模型据以作答的输入,一旦声明路由成为权威必须删除。凡是“一次运行对同一文本发出多次不同领域调用”的形态,测试必须带上产品真实发送的计划元数据并逐领域断言执行路由等于声明领域,否则测试会用一段刚好自洽的文本掩盖矛盾(本 bug 正是如此漏过)。
- 相关记录:BUG-257、BUG-256、BUG-255
BUG-260 | React Compiler 无法接管 page.tsx 的 Home:编译器静默拒编 2730 行组件且不报任何错
- 状态:won't fix(本轮不修;Next 已升到 16.3.1 并保留,编译器配置已回滚,原因见下)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/主对话页frontend/src/app/page.tsx的重渲染性能,以及后续任何「靠 React Compiler 免除手写记忆化」的计划。 - 用户现象:无终端用户可见现象。对维护者而言的现象是:
next.config.ts里reactCompiler: true开启后构建打印✓ turbopackRustReactCompiler、退出码 0、测试全绿,看起来完全成功,但Home一个函数都没被优化。这个失败不产生任何警告、错误或日志,只看构建输出无法察觉。 - 根因:本条所有行号与槽数均测于交付基点
e8d201dd;page.tsx此后仍在演进(交付时远端已改到 3719 行),复现时应按函数名而非行号定位。page.tsx当时有 3738 行,其中export default function Home()单个函数占 2730 行(1002–3738),带 24 个useState、18 个useEffect、0 个手写useCallback/useMemo。Next 16.3.1 的 Rust 版 React Compiler 会正常编译同一文件里的其他函数,却拒编Home:默认infer模式下全项目 44 个函数拿到缓存槽(app-sidebar112 槽、use-birth-time-guided-journey84 槽、sidebar-session-row73 槽等),page.tsx内部两个小组件(原始行 760、815)也拿到了 11 和 24 槽,唯独Home的生成代码仍以裸useState序列开头、没有_c(N)前导。把compilationMode设为all(绕过组件识别启发式、强制编译每个函数)后page.tsx被编译函数从 2 涨到 47,Home依然不在其中——既然all模式下不存在「未被识别为组件」,只剩一种解释:编译器尝试了Home并失败,然后按panicThreshold默认值none(官方文档原话 "skips components which cannot be compiled")静默跳过。具体原因经授权后已定位(panicThreshold: "all_errors"在 Rust 版下形同虚设,设了也不报错,因此改用临时安装babel-plugin-react-compiler并挂logger钩子的方式取诊断,诊断完已卸载):Home的失败全部来自编译器自身未实现的语法与内部断言失败,没有一条是本仓库代码写错。 稳定版babel-plugin-react-compiler@1.0.0对Home报 24 条错误,去重后三类,全部带Todo:前缀(React Compiler 用Todo:标记「该语法尚未实现」):13 条Todo: (BuildHIR::lowerStatement) Handle TryStatement with a finalizer ('finally') clause、9 条Todo: (BuildHIR::lowerStatement) Support ThrowStatement inside of try/catch、2 条Todo: (BuildHIR::node.lowerReorderableExpression) Expression type MemberExpression cannot be safely reordered。即编译器当时还不支持try/finally和try/catch内的throw,而Home有 13 个finally和 9 个这样的throw。这 13 个finally做的全是finally该做的事——window.clearTimeout(bootstrapTimeout)、polling = false、rectificationOpenInFlight.current = false、cancellationRequests.current.delete(requestId),以及 8 处setProfileSaving(false)/setAvatarSaving(false)/setCreatingSession(false)/setBirthTimeAssessmentPhase(null)之类的加载态复位;删掉任何一个,try 块抛错时 UI 就永久卡在加载态,是拿真 bug 换假优化。另在更新的0.0.0-experimental-a1856f3-20260507上复测:那 22 条try/finally与throw错误已被上游修好,但Home随即撞上编译器内部断言失败Invariant: Expected all references to a variable to be consistently local or context references(page.tsx:2666,catch (error)的绑定同时被直接使用和被setRequestError((current) => ...)的闭包捕获);在仓库外的副本上把这一处改掉后,又冒出下一个Invariant: [PruneHoistedContexts] Unexpected hoisted function(page.tsx:1837的refreshAccount,被 1626 行的useEffect提前引用)。Invariant:在 React Compiler 的分类里是编译器 bug 而非用户代码违规。Home共 50 个函数声明,其中 6 个被声明前引用(refreshAccount1837/1626、editDeclaredBirthTimeDetails2280/1098、completeGuidedBirthTime2308/1097、openRectificationFromHomepage2515/2373、openRectificationSession2519/1982、handleRectificationProfileIncomplete2531/2457),要满足编译器就得在 2700 行的组件里跨千行重排这 6 个定义,且照上述规律修完还会有下一个内部 panic。诊断顺带交叉验证了产物取证的正确性:Babel 版报告成功编译的两个函数是BirthLocationFields @ 760、ProfileFields @ 815,与先前从 Rust 版构建产物 source map 反查出的两个缓存槽(行 760/815,槽 11/24)逐一对上,两条独立证据互相印证。 - 修复:未修复。
next.config.ts的reactCompiler: true与experimental.turbopackRustReactCompiler: true已回滚到与origin/staging逐字节一致;Next 16.2.10 → 16.3.1 的升级保留(该升级本身独立验证通过,是 Rust 版编译器的前置条件)。本轮共试六种配置全部失败:reactCompiler: true、加panicThreshold: "all_errors"、再加文件顶部"use memo"、compilationMode: "all"、"all"+all_errors、compilationMode: "annotation"+Home体内"use memo"。其中compilationMode: "all"还会直接把构建搞坏:它会编译模块作用域的普通回调,birth-time-intake.tsx:22的Array.from({length:24}, (_, index) => ...)被插入useMemoCache,预渲染/时抛TypeError: Cannot read properties of null (reading 'useMemoCache')。注解模式(领导指定的退路)反而最差:全项目 0 个缓存槽,连原本能编的 44 个也停了。 - 验证:
npx tsc --noEmit无输出;npx eslint0 error(4 个既有 warning 在未改动文件里);npx next build退出码 0;非数据库套件 199 个文件 1592 条全绿、fail 0 skipped 0 todo 0(与开工基线一致)。取证方式:Home在产物里被压缩改名,function Home(搜不到,改用 source map 反查——解.next/server/chunks/ssr/*.js.map的 VLQ mappings,把每个缓存槽_c(N)(压缩后真实形态是(0,X.c)(N))归属回原始文件与行号。反向验证:只加/删page.tsx顶部一行"use no memo",page.tsx被编译函数数在 2 与 0 之间可见切换,且切换只影响page.tsx、其他文件槽数不变,证明取证方法不是恒为真;两种状态下都判定Home未编译。构建代价(各测 4 次取最快,同机噪声大):开启前 36.65s / 关闭后 35.11s,差异落在噪声内,Rust 版没有可测量的构建变慢;/首屏 JS gzip 470.1 KB → 481.9 KB,+12031 B / +2.50%,低于 5% 阈值。 - 待跟进:其一(已完成,结论见根因):定位
Home的失败原因已获授权并完成,答案是上游编译器缺陷,不是本仓库代码问题。同时用同一套诊断量化了「换编译器版本能否提高覆盖率」这个问题,答案是不能:对frontend/src全部 373 个文件跑批量诊断,稳定版 1.0.0 与 experimental 版编译成功的函数数完全相同,都是 134 个,失败文件也都是同样的 21 个,只是报错事件从 74 降到 48——被上游修掉的那些错误类别,所在函数都还有别的拦路错误,所以一个函数都没多编译出来。因此换 Babel 版或换更新版本都买不到任何东西,babel-plugin-react-compiler诊断完即卸载(npm ci从锁文件权威还原,git diff为空),不进交付。复现诊断的方法:临时npm i -D babel-plugin-react-compiler,用@babel/core的transformSync配parserOpts.plugins = ["jsx", ["typescript", {isTSX:true}]]单独跑该文件,给插件传logger: { logEvent(file, event) {} },event.kind为CompileError的即为 bailout,event.detail.message是原因。其二,是否为了那 44 个能编的组件而保留reactCompiler: true:它们大多在/上渲染(侧边栏、会话行、日期选择器、三个引导 hook),收益真实但本轮未测量,代价是首屏 +2.5%;本轮按止损条款选择回滚,这个取舍留给决策。其三,若要让Home真正受益,最可能的路是把它拆成若干个小组件——但那是业务代码重构,本轮明令禁止碰frontend/src/**。诊断结果还把这条路的性质说清了:为迎合编译器去改Home(删finally、重排 6 个提升函数、逐个绕内部断言)是被编译器 bug 牵着走的打地鼠,每一步都拿确定的正确性换不确定的记忆化收益,不该做;真正值得做的是按职责把Home拆小,那样每个小组件天然落在编译器能处理的范围内,同时也解决可维护性问题。 - 防复发:「配置开启 + 构建绿」绝不等于「React Compiler 生效」。编译器放弃某个组件时不报错、不警告、不留日志,这是它的默认行为(
panicThreshold: "none")而非缺陷。任何启用 React Compiler 的改动都必须在构建产物里定位目标组件、确认存在编译器注入的缓存槽,并用"use no memo"做一次反向验证证明取证方法会随编译状态变化;只贴构建退出码等于没验证。另外"use memo"/"use no memo"是函数体内的指令,写在文件顶部时"use no memo"可作整文件退出、但"use memo"不构成整文件加入。 - 相关记录:BUG-260 无前序同类记录。本记录三次改号:初次写作取 255;第一次 rebase 到
origin/staging(c8d9ec64)时远端已占 254–255,改 256;第二次 rebase 到e1db5762时远端又占到 259,改 260。每次都按 BUG-254 的防复发要求处理——标题与远端各记录均不同,故另分新号而非合并。BUG-253 所记的抢号失效模式在同一轮交付里连续复现两次,暴露出「追加前检索最大编号」这条措施的边界:它只在写记录那一刻成立,防不住推送前远端继续前进。可靠做法是把定号推迟到推送前最后一次 rebase 之后。 - 修复版本:本地未提交候选
BUG-261 | staging web 镜像把 skills/ 拷两遍,符号链接撞上被解引用的同名目录
- 状态:resolved
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
deploy/railway-web.Dockerfile的镜像构建,即 staging 与生产的 web 镜像产出。构建期缺陷,不影响已在运行的实例。 - 用户现象:无终端用户可见现象。对维护者而言:
docker build -f deploy/railway-web.Dockerfile在最后一步失败,ERROR: failed to build: failed to solve: cannot replace to directory /var/lib/docker/buildkit/containerd-overlayfs/cachemounts/<id>/app/skills/jyotish-vedic-astrology/assets with file,指向COPY --from=build /app/skills /app/skills。危险之处在于它依赖 BuildKit 实现才会暴露:Gitea/GitHub runner 上一直构建成功,本地 Docker 29.1.3(containerd-overlayfs 快照器)必定失败,因此这是一颗按 runner 环境触发的定时炸弹,而不是一个稳定可见的错误。 - 触发条件:用一个不允许「以文件覆盖已存在目录」的 BuildKit 快照器构建该 Dockerfile。与 Next 版本、CPU 架构均无关:在升级前的基线提交(
next16.2.10)与 amd64/arm64 两种平台上都能复现同一条错误。 - 根因:同一份
skills/以两种互不兼容的形态进了最终阶段。skills/jyotish-vedic-astrology/assets是仓库里 git 跟踪的符号链接(-> ../../assets)。frontend/src/lib/skill-package-registry.ts存在动态文件访问,Next 构建期打印Dynamic filesystem access ... causes tracing of the whole project,于是文件追踪把skills/整棵树带进.next/standalone/,并在拷贝时把符号链接解引用成真目录。最终阶段第一步COPY --from=build /app/frontend/.next/standalone /app先把这份解引用产物落到/app/skills,随后第 36 行COPY --from=build /app/skills /app/skills又要把源码树里的符号链接放到同一路径上——用文件覆盖目录。两行 COPY 相隔 5 行、意图并不冲突,冲突完全由中间那层不可见的文件追踪行为造成。 - 修复:在构建阶段
npm run build之后加一行RUN rm -rf /app/frontend/.next/standalone/skills,删掉追踪产物里的那份,让显式COPY skills确定性地独占该路径。没有改动两行 COPY 的顺序或语义,也没有改outputFileTracingIncludes:/app/assets仍由 standalone 提供,而它是outputFileTracingIncludes为/api/consult显式声明的(../assets/**/*),不是追踪的附带产物,因此符号链接照旧可解析。 - 验证:修复后完整构建镜像成功(
naming to docker.io/library/jyotisha-web:fixed done)。进镜像逐项核对:/app/skills下 1208 个文件、assets仍是lrwxrwxrwx ... -> ../../assets且ls能列出其中文件、/app/frontend/server.js存在。运行时冒烟:容器启动打印▲ Next.js 16.3.1,GET /返回 200(日志中的SupabaseConfigurationError是未注入环境变量所致,属预期)。对照实验确认与本批 Next 升级无关——在升级前的3371baca(next16.2.10)上用同一条命令构建,在同一步报同一条错误。 - 待跟进:本条没有自动化回归测试,与 BUG_HISTORY 工作流对
resolved的要求存在缺口,此处如实标注而非掩盖。原因是复现该缺陷必须真的构建镜像(本机约 16 分钟),放不进npm test。可行的折中是在tests/health-deployment.test.ts加一条结构断言:若 Dockerfile 同时存在「拷贝 standalone 到/app」与「显式拷贝skills」两行,则必须存在删除追踪副本的一行。这是结构守卫而非行为回归,能防住这一行被顺手删掉,未在本轮加入。更彻底的方向是消除skill-package-registry.ts的动态文件访问以恢复精确追踪,那属业务代码改动,另开一轮。 - 防复发:Next 的输出文件追踪会把项目文件带进
.next/standalone/并把符号链接解引用成真目录;任何 Dockerfile 若既拷 standalone 又显式拷同名目录,两者就在争同一路径,必须显式指定谁赢,不能靠 COPY 顺序碰运气。仓库内 git 跟踪的符号链接(如skills/*/assets、skills/*/references、skills/*/scripts)是这类冲突的固定诱因。更普遍的教训:镜像构建在 CI 上绿不等于 Dockerfile 正确,凡涉及符号链接与目录同名的 COPY,判定必须落到「换一个 BuildKit 快照器是否仍然成立」。 - 相关记录:BUG-261 无前序同类记录。与 BUG-260 同批交付但成因无关,是为验证 BUG-260 的 Next 16.3.1 升级而首次在本地构建 staging 镜像时暴露出来的。编号顺延过程见 BUG-260 的相关记录。
- 修复版本:本地未提交候选
BUG-262 | staging 镜像内 posthog-node 的 Node 引擎要求高于基础镜像实际版本
- 状态:investigating
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:staging 与生产 web 镜像内的
posthog-node@5.41.0(产品分析上报)。 - 用户现象:目前无任何可见现象。仅在镜像构建日志里表现为一条 npm 警告:
npm warn EBADENGINE package: 'posthog-node@5.41.0', required: { node: '^20.20.0 || >=22.22.0' }, current: { node: 'v22.15.0', npm: '10.9.2' }。 - 触发条件:构建
deploy/railway-web.Dockerfile。node:22-alpine当前解析到 Node v22.15.0,而posthog-node@5.41.0声明需要^20.20.0 || >=22.22.0,v22.15.0 落在两个区间之外。 - 根因:尚未确认实际影响面。已确认的事实只有两点:一是版本区间确实不满足,二是与本批 Next 16.3.1 升级无关——
git diff 3371baca 37938187 -- frontend/package-lock.json中没有任何posthog相关改动,且该警告在升级前的基线构建日志里就已出现。尚未确认的是posthog-node是否真的用到了 Node 22.22 才有的 API:EBADENGINE只是警告、不阻断安装,npm 不会因此拒绝装包,所以风险形态是「构建期无声、运行期在某个具体调用上抛错」,而不是构建失败。 - 修复:未修复。两条候选路径的代价都超出本轮范围:升基础镜像到
node:24-alpine会引入一整片新的验证面(原生模块需重编、sharp的 musl 二进制需重新确认);降posthog-node需确认低版本是否仍满足现有调用。 - 验证:不适用(未修复)。已核实的事实见根因。
- 待跟进:先确定这是真风险还是纯噪声——读
posthog-node@5.41.0的 changelog/引擎声明来源,确认它把下限提到>=22.22.0是因为用了新 API 还是仅仅是维护者的支持策略。若属后者,本条可降级为「已知噪声」并就地记录,不必改依赖。 - 防复发:镜像构建日志里的
EBADENGINE不得当作噪声跳过——它意味着依赖声明的运行环境与镜像实际提供的不一致,而 npm 不会阻止这种安装,因此它是少数「构建期唯一一次提示、之后只会在运行期爆发」的信号。基础镜像用浮动 tag(node:22-alpine)时,实际 Node 版本会随上游重建漂移,依赖的引擎下限也会随升级上移,两者相向移动,这类不匹配会反复出现。 - 相关记录:BUG-262 无前序同类记录。与 BUG-261 同为首次本地构建 staging 镜像时暴露的构建期问题。
- 修复版本:未修复
BUG-263 | “跳到最新”按钮悬在输入框上方约 160px,压住正文而不是贴着输入框
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/主对话页在用户向上翻阅历史时出现的“跳到最新”控件。 - 用户现象:按钮既没有贴着下方输入框,也没有落在空白处,而是悬在正文中间挡住一整行回答文字;视觉上不像输入框的附属控件,更像漂在内容上的异物。
- 触发条件:会话已有消息,用户向上滚动超过 96px 锚定阈值。桌面端偏移约 160px,移动端约 128px。
- 根因:按钮此前是
.conversation(滚动容器)的position: sticky; bottom: 12px子元素,而.conversation带padding-bottom: var(--composer-reserve)(桌面 148px / 移动 116px)。sticky 元素被钉在视口底部之前,会先被自己的包含块——也就是滚动容器的 内容盒——夹住,而内容盒底边正好比容器可视底边高出这一段 padding。于是bottom: 12px从未生效,按钮实际停在 padding + 12px 处。这段 padding 是早期输入框覆盖式布局的遗留:现在.chat-panel用grid-template-rows: 68px minmax(0,1fr) auto,.composer-wrap已是独立行、不再覆盖对话区。 - 修复:把按钮从滚动容器移到
.composer-wrap内部,改用absolute inset-x-0 bottom-full+pb-3,让它挂在输入框区域自己的上边缘。.composer-wrap本就是position: sticky(定位元素)且z-index: 2,可直接作为包含块,无需新增定位上下文。这样偏移不再依赖--composer-reserve,输入框长高、推荐问题行出现或消失时按钮都跟着走。按钮自身的可访问性属性(真实<button>、aria-label、焦点环、44×44 触控区、pointer-events-auto)原样保留。 - 验证:新增合同测试固定新位置(断言控件位于 composer 块内、使用
bottom-full、不再出现sticky),并把 BUG-218 / BUG-252 两个文件里按缩进切片的定位锚点改成与缩进无关;两文件 23 条断言全绿。几何结论这次经过浏览器实测:用一份复刻.chat-panel/.conversation/.composer-wrap真实规则的静态页面同时渲染新旧两种写法,量得旧写法距输入框 160px、新写法 11px,与线上截图相符;量完即删,未留在仓库。tsc、eslint清洁。未做的验证:没有在真实登录会话里目视确认(需要长对话与账户),复刻页面只覆盖了本条涉及的布局规则。 - 防复发:滚动容器留了
padding-bottom时,它的position: sticky子元素永远无法贴到容器可视底边——sticky 受包含块内容盒夹持,调bottom偏移不解决问题。悬浮在输入框上方的控件应挂在输入框容器上(bottom: 100%),而不是挂在滚动容器里,这样才不依赖任何预留高度常量。另:BUG-218 当时已写明“浏览器内的视觉位置未经人工目视确认”,本条正是那句话对应的实际后果——纯源码合同测试能固定 DOM 与属性,固定不了几何位置,涉及定位的改动必须实测。 - 相关记录:BUG-218(引入该按钮与锚定逻辑)、BUG-252(曾把它记作 BUG-217 新增,并留下焦点丢失的待跟进项,本轮未处理)
- 修复版本:本地未提交候选
BUG-264 | 初始化填报准确出生时间后,第一条咨询必定 409:客户端按提交的声明选路,服务端已把它升级为 accepted
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/初始化流程的出生时间与出生地点保存、PATCH /api/account、POST /api/consult的出生真值校验。 - 用户现象:新用户在初始化里选择“我知道准确出生时间”并填到分钟,走完地点一步进入首页,发出第一条咨询即被拒绝,提示“出生时间状态已经变化 / 请刷新后重新选择使用填报时间、一般咨询或先完成校正”。刷新页面后同一条问题可以正常发出。
- 触发条件:声明为
family_exact且误差为 0,经账户接口保存后不刷新页面直接发第一条咨询。 - 根因:账户写入会在服务端派生出生真值——零误差的准确时间被直接采用为
active_birth_time且状态升级为accepted——但PATCH /api/account只回{ok:true},页面又用提交的草稿覆盖profile,草稿里状态仍是reported、time为空。于是客户端按未确认分钟选unverified_birth_time,而咨询接口的未确认分支明确拒绝accepted,在扣点前抛出mode_changed返回 409。派生规则写在服务端、客户端却各自推断同一件事,是这次不一致的入口。 - 修复:
PATCH /api/account随写入成功返回它派生的出生真值(状态与当前排盘分钟),新增resolveAppliedAccountBirthTime负责这一派生;persistProfile返回按该真值对账后的档案,初始化出生时间、初始化地点、账户弹窗保存和默认星盘切换四条保存路径统一采用返回值,不再沿用本地草稿。服务端的真值校验保持严格,不为客户端的过期视图放宽。 - 验证:
account-api.test.ts锁定派生结果(零误差准确时间→accepted+分钟;改为时段声明→回落reported;已确认与 legacy 分钟不被覆盖;纯改名不产生状态);birth-time-intake.test.ts锁定客户端对账(含HH:mm:ss、未知状态和缺字段时不动草稿);profile-persistence.test.ts断言每条保存路径都采用返回档案,禁止回退到setProfile(profileDraft)。npm test1710 项中 1709 通过,唯一失败是并发跑database-*postgres fixture 的既有抖动,单独串行复跑通过;tsc --noEmit与目标文件 ESLint 清洁。未做的验证:没有在 staging 真实新用户流程里目视复跑一遍,需要一个未初始化的账户。 - 防复发:出生时间状态与当前排盘分钟由服务端唯一派生,客户端只能采用接口返回值;任何新增的档案保存路径都必须消费
persistProfile的返回档案,源码合同测试会拦住用本地草稿覆盖profile的写法。咨询选路不得从未落库的草稿推导。 - 相关记录:BUG-018(首次保存未写入档案状态,同一类真值不一致)、BUG-017(
mode_changed的另一入口) - 复发自:无
- 修复版本:本地未提交候选
- 2026-09-29 产品修订(
TASK-mobile-chart-and-confirmed-edit-20260929M2,不是本 Bug 复发):原约定「已确认与 legacy 分钟不被普通资料修改覆盖」改为——只有声明字段(出生日期、时间与来源、时段 / 范围、地点代码与坐标、时区)真的变了,已确认或 legacy 分钟才被丢掉,之后按新声明派生,与未确认状态完全一致(零误差家人时间 →accepted+ 新分钟,否则reported,rectification_case_id清空);同时清掉图表会回落读取的遗留birth_time,以及跨午夜采用三列active_birth_date/active_birth_timezone_offset/active_birth_provenance(数据库触发器zz_guard_adopted_birth_date也会清)。只改称呼、岁差、性别、头像不动已确认分钟。影响:用户改出生资料会让已采用的生时校正结果作废,这是产品要的(「用户改生日肯定是得到信息了」)。同时把声明比较改成按值比较(库里的HH:MM:SS与表单提交的HH:MM、数字与数字字符串、空串与 null 视为相同),否则/people每次整表重提都会被误判为新声明。account-api.test.ts与chart-profile-update-consistency.test.ts里锁旧约定的断言按三栏改写,新增一条「confirmed 分钟只跟随新声明」测试。数据库侧:guard_birth_time_journey(最新版 20260804020000)只在状态为空且有分钟时补confirmed,本修订总是写明确状态,不会被改回;zz_guard_adopted_birth_date、agentic_rectification_profiles_rebaseline_guard、invalidate_agentic_rectification_results_on_profile_change在声明变化时照旧触发。本机无 Docker,未跑npm run test:db,staging 实测见真机清单。
BUG-265 | 首页“今日星语”号称个人化,实际是 4 张写死卡片轮换
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:
/首页在出生资料完整时展示的“今日星语”入口卡片。 - 用户现象:卡片标题是“今日星语”、位置紧挨生时校正入口,读起来像是基于本人星盘的当日解读,但同一个人换个日期、不同人同一天,看到的往往是同几句话。
- 触发条件:任何完成资料的账号打开首页。
- 根因:
/api/daily-starlanguage从来没有接过模型。顺利时走transitBackedCard,它确实调了/api/chart与/api/transit,但只把total_triggers填进一句固定模板,action与caution是常量;引擎 2.5 秒超时或资料不全就走pickCard,用「日期+生日+省市」的字符码之和对 4 取模选一张写死卡片。page.tsx还把同样的 4 张卡抄了一份作为客户端兜底,因此即便接口整个挂掉,界面也照样显示得像有内容。 - 修复:改成 Agent 生成。新增
getDailyStarlanguageAgent(frontend/src/mastra/index.ts)与纯函数模块frontend/src/lib/daily-starlanguage.ts(证据摘要、模型输出解析、缓存键)。路由先取/api/chart,再并行取/api/dasha(Vimshottari 与 Narayana 双轨)、/api/varga_full(D9/D10)、/api/transit,把结果压成一份紧凑证据交给 Agent,只接受完整的{trend, action, caution}JSON。生成前要求已登录会话(避免匿名烧 token),按「账号+出生载荷+日期」在进程内缓存当天结果,并用 in-flight Map 合并并发请求。前端与路由里那 4 张写死卡片全部删除。 - 验证:新增
frontend/tests/daily-starlanguage.test.ts7 条:证据抽取覆盖 Vimshottari 当前 MD/AD、按今天选中的 Narayana 期、D9/D10、过境触发与功能吉凶星;引擎层缺失时必须显式列进missingLayers;status: blocked的功能吉凶层不得进入证据;未确认的出生时间必须写成硬约束进提示词;模型输出缺字段或过短一律拒收;缓存键不得跨账号、跨资料、跨日期复用。另有源码合同断言鉴权、缓存键、双轨 Dasha 调用与「不留写死卡片」。tsc、eslint清洁,next build通过,非数据库套件 1630 条中仅rectification-pr4-database因本机 Docker 环境失败(与本次无关)。未做的验证:没有真实跑通一次线上生成——本机没有 Python 引擎与模型密钥,Agent 实际产出的措辞、耗时与失败率均未观测。 - 待跟进:内存缓存随进程重启失效、且多实例不共享;若上线后发现重复生成成本明显,需要改成落库(要新迁移,staging 迁移是手动工作流)。另外
/api/daily-starlanguage现在最长可能跑满 60 秒,首页首屏会看到“正在结合你的星盘写今天的星语”的等待态,是否需要预生成尚未决定。 - 防复发:任何号称个人化的界面文案,其数据来源必须能追到当次计算或当次生成;用哈希在固定文案池里取模不是个人化,只是伪装成个人化。降级路径不得复制一份“看起来正常”的内容——生成失败时必须让用户看出没有生成成功,本次改成明确的一句话降级文案而不是通用建议。
- 相关记录:BUG-265 无前序同类记录。与 BUG-263 同批,均为首页可见问题。
- 修复版本:本地未提交候选
BUG-266 | staging 连续 5 个提交发不出去:质量门跑在跳板机上,把那台机器的磁盘占满,PostgreSQL fixture 全线起不来
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
.gitea/workflows/全部 12 个 job 的 runner 归属、staging 质量门的数据库集成测试、publish与Deploy staging的放行,以及跳板机上与本项目无关的 Nacos/MySQL/Redis/xxl-job 服务。 - 用户现象:推到
staging的提交一个都没上线。https://staging.jyotisha.chat/api/health长时间停在561010f2,而origin/staging已经前进了 4 个提交;Gitea 上validate连续 5 次 failure、publish连续 5 次 skipped。 - 触发条件:向
staging推任何提交。与提交内容无关。 - 根因:两层。其一,
manman-linuxrunner 以manman-linux:host注册在跳板机上,job 直接跑在该机的宿主文件系统里,而质量门每次 push 都在本地 build 两个按 SHA 打标的镜像(api-<sha>、web-<sha>)且全流程没有任何镜像与构建缓存回收,累积到 1453 个镜像、根分区 99G 用满 94G、Avail 归零,于是docker compose up -d --wait postgres阶段initdb直接No space left on device,23 个数据库测试级联失败。这台机器同时还跑着与本项目无关的生产服务,等于用别人的磁盘做构建。其二,当初把门禁从xiaoxin搬到跳板机的理由(ERR-103「xiaoxin 缺少 Docker Compose v2」)是误诊:xiaoxin的 Docker Engine 一直正常,真正的原因是/root/.docker/cli-plugins/docker-compose有一个 2026-08-05 建立的零字节文件,用户级插件目录优先级高于系统目录,把系统里正常的 compose 插件遮蔽成exec format error。误诊导致门禁被搬到一台不该承担构建的机器上,磁盘耗尽只是时间问题。 - 修复:删掉
xiaoxin上那个零字节插件占位文件并补装docker-compose-v2(2.40.3),确认docker compose version --short与--project-name两项门禁能力检查通过;把.gitea/workflows/里 8 个仍指向manman-linux的 job 全部改为xiaoxin(该机 20 核、61G 内存、877G 空闲);新增deploy/reclaim-runner-disk.sh,在validate与publish的 checkout 之后、拉取 Node 工具之前回收磁盘,并在空间仍不足 10 GiB 时带着原因 fail-closed,而不是让 23 个数据库测试去暴露磁盘问题。回收只针对没有任何容器持有的资源(container prune --filter until=6h、name=^jyotisha-postgres-且dangling=true的卷、network prune --filter until=6h、悬空镜像、buildx 缓存,以及除当次 SHA 外的历史api-/web-标签),因此不会掀掉同一台 runner 上并发 job 的 fixture。 - 验证:
staging-backend-workflows.test.ts38 项通过,其中新增两项——遍历.gitea/workflows/*.yml断言每个runs-on都是xiaoxin(并禁止manman-linux重新出现),以及断言两个 job 都在 Node 工具之前调用回收脚本、脚本只回收未被持有的资源且低于阈值时 fail-closed;回收脚本纳入既有 shell 语法校验。tsc --noEmit与该测试文件 ESLint 清洁。在xiaoxin上实测:compose 2.40.3 通过门禁的两项能力检查,且到 Gitea、ACR 镜像仓库、staging 主机 22 端口、npm/pypi/ECR/SWR 镜像源的出网全部可达。未做的验证:回收脚本没有在真实 runner 上跑过一次(当前xiaoxin有 877G 空闲,回收逻辑与阈值分支要等首次门禁运行才被真正执行);跳板机上那 94G 与仍在运行的act_runner尚未清理下线。 - 防复发:构建与测试不得跑在承载其他服务的机器上,也不得以
:host模式共用其文件系统。质量门在开跑前自己保证磁盘余量,并把「磁盘不够」报成一句明确的失败,而不是让下游 fixture 去替它失败。runner 能力缺失必须定位到具体原因再决定搬迁:docker compose不可用时要先查docker info的 client plugins 与用户级~/.docker/cli-plugins遮蔽,不能直接判定整台机器不支持 Compose——ERR-103 正是漏了这一步,代价是把门禁搬到错误的机器上并最终堵死发布。 - 相关记录:ERR-103(
docs/research/pre_work_error_ledger.md,同一 Compose 现象的误诊,本次给出真实根因)、ERR-105(同一台跳板机磁盘耗尽的基础设施记录)、BUG-264(本次被卡住无法发布的修复) - 复发自:无
- 修复版本:本地未提交候选
BUG-267 | 咨询证据门的三处判据都没接到权威来源:婚姻/财富的必需层从未被检查,领域边界靠猜关键词,精确应期无条件放行
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
scripts/jyotish_api_server.py的_build_consumer_context(),即所有产品咨询(web/api/consult)与 MCP/研究路径共用的回答真相合同。直接影响 10 条路由中 7 条的证据门、6 个领域的解读边界,以及全部路由的精确应期许可。 - 用户现象:手测 staging 时,一次婚姻方向的咨询回执报
status: "ready"、missingLayers: []、preciseTiming: "allowed",看上去证据齐备;但同一次回答里既没有 Upapada(UL)层的任何结论,也没有性别解读边界。回执的「齐备」与回答的内容互相矛盾,而回执本身不含任何可据以追问的线索。 - 触发条件:任何走
marriage、wealth、health、education、migration、family、annual路由的咨询(即除career、timing、general外的全部路由);以及任何问题文本没落在七条关键词正则里的领域提问。 - 根因:同一个函数里三处判据各自接错了来源。其一,
route_requirements的键写成relationship与finance,而_ROUTE_DEFINITIONS里的路由名是marriage与wealth,.get(route, general)于是静默把这两条路由降级成general的门(D1/D9/dasha_boundaries),婚姻的 UL 与财富的 D2 从未进入检查;另有 5 条路由(health/education/migration/family/annual)压根没有条目,同样落到general。missingLayers: []因此不表示证据齐备,只表示没检查过。其二,7 个领域上下文层与解读边界由 7 块re.search匹配问题文本决定,而领域此时已经由模型声明并写进了route_packet——用文本再猜一遍既是重复,又只能覆盖关键词表里的写法:本次实测中一句明显是情感取向的问题因为写作「情感」而非表里的「感情」,在marriage路由上完全没拿到性别解读边界。其三,timing_layers_ready判断dasha_boundaries与narayana_dasha是否就绪时读的是missing_route_layers,而该列表只包含本路由要求的层,于是对任何不要求narayana_dasha的路由恒为真,can_answer_precise_timing在该层根本没算出来时也照样放行。三处的共同形态是:判据没有落在权威来源(路由表、路由声明、证据 section)上,而是落在错键、文本猜测和一个恰好为空的列表上,因此全部失败方向都是「静默放宽」。 - 修复:把三处判据都接回权威来源。
_ROUTE_REQUIRED_LAYERS提为模块级常量并对_ROUTE_DEFINITIONS的 10 条路由逐条显式列出,不再依赖general兜底;表里每个层名都限定为证据包真实构建的 section(因此wealth只要求 D2 而不要求引擎当前不产出的 D11,migration要求确实产出的 D4),避免把状态整体推成degraded。领域上下文层与边界改为_ROUTE_DOMAIN_CONTEXT按 route 查表,7 块正则整体删除。出生时间不确定边界改从矫正闸门的真实状态派生(effective_accuracy不在精确档位,或lagna_boundary.is_sensitive),不再等用户把「出生时间不准」说出来;档位缺失按不确定处理——不知道精度不等于已确认精度。timing_layers_ready改为直接读sections的status,与路由要不要求该层无关。唯一保留文本探测的是precise_timing_requested:「用户有没有要一个具体时间」是服务端没有权威来源的信号,因此改成 timing/annual 路由结构性携带、文本探测仅作叠加,一次措辞漏判不再能把信号清零。 - 验证:
tests/test_consultation_consumer_context.py新增 8 条并全绿(该文件共 16 条通过)。8 条覆盖:遍历_ROUTE_DEFINITIONS断言每条路由都有自己的证据门条目(这是本该拦住本 bug 的守卫),且表里每个层名都能在真实证据包的 sections 里找到;marriage缺 UL 必须报degraded且missingLayers == ['UL'];wealth缺 D2 同理;性别边界在不含任何关键词的措辞下仍随路由挂上,且不串入其他领域的边界;出生时间边界在「问题完全没提出生时间但精度为 1hour」时出现、在「精度 minute 且 Lagna 不敏感」时不出现;精度档位缺失按不确定处理;minute档位下 Lagna 敏感仍保留边界;marriage路由在narayana_dasha缺失时can_answer_precise_timing必须为 false(此时missingLayers仍为[]、状态仍为ready,正是旧代码放行的那个组合)。已逐条验证这 8 条在旧代码上会失败:旧表下marriage/wealth的门确为['D1','D9','dasha_boundaries'](UL、D2 均不在内),旧正则对该措辞返回 False,旧timing_layers_ready在narayana_dasha缺失时仍为 True。未做的验证:没有在 staging 上真实跑一次婚姻类咨询复看回执,因此「回执与回答不再矛盾」只有单测证据,线上措辞与耗时未观测。 - 待跟进:其一,
birth_time_rectifier.get_effective_accuracy()会返回'5min'(声明 minute + 家人清楚记得),而ACCURACY_MATRIX没有'5min'这一行,get_enabled_vargas()于是落到unknown档——比声明15min更差。本轮只让'5min'在边界判定上按不确定处理(方向正确),没有补这一行矩阵,分盘可用性仍被低估。其二,jyotish_api_server.py约 7293 行处(穆胡尔塔领域选择)仍有一处同形态的关键词匹配,本轮未动。其三,projectEvidenceContract()的投影范围仍不含deterministic_claims_forbidden_for(见 BUG-256 待跟进),本轮未改投影层。 - 防复发:查表取合同时不得用
.get(key, 默认值)静默兜底——本次三处缺陷里有两处都是「取不到就用一个更宽松的值」,而更宽松的失败方向不会有任何人报错。凡是按路由/领域分派的表,必须有一条测试遍历权威路由集合断言逐条覆盖,并断言表里引用的层名在运行时真实存在;键名与权威定义分处两个文件时,这条测试是唯一能发现键名漂移的机制。已经由模型声明的语义(领域、路由)不得在服务端用文本正则重新推断一遍:重复推断不会更准,只会多出一处静默失效点。判断「某层是否就绪」必须直接读该层的状态,不得借道任何按条件裁剪过的列表——missing_route_layers这类列表为空既可能是齐备也可能是没检查,两者不可区分。 - 相关记录:BUG-259(同一函数上游的路由分歧,本条是「路由定了以后合同没跟上」)、BUG-256(同为回答契约投影/合并层的缺口,其待跟进项与本条同源)、BUG-268(本条的可观测性对照:回执里同样查不到模型有没有读方法)、BUG-270(修完本条核对同一张表时发现门仍比产品声明松三处)
- 复发自:无
- 修复版本:
10ae149c(staging)
BUG-268 | 模型有没有真的翻开方法文档,运行结束后无处可查:计数器数完就丢
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
/api/consult的公开回执AgentExecutionReceipt与服务端可观测事件;影响「web 输出为何不如本地 Agent」这一类问题的可诊断性。 - 用户现象:用户对比本地 Agent 与 web 的输出质量,web 明显更弱。但两份成功回执里能看到的只有
skill.loaded: true、steps: [skill, tool]与stepBudget.used: 2,无法回答「模型到底有没有读过方法文档」——而这正是两条链路最可能的差异所在。 - 触发条件:任何一次咨询运行结束后试图回溯模型的方法使用情况。
- 根因:Mastra 的
skill工具返回 SKILL.md 的全文指令加上 references/scripts/assets 的文件名清单,真正打开某份参考文档要另调skill_read。createConsultationRuntimeHooks确实在afterToolCall里数了skillReferenceReadCount,但这个计数器既没进公开回执,也没进agentObservabilityEventSchema,数完即丢。同时skill_read按设计不记成 runtime step(只有skill/tool/validation三类会记),所以steps与stepBudget.used天然看不见它;服务端日志里唯一能间接反映的是modelStepCount。结果是:唯一能直接回答该问题的数字被算出来后丢弃,只留下一个需要推断的替代量。 - 修复:把
referenceReads加进回执的skill对象,并设为必填而非可选——正是「可选且没人填」让这个数字消失的,必填能让将来任何一处新的回执构造点无法再省掉它。同时把skillReferenceReads加进consultationModelStepTelemetry(),它已被展开进可观测日志,因此服务端日志自动与modelStepCount并列拿到该值。两个构造点(工具可选路径与强制工具路径)都改为从state.skillReferenceReadCount读取。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts37 项通过,其中新增 1 项:走真实 hooks,断言加载 skill 后计数仍为 0(说明「已加载」不等于「读过方法」)、两次成功的参考读取记为 2、失败的读取不计数、参考读取不出现在 steps 里(因此 steps 无法替代该字段),最后断言回执解析后skill.referenceReads === 2。既有断言同步收紧:consultationModelStepTelemetry()的期望值现在包含skillReferenceReads。tsc --noEmit清洁。未做的验证:没有在 staging 上取一次真实回执,因此线上那两次运行的referenceReads究竟是 0 还是别的值仍未观测——这正是本条要让它可观测的那个数。 - 待跟进:线上真实值已取到(2026-08-18 手测三次,
10ae149c):两次单领域 career 均为2,一次多领域 career+wealth 为0。所以不是「从不读」,而是多领域路径下模型一份方法文档都没打开——恰是最需要方法的那条路径。单样本,尚不能断定必然。下一步应先复跑几次多领域确认是否稳定为 0;若稳定,方向是让方法在多领域计划下仍可达(该路径的工具结果体积大得多,可能挤掉了模型继续读文档的动机),而不是继续加服务端约束。 - 防复发:被数出来的诊断量必须有一个出口(回执或可观测日志),否则等于没数。当某个字段的作用正是「证明某件事发生过或没发生过」时,它在 schema 里应当必填:可选字段缺失与「值为 0」在下游无法区分,而这里 0 恰恰是最需要被看见的答案。另外,不要用
steps/stepBudget.used推断模型的全部动作——这两者只记录被显式登记的三类步骤,模型的其余工具调用在其中不可见。 - 相关记录:BUG-267(同一批手测暴露的另一处静默缺口)、BUG-255(步数预算被浪费,当时也依赖
modelStepCount这一间接量定位)、BUG-258(同为「失败/结束时回执信息不足」的形态) - 复发自:无
- 修复版本:
10ae149c(staging)
BUG-269 | “今日星语”永久停在“正在结合你的星盘写今天的星语”:请求被一个反向的出生时间守卫拦住,从未发出
- 状态:resolved(已发布 staging)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
/首页“今日星语”卡片、首页 hero 的问候与标题、/api/daily-starlanguage的失败归因与超时预算、Onboarding Agent 生成的欢迎语。 - 后续修正:本记录「Agent 欢迎语落到 hero 说明行」的处置已被 BUG-272 推翻——该说明行经用户评审判定为累赘并删除,
onboarding.greeting因此重新回到无渲染点状态,处理见 BUG-272。 - 用户现象:staging 上出生资料完整的账号打开首页,“今日星语”卡片一直显示“正在结合你的星盘写今天的星语。”,永远不出现 Agent 写的文案。同一次反馈里还问到:首页那三个问题是不是写死的,以及首页标题“今天想先理清什么?”为什么没换成登录后按时段问候的形式。
- 触发条件:
birth_time_status为candidate、accepted或confirmed且已有可用出生时间的任何账号打开首页。也就是说,越是资料完整的账号越必然命中。 - 根因:三处独立问题,都在同一屏上。
- 每日星语 effect 的守卫是
if (!hydrated || !profileComplete || birthTimeDisplayState(profile)) return;。birthTimeDisplayState恰好在出生时间可用(candidate/accepted/confirmed)时返回非 null,因此条件的实际含义是「星盘可用时不要请求」,与卡片的渲染条件personalChartAvailable完全相反,请求从未发出,state 永远停在pending。这个守卫在 BUG-265 之前就存在,但那时客户端还有buildDailyStarlanguageCard写死兜底把空状态遮住了;BUG-265 删掉兜底、保留守卫,于是暴露成永久等待态。 /api/daily-starlanguage里/api/chart是唯一没有.catch()的引擎调用(其余四层都有),且engineTimeoutMs只有 8 秒、agentTimeoutMs只有 30 秒。线上实测冷路径端到端 30.6 秒,紧贴 30 秒上限;首次调用在 9.5 秒就返回agent_generation_failed,正是 8 秒引擎超时被当成模型失败上报。失败原因被压成同一个字符串,无法区分是引擎、模型目录还是模型输出。- 首页同时存在三套问候实现:
page.tsx的greetingForHour(hero 第一行)、starter-prompt.ts的 8 条静态随机池(hero 的h1)、onboarding-client.ts的createStartGreeting(按时段+称呼,只用在 onboarding 气泡)。用户要求的按时段问候在第三套里,而 hero 标题读的是第二套,所以「改了却没生效」。同时/api/onboarding返回的 Agent 欢迎语在客户端被greeting: createStartGreeting(presentationName)覆盖,而onboarding.greeting在整个页面里没有任何渲染点,等于 Agent 每次都白写一句欢迎语。
- 每日星语 effect 的守卫是
- 修复:守卫改成
!hydrated || !profileComplete || !personalChartAvailable,与卡片渲染个人内容的条件对齐,并在服务端报 unavailable 时延迟 5 秒重试一次后才落到失败态,卸载时清理定时器。路由给/api/chart补.catch(),把生成结果改成判别联合,失败原因区分chart_unavailable/model_unavailable/agent_generation_failed;engineTimeoutMs提到 20 秒、agentTimeoutMs提到 45 秒,仍在maxDuration = 60之内。问候收敛成一套:createStartGreeting拆出createStartGreetingParts,返回{salutation, question},hero 第一行用 salutation、h1用 question,选中变体在离开首页时重抽;删除starter-prompt.ts与greetingForHour。客户端不再覆盖 Agent 欢迎语,onboarding.greeting落到 hero 说明行并保留原静态文案作为兜底。 - 验证:线上先证明后端是好的——带登录 Cookie 直接调 staging
/api/daily-starlanguage,冷路径 30.6 秒返回真实{trend, action, caution},第二次 1.6 秒命中agent_cache;/api/health确认部署 SHA 就是origin/staging头部,jyotishApi检查为 ok,因此排除部署落后与引擎不可用。新增 4 条回归:每日星语请求条件必须与personalChartAvailable一致且源码中不得再出现birthTimeDisplayState(profile)、失败必须重试一次且清理定时器、/api/chart必须带 catch 且三种失败原因各自可辨、两个超时预算有下限断言;hero 断言 salutation/question 拆分与 Agent 欢迎语落点,并禁止starterPrompt/createStarterPrompt/greetingForHour复活。前端非数据库套件 1711/1724 通过,13 个失败全部是本机 Docker/PostgreSQL fixture(与 BUG-265 同一类环境失败,与本次无关);tsc --noEmit与改动文件 ESLint 清洁。未做的验证:没有在浏览器里看过修好后的首页——本机没有 Python 引擎与模型密钥,无法起完整栈,卡片从pending到ready的实际观感、重试是否够用、以及 hero 换行后的排版都要等 staging 发布后确认。 - 防复发:一个界面元素的「取数条件」必须和它的「渲染条件」写成同一个表达式,不能一边用
personalChartAvailable渲染、一边用另一个语义相反的谓词决定是否请求。删除兜底文案时必须回头检查被兜底遮住的空状态路径是否本来就是坏的——BUG-265 删兜底是对的,但没有验证删掉之后真实账号能不能拿到内容,代价是上线即空转。同一个概念(这里是「登录后的问候」)不允许存在多套并行实现,否则改动必然落在没被渲染的那一套上。Agent 生成的字段如果没有渲染点,就不要生成,更不能在客户端覆盖后还继续消耗 token。 - 相关记录:BUG-265(本次修复的直接前序:Agent 化改造正确但守卫未同步,且其「待跟进」已经预告了首屏等待态问题)、BUG-201(每日星语 effect 依赖完整 Profile 对象的既有决定,本次沿用其引用保持策略,未改依赖形状)、BUG-200(首页文案第一人称与真实性边界)、BUG-272(本次 hero 说明行处置的后续推翻)
- 复发自:无
- 修复版本:
a12f5797(staging)
BUG-270 | 证据门比产品自己向用户预告的层更松:两份声明分处两端且没有任何机制保证一致
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
scripts/jyotish_api_server.py的_ROUTE_REQUIRED_LAYERS与frontend/src/lib/consultation-domain-registry.ts的requiredLayers。涉及marriage、wealth、general三条路由的证据门,其中general是最常走的兜底路由。 - 用户现象:暂无可见现象。与 BUG-267 同一形态——门比声明松,失败方向是静默放宽,健康运行里看不出差别。
- 触发条件:任何走
marriage、wealth、general路由的咨询,且该路由声明要用的某一层实际缺失。 - 根因:修完 BUG-267 后核对时发现,前端领域注册表早就为每个领域声明了
requiredLayers,键名正确(marriage/wealth),并且这份列表会驱动界面上的evidencePreview——也就是产品明确告诉用户「这次会用这些证据」。但服务端的门是另写的一份,两份用不同词汇描述同一个合同,彼此没有任何链接。对照下来门少查三处:marriage声明了 A7(Darapada)而门只要求 UL,wealth声明了 Ashtakavarga 而门只要求 D2,general声明了 D10 与 D2 而门只要求 D1/D9。三者实测在真实排盘中都是used,所以补进去不会把状态推成degraded。这正是 BUG-267 里键名漂移能发生两个月的同一片土壤:合同有两份,没有一份是权威。 - 修复:
_ROUTE_REQUIRED_LAYERS补上marriage的 A7、wealth的 ashtakavarga、general的 D10 与 D2。新增一条测试解析前端注册表,断言其中每个指向真实 section 的条目都出现在服务端的门里。之所以只比对真实 section:注册表里同时有人看的标签(7th house/lord、negative holdout gate)和引擎压根不产出的 D11,机械全量对齐会把每条路由钉死在degraded。该测试对解析失败采取 fail-closed——先断言 10 条路由全部解析到且列表非空,否则一次正则失配就会让它无声通过。另外把tests/test_consultation_consumer_context.py加进run_quality_gate.py的CORE_PYTEST_TARGETS:核对时发现该文件不在任何 CI 档位里,BUG-267 的 8 条与本条的 1 条此前都只在本地手动执行过,staging 门(--profile quick)从不运行它们。防漂移的钉子本身没人跑,等于没钉。 - 验证:
tests/test_consultation_consumer_context.py17 条通过(新增 1 条)。已验证这条钉子是真守卫而非恰好通过:分别从marriage去掉 A7、wealth去掉 ashtakavarga、general去掉 D10,三次都报出对应路由与缺失层,恢复后通过。另用真实排盘跑了 general/marriage/wealth/career/timing/health/annual 七条路由,收紧后core_status仍为ready、missing_route_layers仍为[],确认没有把既有运行推成degraded。未做的验证:没有在 staging 上真实跑一次复看回执,线上表现未观测。 - 待跟进:D11 是另一个方向的口径不一致——前端向用户预告
wealth会用 D11,而引擎从不构建这个 section,属于「承诺了不存在的证据」,与本条(门比承诺松)反向。本轮未处理。更彻底的方向是让两端读同一份声明而不是靠测试比对,但两套词汇(机器 section 名 vs 人看的标签)先得想清楚归一到哪一套。 - 防复发:同一个合同不得有两份声明而没有一份权威。当暂时无法归一时,必须有一条测试把两份的交集钉住,并且该测试要 fail-closed——跨文件、跨语言的比对里,解析失配导致的「无声通过」比比对失败更危险。补门之前先用真实数据确认该层稳定产出:把引擎不产出的层写进必需集会把状态恒推成
degraded,这与放太松同样是错的,只是方向相反。写完回归测试后必须回头确认它在 CI 的哪个档位里真的会被执行:run_quality_gate.py有多组 pytest 目标,staging 门只跑CORE_PYTEST_TARGETS,写进RUNTIME_TRUTH_PYTEST_TARGETS或压根不进列表的文件在 staging push 上一次都不会跑。「本地全绿」不等于「门会拦住它」。 - 相关记录:BUG-267(本条是修完它以后核对同一张表时发现的,同源同形态)、BUG-259(同为路由与合同之间的口径不一致)
- 复发自:无
- 修复版本:
5caf47a4(staging)
BUG-271 | 工具调用在进入工具体之前被拒时,回执里查不到它失败过:步没记、调用没计、原因塌成兜底码
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
/api/consult的公开回执与服务端可观测日志。凡是模型给出的工具参数不被工具inputSchema接受的运行都受影响。 - 用户现象:staging 手测 run
951a841e(10ae149c)第一次工具调用发出tool.failed/calculation_failed,重试成功并给出了完整回答。但run.completed的回执里steps只有 2 条(skill + 那次成功的 tool)、stepBudget.used: 2,失败的那次完全不存在;tool.failed的 code 是兜底值,服务端日志同样只有兜底值。也就是说:客户端看见「失败过一次」,回执却说没有,而且两边都查不到为什么。 - 触发条件:模型对
run-jyotish-consultation传出不被consultationToolInputSchema(.strict(),只接受question与domains)接受的参数。 - 根因:三处叠加。其一,schema 拒绝发生在 Mastra 调用
execute之前,所以工具体内的一切都没跑——consultationToolCallCount不增、appendConsultationRuntimeStep不执行、连chart-calculation活动事件都没发出(这也正是本次能定位的证据:失败那次调用没有任何 activity,重试那次有)。工具自己无法记录一次它从未收到的调用。其二,safeToolError()把一切非AbortError/TimeoutError的错误塌成calculation_failed,公开事件因此不携带任何可区分信息。其三,即使失败落进了工具体的catch,consultationWorkflowFailureCode()对非ConsultationWorkflowError返回undefined,而 append 处写的是...(failureCode ? { failureCode } : {})——于是「最需要解释的那条记录」恰好是唯一没有原因的记录。 - 修复:在流层补记。流是唯一能观测到全部工具失败的位置(无论失败发生在 schema 这一侧还是
execute那一侧),且它持有startedAt计时因而能给出时长。tool-error分支现在比对「流已见的工具错误数」与「state 里已有的失败 tool 步数」,仅在前者更多时补一条status: "failed"、failureCode: "tool_call_rejected"的步——工具仍然记录它能看见的失败(带它独有的时长与真因),流只填空档,不重复计。另新增consultationToolFailureCode(),令每个错误都解析出一个码(工作流传输故障沿用原码、领域计划被拒为invalid_domain_plan、其余为unexpected_error),工具体catch改用它,failureCode不再可能缺失。刻意未改:failureCode仍不进公开回执——publicConsultationRuntimeSteps()的白名单与名为「the public receipt never carries the internal failure classification」的现存测试是刻意约束,workflow_rate_limited这类后端内情不应上线到客户端。原因走可观测日志的toolCalls[].failureCode(该通道本就是内部诊断专用,machineCodeSchema允许新码)。客户端现在能看到「有一步失败了」,运维能在日志里看到为什么。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts40 项通过(新增 3 项)。新增:(1) 只喂tool-call+tool-error两个 chunk(模拟execute从未运行),断言 state 里出现带tool_call_rejected的失败 tool 步、stepBudget.used变为 2、公开回执的 steps 状态序列为["completed","failed"],且序列化后不含该内部码;(2) 工具已记录过的失败不得被流重复记一遍,且原有的workflow_queue_full不被覆盖;(3)consultationToolFailureCode()对工作流错误、计划错误、普通 Error 与非 Error 值逐个断言有码。已验证第 (1) 条在修复前失败(fail 1),修复后 40 全通过。非数据库前端套件 1675/1675 通过,tsc --noEmit与改动文件eslint清洁。未做的验证:没有在 staging 上复现一次 schema 拒绝来复看回执——本次修复后线上是否真的出现带失败步的回执尚未观测,且触发它需要模型再犯一次同样的参数错误,无法主动构造。 - 待跟进:模型当时到底传了什么参数仍然不知道——正因为没被记下来。修复后若再次出现,日志会给出
tool_call_rejected,但具体是哪个字段不合法仍不可见;若该情况反复出现,下一步是在服务端日志里补一个「被拒字段名」的封闭枚举(只记字段名,不记值,避免把模型输出当日志内容)。另外consultationToolCallCount在这条路径上仍然不增(工具体没跑),本轮刻意未动:改它会破掉「never starts a calculation」与「invalid model input does not poison a later valid contract retry」两条现存的刻意断言,而失败已经能从steps看见,计数本身不再是唯一线索。 - 防复发:把「某件事发生过」的记录放在能观测到它的那一层。工具无法记录一次它从未收到的调用,因此这类记录必须由流(或同等的外层观察点)兜底;判断「哪一层能看见」的可靠证据是活动事件的有无,而不是错误码。失败记录的原因字段不得可选省略:
...(code ? {code} : {})这种写法会让分类器覆盖不到的错误静默变成无原因记录,而那恰好是最需要原因的一类。公开与内部两个通道要分别满足:客户端需要知道「失败过」,运维需要知道「为什么」,把后者塞进前者会泄漏后端内情,把前者省掉会让回执与客户端亲眼所见互相矛盾。 - 相关记录:BUG-268(同为「诊断量算出来就丢」,本条是「诊断量压根没被创建」)、BUG-258(同为失败时回执信息不足)、BUG-255(同为模型参数被拒白扔步数,当时的修法是把互斥字段从 schema 里删掉)、BUG-278(补正本条对触发路径的归因,并接手枚举化之后新的拒绝路径)
- 复发自:无
- 修复版本:
b5bcbaed(staging) - 生产验证(2026-08-18 补记):staging run
a5f4409e(2605f34a)出现连续两次tool.failed,回执steps里如实带上两条status: "failed"(39ms / 56ms)、stepBudget.used: 4。这正是本条修复前会丢掉的那两条记录,「无法主动构造」的验证点由手测撞上。同时补正本条根因里的一处归因:实际观测到的触发路径不是inputSchema拒绝,而是execute已进入、但canonicalDomainPlan()在任何状态写入之前就抛(领域不在注册表里),因此工具体同样没能记录任何东西。「失败发生在工具记录任何东西之前」这个机制是对的,落到inputSchema这一层的具体归因是错的——真正的inputSchema拒绝路径见 BUG-278:Mastra 对它 resolve 而不抛,压根不会走到tool-error分支。
BUG-272 | 首页 hero 第三行重复问候被删除,真实性边界随之迁移;onboarding 契约去掉无渲染点的欢迎语并扩展到全部十个主题
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
/首页 hero 文案层级、主题区小标题承载的出生时间边界声明、Onboarding 起点加载文案、/api/onboarding的 payload 契约与缓存版本、onboarding 生成超时预算、首页十张主题卡的文案来源。 - 用户现象:BUG-269 把 Agent 欢迎语放进 hero 说明行后,用户评审首页时指出这一行「有点累赘」,要求删掉整段
starter-hero-note。同一次反馈里还指出「首页可不是三个问题啊,有好多问题」,并追问首页标题是否由 Agent 生成(答:不是,来自onboarding-client.ts的本地时段变体表,5 个时段 × 3 条问句)。 - 触发条件:任何账号打开首页;hero 连续三行都在做同一件事(称呼、提问、再一次欢迎并再问一次「想从哪里开始」)。
- 根因:两处独立问题。
- hero 的信息层级在 BUG-269 之后变成三行同义内容。
starter-greeting已经完成称呼、h1已经完成提问,Agent 欢迎语在句式上又重复了这两件事,因此第三行没有新增信息。但这一行同时是「无可用出生分钟」账号的真实性边界声明所在(BUG-200 约束,starter-questions.test.ts有断言钉住),直接整段删除会连边界声明一起删掉。 - 首页主题卡渲染的是
consultationDomainRegistry的全部十个域,而/api/onboarding的suggestions是career/marriage/timing的固定三元组。页面按主题 id 逐个find,找不到就回落到 registry 的静态prompt,因此其余七个域(wealth、health、education、migration、family、annual、general)恒定是写死文案,个性化只覆盖三分之一的入口,而界面上十张卡看起来完全同级,用户无法分辨哪些是为自己生成的。加载态文案「根据你的资料整理三个起点。」也与实际渲染的十张卡不符。
- hero 的信息层级在 BUG-269 之后变成三行同义内容。
- 修复:分两步。
- 界面收敛:删除
starter-hero-note元素及其两处 CSS 规则,hero 收敛为称呼加提问两行。出生时间边界声明迁到主题区小标题,按personalChartAvailable分支——读者正要挑主题时才看到这句限制,位置比 hero 更贴近实际动作。加载态文案改为「根据你的资料整理今天的起点。」,不再声明具体条数。 - 契约收敛(用户评审后决定,同一分支内完成):
onboarding.greeting从 Agent 契约里彻底移除——schema、fallback、prompt、客户端响应校验与OnboardingContent类型全部不再有这个字段,不再为无渲染点的内容付费。suggestions从career/marriage/timing的固定三元组改成按consultationDomainIds顺序覆盖全部十个域,校验用 refine 钉住「长度与顺序都必须与 registry 一致」,于是首页十张卡全部是 Agent 写的,静态prompt退回纯兜底角色。fallback 直接由generalGuidedJyotishTopics派生,避免手写十条又与 registry 漂移。生成十条比三条显著更慢,预算随之上调:路由maxDuration30→60 秒、服务端生成超时 18→45 秒、客户端单次请求超时 25→50 秒。缓存版本ayanam-onboarding-v4→v5,让所有存量 v4 payload 重新生成一次。
- 界面收敛:删除
- 验证:
onboarding-presentation.test.ts的 hero 断言改为同时检查page.tsx与globals.css中不再出现starter-hero-note,避免只删元素留下死样式;starter-questions.test.ts的边界声明断言从「文件里存在这句话」收紧为「这句话出现在主题区小标题且受personalChartAvailable分支控制」,防止下一次挪动文案时悄悄丢掉。契约部分新增 5 条回归:只覆盖三个域的旧形态响应必须整体拒绝并落到覆盖全域的兜底、主题顺序被交换必须拒绝(否则问题会挂到错误的主题标签下)、fallback 自身必须能通过 payload schema(registry 里的 prompt 一旦不再第一人称就会被这条抓住)、prompt 与 payload/client 源码中不得再出现 greeting 字段、路由必须把 registry 主题列表发给 Agent。客户端请求超时断言从 25 秒下限改到 45 秒下限,与服务端生成预算对齐。全量套件 1720/1729 通过,9 个失败全部是本机 Docker/PostgreSQL fixture(与 BUG-265、BUG-269 同一类环境失败);tsc --noEmit清洁,ESLint 仅存量 4 条 warning,next build成功。未做的验证:没有在浏览器里看过改动后的首页,也没有真实调用过新契约的生成——本机没有模型密钥,十条问题的实际生成耗时是否落在 45 秒预算内、十张卡的文案是否彼此重复、以及 hero 删掉第三行后的留白与边界声明在窄屏的折行,都要等 staging 发布后确认。若线上出现大量fallback,第一嫌疑就是 45 秒预算仍然不够。 - 防复发:删除一个 UI 元素前必须先确认它有没有在承载与自身样式无关的合规或真实性文案——
starter-hero-note表面是装饰性说明行,实际是 BUG-200 边界声明的唯一落点。删元素时同步删样式,并用测试同时钉住两个文件,否则死 CSS 会在下一次改版时被误当作现有设计复用。声明数量的文案(「三个起点」)不要写死在与数据源无关的地方,数量必须跟着 registry 走。Agent 契约里的字段数量变化必须同步三件事:缓存版本、生成超时预算、客户端请求超时;只改 schema 不改预算的结果是全量用户静默落到 fallback,而 fallback 看起来是「正常内容」,不会报错。契约收紧时要用 refine 校验「集合与顺序」而不是只校验单项,否则模型少写几项或错位映射都能通过。 - 相关记录:BUG-269(本次推翻其 hero 说明行处置)、BUG-200(首页真实性边界声明的原始约束,本次迁移未削弱)、BUG-273(同一轮评审的下一项删除)
- 复发自:无
- 修复版本:本地未提交候选
BUG-273 | 回答后输入框上方的三条推荐问题实际使用率极低且从未由 Agent 生成,整条链路删除
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:会话内
composer-suggestions追问按钮、/api/consult写入的 assistant 消息字段、consultation-reply-metadata与agent-reply的解析契约、会话写入 schema、composer-wrap相关样式与 DESIGN.md 的对应条目。 - 用户现象:用户实际测试后反馈「Agent 回答后用户输入框上方的三个推荐问题」使用频率很少,要求删除,并要求 Agent 也不再生成这三个问题。
- 触发条件:任何咨询会话收到 assistant 回复之后,输入框上方固定出现三条按钮。
- 根因:这里有一个与用户预期不同的事实——这三条问题从来不是 Agent 生成的。
createConsultationReplyMetadata按会话主题从写死的十主题三元组里取一组,服务端总是把它塞进parseAgentReply的 metadata 参数,而 metadata 分支优先级高于模型输出(metadata?.suggestions ?? …),因此模型即便真的输出了AYANAM_SUGGESTIONS也会被覆盖;Prompt 本身还明确禁止模型产出隐藏元数据块。同一份三元组表被完整复制在agent-reply.ts与consultation-reply-metadata.ts两处。也就是说,界面上看起来「个性化」的追问入口,实际是按主题查表的十组固定文案,与用户的具体问题、星盘证据都无关——这正是使用率低的合理解释。附带发现:chooseConversationSuggestion里针对「先完成生时校正」这一条的生时校正跳转分支在当前链路下不可达,因为服务端 metadata 恒定覆盖,三元组里从不含这条文案。 - 修复:删除渲染点(
composer-suggestions块)、派生状态activeSuggestions、点击处理chooseConversationSuggestion与常量rectifyBeforeConsultationSuggestion、客户端readSuggestions与三处suggestions写入(send、本地预览、预览会话种子)。服务端consultationReplyMetadataSchema收缩为只有title且.strict(),createConsultationReplyMetadata不再需要theme入参,两份fallbackSuggestions表全部删除。parseAgentReply与parseAgentReplyBody在失去 suggestions 后返回形状完全相同,合并为单一parseAgentReply(value, metadata?),生时校正改调这一个入口。ChatMessage去掉suggestions字段;但会话写入 schema 的suggestions有意保留为可选——该 schema 是.strict()的,发布瞬间仍在运行旧 bundle 的客户端会继续带上这个字段,拒绝整个写入等于丢掉用户那条消息,代价远大于留一个被忽略的字段。stripAgentReplyMetadata同样保留剥离AYANAM_SUGGESTIONS注释的能力,因为删除前写入的历史回答里仍带着这个块,不剥离会把原始 HTML 注释显示给用户。CSS 删除 8 处.composer-suggestions规则(含混合选择器中的片段与两个媒体查询内的规则),Prompt 里「follow-up suggestions 由服务端生成」改为只提标题,DESIGN.md 对应条目改写为「回答不提供追问建议」并记录原因。 - 验证:新增/改写回归共 6 条:会话内不得再出现
composer-suggestions/activeSuggestions/chooseConversationSuggestion且 CSS 同步无残留、metadata schema 必须拒绝suggestions字段(防止 chips 从元数据侧回流)、两个库文件与 consult 路由中不得再出现主题三元组或 suggestions 写入、历史遗留的AYANAM_SUGGESTIONS块必须被剥离且不得复活成字段、输入框与正文之间不得再有会改变高度的兄弟节点(原 chip 行会在流式过程中撑高 composer)、生时校正入口在失去 chip 跳转后仍可从首页卡片进入。全量 1716/1721 通过,5 个失败全部是本机 Docker/PostgreSQL migration fixture;tsc --noEmit清洁,ESLint 仅存量 4 条 warning,next build成功。未做的验证:没有在浏览器里确认删掉 chip 行后会话底部的留白与滚动锚点表现,尤其是 BUG-263 里「跳到最新」按钮的定位曾依赖 chip 行带来的高度变化,需要发布后目视确认按钮位置仍然合理。 - 防复发:不要把查表得到的固定文案摆在会让用户以为是个性化生成的位置——这类「假个性化」入口既消耗界面空间又必然低使用率,而且因为看起来正常,不会有人报 bug。同一份兜底数据不允许在两个模块各存一份副本,否则删除时必然漏掉一处。删除一个可选字段时要区分「输出契约」与「输入契约」:输出侧应当立刻停止产出,输入侧在旧客户端与历史数据仍可能带上它时必须继续宽容接收,否则发布窗口内会丢用户数据。当两个函数因为字段删除而返回形状相同时应当合并,但要意识到其中一个可能是某个历史 Bug(此处 BUG-179)刻意建立的隔离措施,合并前必须确认该措施防的问题已经在结构上不可能发生。
- 相关记录:BUG-179(曾因通用解析器的三条建议兜底污染生时校正,本次删除使其隔离措施不再必要)、BUG-263(「跳到最新」按钮定位曾受 chip 行高度影响)、BUG-249(草稿隔离验证里包含推荐问题填入路径)、BUG-272(同一轮评审的上一项删除)
- 复发自:无
- 修复版本:本地未提交候选
BUG-274 | 个人报告中心与报告正文在移动端无法下拉
- 状态:resolved(已推入 staging,待部署)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
/reports个人报告中心、/reports/[reportId]报告阅读容器;聊天根滚动锁不放宽。 - 用户现象:移动端打开个人报告页后无法向下滑动阅读。
- 触发条件:全局
html, body { height: 100%; overflow: hidden }仍然锁死根滚动。报告中心只有min-height: 100dvh和overflow-x: hidden,没有被视口卡住的纵向滚动容器。报告正文虽然有独立滚动层,但高度写成100dvh:在移动浏览器里它可能比body的100%更高,多出来的部分被根滚动锁裁掉,容器自己却还没溢出,所以也滑不动。 - 根因:BUG-153 只给 ready 报告加了
100dvh + overflow-y: auto,没有覆盖报告中心,也没有按「父级已经是height: 100%」来锁高度。overflow-x: hidden不会让一个随内容长高的块变成可滚动层。 - 修复:
.report-center-shell与.personal-report-reader都改为height: 100%; overflow-y: auto,并补上-webkit-overflow-scrolling: touch。报告正文另加touch-action: pan-y,避免命盘 SVG 把纵向滑动吃掉。 - 验证:报告入口与阅读合同测试锁定两处容器都是
height: 100% + overflow-y: auto,且不得再写min-height: 100dvh/height: 100dvh。 - 防复发:任何跑在聊天根滚动锁里的独立页面,必须有「相对父级视口封顶的高度 + overflow-y: auto」。禁止只用
min-height: 100dvh冒充滚动容器;也不要在html, body已是height: 100%时把子层写成未封顶的100dvh。 - 相关记录:BUG-153
- 复发自:BUG-153
- 修复版本:已推入 staging,待部署
BUG-275 | 后台已发布套餐无法修改,也没有删除/下架入口
- 状态:resolved(已推入 staging,待部署)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
POST /api/admin/products、admin_save_product_draft、商品管理页;已有订单/订阅的硬删除限制不放宽。 - 用户现象:编辑已发布套餐保存时返回「提交内容不符合业务约束」;列表里没有删除或下架。
- 触发条件:对已发布种子套餐(如
standard_monthly)调用action=save并带上该商品 id;或在后台寻找删除按钮。 - 根因:保存函数只更新
status='draft'的行,已发布 id 会抛product_draft_not_found(PostgreSQL22023),被统一映射成「提交内容不符合业务约束」。即便命中草稿,审计after_value仍写入禁止字段code,触发admin_audit_logs_after_value_check(23514),同样被折叠成这句话。界面把所有行都标成「编辑草稿」,也没有 delete/retire API。已发布商品因订单/订阅外键不能硬删除,正确动作是下架。 - 修复:保存已发布/已下架商品时,复用该 code 的现有草稿或创建下一版本草稿,不再改正在售行。新增
admin_delete_product:草稿硬删除,已发布改为retired且enabled=false。商品审计字段改为productCode,避免踩兑换码审计的密钥字段禁令。管理页区分修改/发布/删除/下架,并把上述 SQL 异常映射成可读错误。 - 验证:数据库回归用与线上失败请求同形的权益 JSON 保存已发布月卡,必须得到新草稿且原在售行不变;再次保存更新同一草稿;删除草稿后行消失;删除已发布年卡后变为下架。API/UI 合同覆盖
action=delete、下架文案与错误映射。 - 防复发:后台商品写路径必须区分草稿与已发布。已发布商品的保存不得要求调用方先手建草稿;删除已发布商品只能下架,不能绕过订单/订阅外键做硬删除。
22023/23514的商品异常不得再折叠成笼统「业务约束」。写入audit.admin_audit_logs的 JSON 不得包含code/token/secret/key键。 - 相关记录:无
- 复发自:无
- 修复版本:已推入 staging,待部署
BUG-276 | 发送后刷新草稿被正确清空,但咨询请求从未落地,Agent 无反应
- 状态:resolved(已推入 staging,待部署)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
/主对话发送、POST /api/consult、GET /api/consult/status、tab 内jyotisha.pending-consultation恢复。 - 用户现象:正常发出一条咨询后立刻刷新。输入框草稿已清空,不会把刚发出的问题填回来;对话里也看不到后台任务,Agent 没有后续反应。
GET /api/consult/status对这次requestId返回「咨询请求不存在」。 - 触发条件:点击发送后、服务端
reserve_consultation_usage写入之前刷新页面。包括 2.5 秒撤回窗口内、问题持久化过程中,以及咨询 POST 已发出但还在选路/扣点、尚未插入consultation_requests时。 - 根因:发送在点击当下就清草稿、写入 pending
requestId并展示用户气泡,但真正的咨询占位发生在撤回窗口和persistSession之后的POST /api/consult里。刷新会中断这次 fetch。服务端只有在流开始之后才会continueAfterDisconnect;占位之前断开则行不存在。刷新后恢复链路只轮询 status:把首次 404 当成「可能还在启动」假装 reserved,连打三次 404 后放弃,并且从未用同一requestId重放 POST。pending 存储也只留了 session/request id,撤回窗口内刷新时问题正文既不在会话里也不在草稿里。 - 修复:pending 存储同步写入问题原文、主题和入口;刷新恢复时用这份原文补回用户气泡。status 首次 404 仍等待一次确认,第二次 404 则跳过撤回窗口、不重复追加用户消息,用原
requestId重放POST /api/consult。若重放撞上request_conflict(原请求稍后占位成功),改回 status 轮询而不是当成失败解锁。三次以上确认仍 404 才停止恢复。草稿仍在发送时清空,避免已发出的问题被填回输入框。 - 验证:
frontend/tests/consultation-recovery.test.ts覆盖 pending 带问题正文、404 后重放而非放弃、request_conflict留在恢复态;既有草稿隔离合同继续要求发送清空存储键。 - 防复发:刷新恢复不得把「status 404」直接解释成用户已取消或请求已结束;在占位完成前断开必须能用同一 requestId 重放生成。pending 存储必须包含重放所需的问题原文。已发出的问题不得靠草稿复活,必须出现在会话里并由 Agent 继续。
- 相关记录:BUG-249(草稿持久化清空已发送问题,本条保留该行为)、咨询流恢复迁移
20260808030000_consultation_stream_recovery.sql(断线后续跑只覆盖已占位的 reserved 请求) - 复发自:无
- 修复版本:已推入 staging,待部署
BUG-277 | 模型写给自己的过程叙述被当成回答交付给用户:契约未绿的文本没有丢弃,而是攒起来晚点一次性放出
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
frontend/src/lib/stream-agent-response.ts的outputText。所有/api/consult运行都经过它,但只有「契约变绿之前模型输出过文本」的运行会显形。 - 用户现象:staging 手测 run
a5f4409e(2605f34a)最终回答里没有任何占星结论,整段是模型在向用户解释自己的工具调用出错以及打算怎么改参数(大意:域名单有误、我改用另一组域、两次都不被接受、我改为不指定域),然后就结束了。用户问的问题(「大概哪一年」)没有得到任何回答。三句自述装在同一个answer.delta里——这就是一次性 flush 的指纹。回执显示计算其实成功了:4 步、两次失败 tool 之后一次成功 tool。 - 触发条件:模型在契约未绿(skill 未加载完或工具尚无一次成功)之前输出过文本,且此后契约变绿。失败重试的运行几乎必然满足。
- 根因:守卫写成了「先累加再判断」。
held += text无条件执行,紧随其后的if (!held || !contractReady(options)) return只是「暂不发送」。设计意图(原测试名写着 holds answer text until the contract completes)是「计算未验证前不要把文本流给用户」,但实现选的是攒着晚点发而不是丢掉。契约一变绿,下一次outputText就把攒下的内容整段 flush;emitted随之置真,ensureFinalResponseText()的空回答兜底因此也不会触发,用户既拿不到回答也拿不到「本次没能生成回答」的提示。要命的地方在于这段文本的内容与运行是否顺利强相关:顺利的运行里它是无害的开场语,失败重试的运行里它正好是模型在复述后端错误——而失败重试恰恰是它唯一会变长的场合。换句话说,这个缓冲在最需要它闭嘴的时候话最多。 - 修复:契约未绿时直接丢弃,不进
held。attemptOutput仍然无条件累加,所以「模型说过话但始终没给出回答」(runtime_contract_incomplete)与「模型全程沉默」(empty_answer)的诊断区分不受影响。随之first.held恒为空串,已连同consumeAttempt()的该返回字段一起删除:留一个恒假的判断在最终结算处,会让后来人以为held仍然跨越契约边界携带意义。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts43 项通过。原有的 holds-answer-text 断言按新意图改写并改名(契约前文本不得以任何形式到达客户端、契约后文本原样送出,并断言序列化后的事件流里搜不到那段自述);新增一条按 runa5f4409e形状构造的回归:模型只有自述、契约变绿后再无文本,断言用户拿到的是空回答兜底文案而不是那段自述。已确认改写后的断言在修复前失败。tsc --noEmit与改动文件eslint清洁;与 consult/流相关的 8 个套件 65 项全通过(tests/rectification-v9-database.test.ts在本机因缺少数据库预先就失败,与本改动无关,已用 stash 对照确认)。 - 待跟进:丢弃是一刀切的,合法的开场寒暄也会被丢。目前判断可接受(寒暄没有信息量,回答本体一定在工具结果之后产生),但如果将来要有意展示模型的推理过程,必须走独立事件类型而不是放宽这里——本条正是「把模型的中间输出混进 answer 通道」的后果。未做的验证:没有在 staging 上复现一次失败重试来复看回答。
- 防复发:不要用「暂存」实现「不该展示」。缓冲意味着数据仍在管道里,只是等一个条件;一旦那个条件与内容的性质无关(这里是「计算是否成功」与「这段文本是不是回答」无关),它迟早会在错误的时刻把错误的内容放出来。真正的判据是内容属于哪个通道,而不是时机到没到。同一个通道不得同时承载「给用户的回答」和「模型的过程自述」:一旦混流,任何「先攒着」的实现都会在失败路径上把两者对调。
- 相关记录:BUG-278(同一次 staging 运行暴露,且是本条的上游:域词汇不对导致失败重试,失败重试才让自述变长)、BUG-214(同为契约门与可见输出之间的耦合)
- 复发自:无
- 修复版本:待提交
BUG-278 | 领域词汇在模型能看到的地方从未被声明:schema 收任意字符串,skill 又教了一套不存在的名字,模型照 skill 传参白扔工具调用
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
frontend/src/mastra/consultation-tools.ts的consultationToolInputSchema.domains、frontend/src/lib/consultation-domain-registry.ts、Agent 指令,以及 skill 侧的SKILL.md第 223 行与references/strict-workflow-router.md。 - 用户现象:staging 手测 run
a5f4409e(2605f34a)连续两次tool.failed,模型在回答里自述"event-timing-strict" 不在服务端支持列表。同一批另一次成功运行的回答开头,模型自称「按 wealth-timing-strict + career 两域来算」——把 skill 的检查单标签当成服务端域名在向用户复述。两次失败各白吃一步预算,并间接造成 BUG-277(自述变长后被当成回答交付)。 - 触发条件:模型需要自己挑领域,且它遵循 skill 的方法文档来命名。
- 根因:合同有两份,没有一份权威——与 BUG-267、BUG-270 同一片土壤,只是这次两份分别是「不说话的」和「说错话的」。其一,
domains声明为z.string().trim().min(1).max(64),于是模型收到的 JSON schema 里items只是一个type: "string",完全不陈述合法词汇;真正的白名单藏在execute背后的validateConsultationDomainPlan()里,只有调用失败之后模型才能知道它的存在。其二,模型手上的 SKILL.md 第 223 行明确要求「先判断问题类型,再自动选择career-timing-strict/relationship-timing-strict/wealth-timing-strict/event-timing-strict/event-verification-strict」,被它引用的references/strict-workflow-router.md里这套名字出现 10 次,而skill.referenceReads证明模型确实在读这些文件。这些名字在方法学里是技法检查单的标签,不是任何工具参数,但没有任何一处告诉模型这个区别。模型于是老实照方法文档选名,schema 照收不误,一路走到注册表查表才被拒。 - 修复:把
domains的元素改为枚举。枚举取「规范 id + 全部既有别名」共 37 个值(新增consultationDomainPlanValues/consultationDomainPlanValueSchema),而不是只取 10 个规范 id:模型可以传别名、由服务端归一,这是 Agent 指令里的明文承诺,也有既有测试钉住(["career","finance","home"]→ career/wealth/migration),枚举化不应顺手收紧它。已实测该枚举确实进入模型收到的 JSON schema(items.enum37 项),否则整个改动等于没做。同时在 Agent 指令里点明检查单标签不是域 id:指令层不受 skill 包哈希约束,且按本项目设计其优先级高于 skill 内容。刻意未改 SKILL.md:根SKILL.md与skills/jyotish-vedic-astrology/versions/6.9.14/SKILL.md之间有逐字节相等校验(frontend/src/mastra/index.ts),包目录整树的 sha256 钉在skills/skill-package-registry.json,改内容的正确做法是升版本;但6.9.14同时是pyproject.toml、CHANGELOG.md、README.md与 SKILL.md 自身版本横幅的发布身份,为一行澄清升版本会造出「skill 6.9.15 / Python 包 6.9.14」这类新的不一致,而就地改钉住的版本会让同一个版本号在历史上指两份内容。枚举本身已经是权威约束,文档澄清留待下一次真实的 skill 发布搭车。 - 附带发现(本轮实测,此前无记录):Mastra 的
createTool会校验inputSchema,但校验失败时不抛异常,而是正常 resolve 一个{ error: true, message, validationErrors }信封(信封的 message 里会列出枚举值,这也是模型自我纠正的唯一线索)。这直接改变了枚举化的风险面:被拒调用从「进入execute后抛错」变成「resolve 一个值」,流层原本会照常把它当tool-result发出tool.completed——把一次从未执行的计算报告成完成,比枚举化之前更误导。因此在tool-result分支按信封的结构(error === true且带validationErrors对象)识别拒绝,而不是匹配上游的英文文案,改发tool.failed并沿用 BUG-271 的补记逻辑记一条tool_call_rejected步。同时更正 BUG-271 对触发路径的归因,详见该条的生产验证补记。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts43 项通过(本条新增 1 项、改写 3 项)。新增「schema 陈述它接受的词汇」:断言event-timing-strict/wealth-timing-strict被拒、规范 id 与别名(finance、感情)被收,并断言枚举值可从 schema 结构上读出(shape.domains.unwrap().element.options)——这一条专门防「换成z.preprocess之类的包装后,校验仍然通过但 JSON schema 里的枚举没了」这种静默失效。改写:原先钉住「注册表抛错」的两条断言改为钉住信封(机制变了,意图不变:不得启动计算、不得污染请求级缓存),并额外断言信封 message 里列出了合法 id。另新增「被 schema 拒绝的调用不得报告为 completed」,断言事件流里没有tool.completed、有tool.failed、state 里落下tool_call_rejected步,且内部码不泄漏到客户端。tsc --noEmit、改动文件eslint、tests/test_consultation_consumer_context.py17 项均清洁。未做的验证:没有在 staging 上观测模型见到枚举后是否不再传检查单标签。 - 待跟进:SKILL.md 第 223 行与
references/strict-workflow-router.md里的 10 处仍在教这套词汇,须随下一次 skill 版本发布一并澄清。另外 37 项枚举里含中文别名,若日后确认模型只用规范 id,可考虑收窄到 10 项以减少 schema 噪声——但那是收紧合同,需要单独一轮并同步 Agent 指令里的别名承诺。 - 防复发:模型必须遵守的词汇,要写在模型读得到的地方,也就是 schema,而不是只写在校验它的代码里。
z.string()这类「结构上合法、语义上无约束」的字段等于把合同留在服务端自言自语:模型只能靠试错发现规则,而每次试错都是一次真实的调用预算。给模型的工具 schema 里出现自由字符串时,先问一句「合法值是有限集吗」——如果是,就必须枚举出来。枚举化之后要实测生成的 JSON schema,不能假设 zod 的包装层会把枚举透传。还要顺带检查「被拒」这条路径在框架里是抛还是返回:抛与返回会落到流层完全不同的分支上,误判会把失败报告成成功。 - 相关记录:BUG-271(本条更正了它对触发路径的归因,并接手枚举化之后新出现的拒绝路径)、BUG-277(本条是它的上游)、BUG-267 与 BUG-270(同为「同一合同两份声明、没有一份权威」)、BUG-255(同为模型参数被 schema 拒白扔步数)
- 复发自:无
- 修复版本:待提交
BUG-279 | 模型手上从来没有副运边界:证据包读的是引擎从未写过的键,而精确应期的许可由另一个同名对象签发
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
frontend/src/mastra/consultation-workflow.ts的local_layers与projectTimingEvidence、scripts/jyotish_api_server.py的_attach_local_consultation_layers与_build_consumer_context、scripts/unified_consultation_orchestrator.py的证据分节表。所有/api/consult个人盘运行。 - 用户现象:staging 手测中两次成功运行(「未来一年」「财运」)的回答都出现「副运细节与行运触发未在本轮完整计算」,只能给到大运级别的方向;而同一次运行的回执写着
preciseTiming: "allowed"。用户直接问了这两句为什么对不上,以及副运为什么没算。 - 触发条件:任何依赖应期的回答。恒定发生。
- 根因:一个名字指着两个对象。模型侧
local_layers.dasha_boundaries读chart.modules.dasha_boundaries,而这个键在整个仓库里从未被赋值过——_attach_local_consultation_layers只挂 varga_full / arudha_padas / narayana_dasha / ashtakavarga / kp_cusps——所以该字段恒为undefined,投影时整段消失,模型手上只剩chart.dasha.periods,也就是 6 到 20 年一段的大运列表。证据门里同名的分节dasha_boundaries读的却是modules.dasha(大运),它当然是used,于是timing_layers_ready成立、can_answer_precise_timing为真。回执因此签发了「能给到月份」的许可,而模型连一条副运边界都没有,只能在回答里如实说副运没算全。两侧各自自洽:读的一侧永远拿不到数据,判的一侧永远看得见数据,中间没有任何断言比较过这两个名字是不是同一个对象。BUG-268 让skillReferenceReads可见时曾怀疑是模型没翻方法文档,本条说明缺的是数据本身。 - 修复:三处。其一,服务端真的算出副运并挂到
modules.dasha_sub_periods:从chart.dasha.periods里取出覆盖参考日的那一段大运,用dasha_analyzer.build_antardasha按比例细分为 9 段。刻意不复用现成的_compute_vimshottari_analysis_layer(它从月亮黄经另建一条时间线):两条线出自同一引擎、数值几乎一样,但「几乎」意味着模型手上会出现两套大运日期且无从判断该引用哪套;从包里已经展示给模型的 periods 上切,副运边界必然落在模型看得见的大运边界之内。其二,证据包新增独立分节dasha_sub_periods(源路径写明modules.dasha_sub_periods),精确应期改为同时要求dasha_boundaries+dasha_sub_periods+narayana_dasha——大运列表只能定位十年,本就不该单独签发月份级许可。其三,模型侧字段随之改名为local_layers.dasha_sub_periods,与服务端实际写入的键对齐;Agent 指令补一句:该层在场就用它的边界、不得再说副运没算,缺席则一句话说明。 - 验证:真实排盘端到端跑通(1990-05-12 北京,参考日 2026-08-18):
current.mahadasha与chart.dasha.periods里的 Sun 段逐字段相同(2024-07-03→2030-07-03),当前副运为 Jupiter 2026-07-21→2027-05-09,9 段副运首尾正好贴合大运首尾,local_consultation_layers.diagnostics为空,分节used,can_answer_precise_timing为真。tests/test_consultation_consumer_context.py21 项通过(新增 4 项:副运边界必须切自包里展示的 periods 且覆盖参考日;跨语言键名守卫;只缺副运时精确应期必须被拒;副运在场时放行)。测试基准盘原先只写了{'current_md': 'Sun'},已改为用compute_vimshottari_timeline派生真实 periods,否则新层在 fixture 上根本不会被执行。前端除数据库套件(本机无 Postgres)外 1633 项通过,tsc --noEmit与eslint清洁。 - 待跟进:Pratyantardasha(第三层)仍未计算,所以「哪一周」仍只能答到副运加 90 天慢行星触发窗,不能声称精确到日。
_compute_vimshottari_analysis_layer那条从月亮黄经另建的时间线仍留在 dasha 接口路径上,未与本层合并。行运触发窗已由 BUG-302 接入咨询包。未做的验证:没有在 staging 上复看真实回答是否不再出现「副运没算全」。 - 防复发:跨语言的字段名不能靠人眼对齐。一端读
modules.X而另一端从不写X,两侧测试都不会失败——读的一侧只看到undefined被投影掉,写的一侧根本不知道有人在读。已加的守卫直接比较两端:把local_layers里所有modules.<key>读法抽出来(先剥注释,否则解释性注释里的旧键名会被算成读法),与服务端真实挂载后的chart['modules']键集合求差,非空即失败。另一条教训是分节名要说清自己是什么:dasha_boundaries既能读成「大运边界」也能读成「所有周期边界」,正是这层歧义让门以为自己检查过副运。 - 相关记录:BUG-267(同一段代码里另一处「判据没接到权威来源」,精确应期的空判是那轮修的)、BUG-270(同为两端声明不一致且缺跨端断言)、BUG-268(同一批可观测性问题,本条是它排除掉的另一种解释)
- 复发自:无
- 修复版本:待提交
BUG-280 | 计算成功而模型没写回答时,用户被扣点并只收到一句「没有可展示的回答」
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
frontend/src/lib/stream-agent-response.ts的最终结算段、frontend/src/app/api/consult/route.ts的重试接法。两条 agentic 路径(个人盘与无出生分钟)都在内。 - 用户现象:staging 手测一次多域运行
run.completed,回答全文只有「本次计算已完成,但暂时没有生成可展示的回答。请换一个角度提问……」;回执显示工具 63.5 秒成功、状态 ready。用户问「为什么这次就生成失败了,也没有 run failed」——问的正是这个矛盾:运行报成功、点数照扣、回答没有。 - 触发条件:契约已绿(skill 已加载且恰好一次成功计算),而模型此后未输出任何非空文本。
- 根因:兜底文案被当成回答交付并结算。
ensureFinalResponseText()在契约绿且输出为空时返回一句固定道歉,随后fullOutput被替换、emitted置真、onComplete照常调用complete_consultation_response——用户为一句「没有回答」付了费。同时它让下方以「输出为空」为条件的分支全部变成死代码:兜底一定先把fullOutput填满,所以既有的empty_answer失败码从未有机会发出,run.failed也就永远不会出现,这正是用户看到「没有 run failed」的原因。至于模型为何不写,本轮无法定案:最接近的证据是既有回归里同形状运行的modelFinishReason为tool-calls(还想调工具却用尽了AGENT_MAX_STEPS = 8),且已确认那次不是超时(工具 63.5 秒成功,110 秒预算尚余约 46 秒)。修复因此不押注单一根因,而是覆盖「计算成功、模型没写」这一整类。 - 修复:两步。其一,先再问一次:契约已绿而输出为空时,用同一份 baseMessages 追加一句「上一轮没有输出任何回答文本,请直接给出回答」重跑一轮模型循环,并记一条
answer-retry校验步。这轮重试不关闭工具——请求级缓存在execute之前就短路返回(if (calculation) return calculation,不计数、不记步、不重算,因此单次计算边界不受影响),模型再调一次工具即可原样取回同一份证据;反过来若用toolChoice: "none",模型手上没有任何计算结果,只能凭空写。新一轮也拿到全新的maxSteps预算,这正是「上一轮因步数耗尽而沉默」所需要的。其二,重试后仍为空则以empty_answer失败:不再交付兜底文案,run.failed带回执与「不会扣点」的文案,账务走cancel。固定道歉文案与ensureFinalResponseText()一并删除。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts44 项通过。新增两项:计算成功但无文本时触发一次 answer-retry 并交付重试写出的回答(末步为answer-retry);重试仍沉默则run.failed/empty_answer、onComplete不触发、文案含「不会扣点」。改写两项:BUG-277 那条「只有自述」的回归改为断言既不交付也不结算;步数耗尽那条(modelFinishReason为tool-calls的生产形状)不再断言兜底被结算。计费契约测试改为钉住 4 次usages.push(retried.totalUsage)与 2 处retryForAnswer(两条路径各有契约重试与回答重试,四次模型调用的 token 都必须计量)。前端除数据库套件外 1633 项通过,tsc --noEmit与eslint清洁。未做的验证:没有在 staging 上复现一次空回答来观测重试是否救回。 - 待跟进:仍未取到那次运行的
[agent-observability]日志,modelFinishReason未定案;若日后确认多域运行普遍在第 8 步耗尽,应当调预算而不是靠重试兜。回答重试没有独立时间预算:若上一轮已把 110 秒用尽,它会立即被 abort 并以calculation_failed结束(同样不扣点),代价是失败码不如empty_answer精确。 - 防复发:兜底文案不能既当回答又当结算依据。「保证客户端总能收到点东西」和「这次咨询算完成」是两件事,用同一段文本表达两者,必然出现「没回答也扣点」。空回答的正确出口是失败码加不扣点,而不是一句道歉。另一条:一旦某个兜底把最终变量填满,它下游所有以「该变量为空」为条件的分支都成了死代码——
empty_answer就这样被埋了整轮。加兜底时要顺手确认它没有吃掉下游的诊断分支。 - 相关记录:BUG-277(同一段结算逻辑;它删掉的缓冲正是兜底此前不触发的原因之一)、BUG-214(同为契约门与可见输出的耦合)、BUG-271(同一批「失败在回执里查不到」)
- 复发自:无
- 修复版本:待提交
BUG-281 | staging 质量门 1731/1735:xiaoxin 上 Docker 地址池耗尽,4 个 PostgreSQL fixture 起不来
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:Gitea
backend-quality-gate.yml的npm test --prefix frontend、frontend/tests/helpers/postgres-fixture.ts、deploy/reclaim-runner-disk.sh、xiaoxin runner 上的 Docker user-defined networks - 用户现象:
927bdd7a的 validate 在 1735 项前端测试里红 4 项,日志末尾是两条已通过的 truth-source 合同。失败原文都是docker compose up -d --wait postgres创建jyotisha-postgres-*-app网络时返回all predefined address pools have been fully subnetted。 - 触发条件:向
staging推送后,xiaoxin(20 核)上tsx --test按os.availableParallelism()并行跑全部frontend/tests/*.test.ts。约 20 个文件会各自startPostgresFixture(),每个 Compose 项目占用一个 user-defined network。 - 根因:两层。其一,质量门跑的是全量并行
npm test,而test:db才把数据库套件串行化;20 核 runner 上一轮实测创建了 26 个jyotisha-postgres-*网络,其中 4 个在分配子网时失败。其二,BUG-266 的回收脚本只做docker network prune --filter until=6h,同一天内崩溃或未拆掉的 fixture 网络继续占着 Docker default address pools;该 prune 本来就不会删仍有 endpoint 的网络,6 小时门槛对地址池没有额外保护,只会把当天残留留到下次compose up爆掉。失败的 4 项分别在database-local-business、identity-auth-integration、model-configuration-security、rectification-pr4-database,与927bdd7a的咨询副运改动无关。 - 修复:fixture 在
docker compose up前领取最多 2 个并发槽(可用JYOTISHA_POSTGRES_MAX_FIXTURES覆盖),持有期覆盖整个 Compose 项目寿命;槽位用目录锁,进程已死则回收,启动失败与stop()都会释放。回收脚本在既有 6 小时 prune 之后,对名字匹配jyotisha-postgres-的网络逐个docker network rm:空闲的删掉,仍有 endpoint 的留下并发 job。 - 验证:
postgres-fixture-contract.test.ts2/2、staging-backend-workflows.test.ts38/38 通过(含回收脚本会network rm残留jyotisha-postgres-网络、仍拒绝docker rm --force/system prune,以及部署脚本 shell 语法)。未做的验证:没有在 xiaoxin 上复跑完整质量门。 - 防复发:Docker user-defined network 和磁盘是两类资源。磁盘回收不能代替地址池回收;
until=6h对「今天刚留下的空网络」无效。每个并行测试文件一个 Compose 项目,在多核 runner 上会按核数打满 default address pools。数据库 fixture 必须自己限制并发,不能依赖tsx默认并行度。 - 相关记录:BUG-266(同一 runner 上的磁盘耗尽与 6 小时网络 prune)、BUG-264(本地全量
npm test并发跑database-*fixture 的既有抖动) - 复发自:BUG-266(回收只覆盖磁盘与 6 小时以上空网络,未覆盖地址池)
- 修复版本:待提交
BUG-282 | 生时纠正 opening 强制 named tool_choice,thinking 模型立刻 run.failed
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:staging 首页进入生时纠正、
POST /api/rectification/agent的action=opening、V10runV9AgentTurn第一步prepareStep - 用户现象:从首页进入生时纠正后流立即结束。NDJSON 只有
run.started接着run.failed,没有skill.bound、case.loaded或任何回答增量。 - 触发条件:登录后从首页打开生时纠正;当前会话模型处于 thinking/reasoning 模式。
- 根因:runner 为了保证第一步读取 Case,把
prepareStep设成toolChoice: { type: "tool", toolName: "rectification-read-case" }。上游 thinking 模式拒绝任何非 auto 的tool_choice,返回Thinking mode does not support this tool_choice且isRetryable: false。该错误被压成泛化run_failed,客户端因此只看到两个公开事件。鉴权、Case 绑定和 turn 创建都已成功,失败发生在第一次 provider 调用。 - 修复:第一步仍只暴露
rectification-read-case,但toolChoice改为"auto"。运行器继续拒绝在case.loaded之前调用其他公开工具。provider 若仍返回该原文,错误码改为可诊断的thinking_tool_choice_unsupported,并写一条不含正文的 attempt 失败日志。 - 验证:
rectification-v9-agent.test.ts断言第一步为activeTools=["rectification-read-case"]且toolChoice="auto";rectification-v9-stream.test.ts新增 thinking-mode 拒绝用例,确认只尝试一次、公开事件仍是run.started/run.failed、attempt 错误码为thinking_tool_choice_unsupported。 - 防复发:thinking 模式不能发送 named 或 required
tool_choice。需要限制第一步工具时,用activeTools收窄集合,把选择权留给 auto;真实读取门禁继续由 runner 在case.loaded之前拦截其他公开工具。 - 相关记录:BUG-261
- 复发自:无
- 修复版本:待提交
BUG-283 | 今日星语每次进入首页都停在“没能生成”
- 状态:regressed
- 首次发现:2026-08-18
- 最近更新:2026-08-19
- 影响面:
/首页“今日星语”卡片、POST /api/daily-starlanguage、进程内缓存与浏览器当日缓存 - 用户现象:资料完整的账号每次打开首页,卡片都显示“今天的星语没能生成,稍后再回来看看;这里不会用通用文案顶替。”,没有当日内容。
- 触发条件:出生时间可用于个人星盘的账号进入首页。冷路径或换实例后必现。
- 根因:BUG-265 把卡片改成同步等待 Agent,失败就不展示内容;BUG-269 修好了请求守卫,但首页仍把 30–45 秒的模型生成当作首屏依赖。进程内缓存不跨实例、不跨重启,JSON 解析或超时失败后客户端只剩失败文案。再进入等于再走一条冷路径。
- 修复:星盘、双轨 Dasha、分盘和过境算完后,立刻用这份证据拼一张个人卡片返回,不再等待模型。Agent 改为后台润色,成功才覆盖缓存。首页按账号+资料指纹把当天卡片写入 localStorage,刷新先显示已有内容;只有星盘算不出来才落到失败文案。日期改用出生时区日历日,不再用 UTC。
- 验证:
frontend/tests/daily-starlanguage.test.ts覆盖证据卡片因盘而异、未确认出生时间不用上升宫、请求路径不阻塞 Agent、失败不覆盖已缓存卡片。 - 防复发:首页入口卡片不得把 LLM 生成当作首屏成功条件。禁止用固定四句文案池顶替;也禁止在生成失败时让整张卡空白。这条防复发没有挡住请求路径继续等待完整
/api/chart加四层附加计算,复发见 BUG-299。 - 相关记录:BUG-265、BUG-269、BUG-299
- 复发自:BUG-265(诚实降级正确,但把模型生成当成了首页可用性)
- 修复版本:
9d8b91ac(staging,已被 BUG-299 覆盖)
BUG-284 | 技能激活的 77% 是 1592 条无说明的文件名,方法论从未进入任何一次 web 回答(referenceReads 恒为 0)
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
frontend/src/lib/consultation-methodology.ts(新增)、frontend/src/mastra/consultation-tools.ts的toModelDomainPlanContext与运行时状态、frontend/src/mastra/index.ts的 Agent 指令、frontend/src/lib/consultation-agent-events.ts与frontend/src/lib/agent-observability.ts的回执与日志字段。所有走run-jyotish-consultation的个人盘咨询。 - 用户现象:用户从项目一开始就在问「为什么 web 上调用模型调用 skill 达不到本地 Agent 框架调用 skill 的输出效果」。BUG-268 把
skill.referenceReads暴露到回执后,两次 staging 手测(66c44875综合主题、1797cc73事业应期)都是loaded: true, referenceReads: 0,10 步预算只用 2 步:加载 skill、调一次工具、直接开写。回答本身可读且盘面自洽,但用的是模型自带的印度占星常识加服务端精确数据,仓库里那套方法论至今没有参与过任何一次线上回答。 - 触发条件:任何一次个人盘咨询。与 route、域数、模型无关。
- 根因:技能激活的载荷结构使「按需读取」在这个包上不可能发生。实测
skill()单次返回 129,651 字节,其中 SKILL.md 方法论正文 30,507 字节,## References清单 59,584 字节(827 条路径),## Scripts清单约 39,300 字节(760 条路径)——77% 是 1,592 条不带任何说明的文件名。Mastra 的formatSkillActivation()就是 instructions 加上包内全部文件的平铺列举(#discoverFilesInSubdir递归收集,只跳过符号链接目录),本身没有筛选机制。SKILL.md 第 220 行那句「凡是用户询问事业、婚恋、财务、应期……必须先读取references/strict-workflow-router.md」是这 30KB 里的一行,后面紧跟 1,592 个无从分辨的候选;而 Agent 指令另一侧明确要求「调 run-jyotish-consultation 再作答」。模型于是执行了那条可执行的,skill_read一次未发。这不是模型偷懒:给定这份载荷,正确的文件无法被可靠地挑出来。Mastra 自己也在警告Instructions have 717 lines (recommended: <500)。 - 修复:路由已在服务端定好,skill 又已经声明了每条路由必读哪份清单,所以这个选择根本不需要占用模型的一个回合。新增
consultationMethodologyForDomains():按本轮实际执行的域,从哈希锁定版技能包(versions/6.9.14,registry 里 sha256 内容寻址)取出strict-workflow-router.md的共享基线段与该路由的 strict 段、event_judgment_skeleton.md以及该域的event_judgment_<domain>.md,按标题文本定段(路由文件重新编号不会失配),单段上限 6,000 字符、整块上限 24,000 字符,随工具结果的methodology字段与证据一起交付。实测单域 4 段约 9KB、最宽的四域计划 9 段 21KB,均未触上限。further_reading不是硬编码,而是扫 SKILL.md 自己写出的references/...路径并剔除包内不存在的,得到 19 条可按需skill_read的索引——取代原来那 1,592 条。路由未声明 strict 清单的域(annual/general/health/education/migration/family——full-reading-strict只出现在路由表里,文件中没有对应段落)如实列进domains_without_strict_checklist,不拿别的路由顶替。Agent 指令新增一段:把methodology当作本次作答的方法而非背景,逐条对着证据走,遵守其输出纪律(事业路由要求把「机会接触 / 结构性机会 / 结果显现」三层分开写,不得合并成一个模糊的事业机会窗口——1797cc73那轮恰好就是合并的),且这些段落已交付、不要再花回合重读。 - 验证:新增
frontend/tests/consultation-methodology.test.ts8 项:事业路由的 strict 段确实带着D10与Opportunity contact到位;无 strict 清单的域如实上报且不被顶替;多域计划每条清单只出现一次;十个域逐个与最宽计划都在体积预算内;further_reading只含包内存在的references/路径且远少于包体清单;交付内容来自锁定版本号;按标题定段在下一个##处停止。consultation-agentic-runtime.test.ts45 项通过,新增一项钉住回执把「服务端交付了几段」与「模型主动读了几篇」分开报。tsc --noEmit与eslint清洁(5 条既有 warning 不在改动文件内)。未做的验证:没有在 staging 上比对同一问题在交付方法论前后的回答差异。 - 待跟进:那 99KB 的文件名清单仍然每次请求都发一遍,只是不再是模型唯一的导航入口——已由 BUG-285 收掉(不是收窄清单,而是撤掉激活本身)。
referenceReads预期仍会经常为 0,这本身不再是缺陷,但它和methodologySections要一起读。 - 防复发:「渐进式披露」只在候选集可判别时成立。把 1,592 个文件平铺给模型,等于既没有给方法也没有给索引,而
loaded: true会让这件事看起来是好的——技能加载成功与方法论被采用之间没有任何因果关系,必须分别度量。另一条:当某个选择在服务端已经确定(这里是 route),就不要把它变成模型的一次工具调用;确定的事交付,不确定的事才询问。 - 相关记录:BUG-268(把
referenceReads暴露出来,这个问题才可见)、BUG-278(同为「skill 说的和 schema 收的不是一套词汇」)、BUG-279(同为服务端已算出的东西没送到模型手上)、BUG-285(撤掉激活,并补上激活内容从未被哈希覆盖这个洞) - 复发自:无
- 修复版本:待提交
BUG-285 | 技能激活逐轮重发 129KB,且模型看到的包内容从未被任何哈希覆盖
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
frontend/src/mastra/skill-binding.ts(新增)、frontend/src/mastra/index.ts的四个 Agent 与指令、frontend/src/mastra/consultation-tools.ts的运行时状态与钩子、frontend/src/lib/stream-agent-response.ts的契约门与事件映射、frontend/src/app/api/consult/route.ts的回执与重试。个人盘咨询、无出生分钟的通用问答、首页日签的 general 路径、引导问题生成——四条路径全部。 - 用户现象:没有直接的报错现象,这是一笔一直在付、但从没有人看见账单的开销。用户在 BUG-284 之后追问了两件事:模型在同一个会话里每问一次是不是就要重新调一次 skill;以及那 129,651 字节里 77% 是文件名这件事该怎么处理。
- 触发条件:任何一次对话的任何一轮。
- 根因:两件事叠在一起。(一)激活的开销是逐轮的:咨询路径没有配 Mastra memory,也没有
threadId/resourceId;Agent 每次请求新建(id里含requestId,不进缓存);回放的history只有{role, text}纯文本,不含任何工具调用或工具结果。所以模型每一轮都必须重新激活一次技能,那份载荷逐轮重发;更糟的是一次请求内的retry(契约不完整)与retryForAnswer(空回答)都是agent.stream([...baseMessages, 追加一句]),各起一个全新的模型循环、各自再激活一次,最坏情况单次请求付三遍。(二)模型看到的包内容不受哈希约束:skills: [jyotishSkillPath]指向的是工作树视图skills/jyotish-vedic-astrology,而那里的references是软链到../../references的 827 条全量树;完整性检查只逐字节比对 SKILL.md,registry 的 sha256 管不到激活时列举出来的那 1,592 条路径。生时纠正的 Agent 没有这个洞,它用的是resolveSkillPackageRuntimePath(skillPackage)(先校验 sha256,再建一个以规范技能名命名的符号链接别名指向锁定版目录)——正确的机制仓库里早就有,只是咨询路径没用。 - 修复:把技能从「模型的一次工具调用」改成「服务端在跑之前就绑定好」,与生时纠正对齐。新增
frontend/src/mastra/skill-binding.ts:从锁定版包读 SKILL.md、剥掉 frontmatter,包进<jyotish-skill name version>交给 instructions;skills指向resolveSkillPackageRuntimePath()的哈希校验路径;用一个providesSkillDiscovery: "on-demand"的输入处理器撤掉skill与skill_search,只留skill_read(这是 Mastra 为「调用方自行负责技能发现与指令加载」留的声明位,不是绕路)。四个 Agent 统一走这个绑定,Load the ... skill before answering一类句子改成声明方法已在提示里、并明确「以下是本产品的运行时契约,与 skill 方法冲突时以契约为准」。运行时状态的jyotishSkillLoaded改名jyotishSkillBound并在创建时即为真,绑定作为回执的第一步记下(不带耗时,因为确实没有取任何东西);skill.started/skill.completed两个公开事件改由服务端在流开头宣告,客户端契约与文案不变,但首个 activity 从「等激活往返之后」提前到「立刻」。无出生分钟那条路径的契约重试整段删掉——它的requireTool为假、方法又已绑定,已经没有任何可修的契约了。那个处理器不只是个标记:它逐步检查系统提示里确实带着绑定标记,读不到提示就放过,能读到而没有方法则中止本轮——撤掉激活之后,一个装配时漏了方法块的 Agent 不会再报错,只会安静地在没有方法的情况下作答,这正是本轮要消掉的失败形态。 - 验证:新增
frontend/tests/skill-binding.test.ts4 项:绑定的正文与锁定版 SKILL.md 逐字一致且运行时路径落在 sha256 目录下;实测激活载荷与绑定载荷的比值(前者是后者两倍以上,后者不到前者 45%);绑定后的工具面恰好只有skill_read而技能仍被声明;系统提示缺方法时中止、带方法与读不到提示时都放过。实测数字:从锁定版包激活是 118,352 字节(## References56,696、## Scripts14,824),绑定后 47,289 字节,每次省 71,063 字节;相对生产当前实际发出的 129,651 字节省 82,362。顺带一项一致性:锁定版的references是 189 条的真目录,而工作树视图是软链进 827 条,所以skill_read能读到的范围与methodology.further_reading校验的范围现在第一次是同一棵树。非 DB 套件 1,666 项中 1,665 通过,唯一失败是onboarding-route.test.ts里 50ms 墙钟对 5ms 超时的竞速用例,单独跑通过(19ms)且该用例注入generateText、不构建 Agent,与本轮无关。tsc --noEmit清洁,改动文件eslint清洁(6 条既有 warning 都不在改动文件内)。未做的验证:没有在 staging 上比对绑定前后同一问题的回答差异,也没有实测冷启动多算一次包哈希的代价(resolveActiveSkillPackage与resolveSkillPackageRuntimePath各算一次)。评审中被自己的测试抓到一个真 bug:守卫最初用JSON.stringify(system).includes(标记),而标记里带双引号、序列化后会被转义,那样每一轮都会误判「方法没绑上」并中止全部请求;改成按形状递归收集字符串。 - 待跟进:Mastra 加载技能包时仍会对 SKILL.md 正文报行数警告(
skill_read仍要声明哈希目录),那是文件体积,不是 prompt 摘录。全谱系调用、强制工作流与strict-workflow-router.md的 Full-Spectrum / health-timing-strict 已写入商业入口并交付给网页模型,见 BUG-287。不要把研究仓入口绑进聊天 Agent。 - 防复发:一次「模型可以忘记」的必要步骤,等于一条必然会出现的失败路径加一条修它的重试;当那一步的内容在服务端已经完全确定时,绑定比请求便宜,也比请求诚实。另一条:完整性检查必须覆盖模型实际看到的东西,而不只是入口文件——只比对 SKILL.md 的字节,会让人以为整个包都被钉住了。
- 相关记录:BUG-284(同一条链路的上半段:先把方法按路由交付,本轮再撤掉激活)、BUG-282(生时纠正侧的服务端绑定与
skill.bound,本轮对齐的先例)、BUG-214(runtime_contract_incomplete的门,本轮去掉了它对模型加载动作的依赖)、BUG-286(摘录绑定并解开非解盘 Agent) - 复发自:无
- 修复版本:待提交
BUG-286 | 咨询 prompt 绑了整份 SKILL.md,general 与 onboarding 也各付 47KB 解盘方法
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
frontend/src/mastra/skill-binding.ts的绑定正文、frontend/src/mastra/index.ts的四个 Agent 工厂、frontend/src/app/api/consult/route.ts的无出生分钟路径注释。个人盘咨询、无出生分钟一般问答、引导问题生成。 - 用户现象:没有直接报错。Mastra 对技能入口报
Instructions have 717 lines (recommended: <500);无出生分钟问答与首页引导问题每次仍把约 47KB 解盘手册送进模型,而这两条路径一个不能做任何本命声明、一个只产主题建议问题。 - 触发条件:任何一次个人盘咨询、无出生分钟咨询、或 onboarding 生成。
- 根因:BUG-285 为了不改行为,把锁定版
SKILL.md全文塞进 instructions,并让四条 Agent 共用同一绑定。商业入口相对研究稿只换了文件头,底下仍留着施工判断原则、37 个子命令、名人案例索引;这些对回答一个用户的盘没有作用。上游研究入口仍标6.9.14,比产品快照只多一行 Formal Varga 审计,把它绑进 Mastra 会更胖,并与可见语音冲突。 - 修复:咨询路径改为按标题从商业
SKILL.md摘录运行时段落(商业路由、关联技法完整调取、五层硬约束、强制规则去掉开源复用边界、未闭环点、核心方法论、强制规范速查、注意事项),施工/CLI/案例索引留在包内供skill_read。general 与 onboarding 不再插入方法块、不再声明 skill,与日签 Agent 对齐;onboarding 用一段产品范围条替代 47KB 方法。不绑研究入口。 - 验证:
frontend/tests/skill-binding.test.ts断言摘录来自商业入口且不含施工/CLI/案例标题,并含关联技法完整调取;新增「只有解盘 Agent 绑定方法」。相关套件skill-binding/consultation-birth-time-mode/consultation-voice-contract/consultation-workflow-contract/onboarding-route/daily-starlanguage/consultation-agentic-runtime/consultation-methodology通过。tsc --noEmit清洁,改动文件eslint清洁。Mastra 加载技能包时仍会对 SKILL.md 报行数警告,因为咨询 Agent 还要skill_read指向该目录;那是文件体积,不是 prompt 摘录。 - 待跟进:SKILL.md 施工/CLI/案例段落仍在包内给本地 Agent 和
skill_read,聊天 prompt 继续只摘运行时段。未在 staging 上比对摘录前后同一问题的回答差异。 - 防复发:哈希锁定的入口可以全文保留给本地 Agent,但聊天 prompt 只能摘运行时段落;一条不能解盘的路径不得绑解盘方法。Mastra 对技能文件的行数警告与 Agent.instructions 体积是两件事,不要用发版去修只属于 prompt 的问题。
- 相关记录:BUG-285(全文绑定的直接前因)、BUG-284(路由方法论已随工具结果交付,本轮不再把同一份手册全文再塞一遍)、BUG-287(全谱系写入商业入口与咨询运行时)
- 复发自:无
- 修复版本:待提交
BUG-287 | 咨询只算主题相关分盘,西洋层与技法审计表到不了回答
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-18
- 最近更新:2026-08-18
- 影响面:
scripts/jyotish_api_server.py咨询本地层、scripts/unified_consultation_orchestrator.py证据包与分盘调度、scripts/consultation_plan_contract.py证据类别、frontend/src/mastra/consultation-workflow.ts模型投影、frontend/src/mastra/product-voice.ts可见回答格式、商业SKILL.md、references/strict-workflow-router.md(与versions/6.9.14包内副本)。个人盘咨询。 - 用户现象:没有直接报错。排盘只带 D2/D4/D9/D10/D11;其余 D1–D60 被写成「追问再展开」。西洋本命默认不算行运/回归/推运。模型上下文没有技法审计表,可见语音还禁止把它写进回答。对照本地 Agent 调用
yinduzhanxing-skill,网页还缺 Full-Spectrum 路由合同、健康清单、功能性吉凶星/Shadbala/Kakshya 进入作答,且口语被要求不要先写原始结构。 - 触发条件:任何一次有出生分钟的个人盘咨询。
- 根因:咨询路径按主题子集计算分盘以省成本;西洋 timing 要调用方逐项点名;
toModelOutput只投影四张卡片里的允许键,runtime_evidence_log里的审计表从未进入模型上下文;VISIBLE VOICE 明确禁止复制 Technique Audit Table。 - 修复:咨询默认计算 20 张正式分盘加其余 D-N 研究分盘,并自动挂上已实现的西洋本命与 timing 层(失败标 blocked,不静默省略)。consumer_context 带
varga_spectrum、western_spectrum、technique_audit_table。模型投影放行这些字段。可见回答末尾要求一张中文技法审计表(已执行 / 阻塞 / 不适用),口语须先用已交付的原始结构再综合。同一合同写入商业SKILL.md(关联技法完整调取 / 全谱系真实调用 / 强制工作流 / P0-P1 观察层),并把yinduzhanxing-skill的Full-Spectrum Invocation Contract与health-timing-strict写入产品strict-workflow-router.md随方法论交付。咨询本地层补上 Functional Benefic/Malefic、Shadbala、Kakshya。重算6.9.14包哈希;不另维持一份产品覆盖层。 - 验证:
tests/test_consultation_consumer_context.py断言正式 20 / 研究 40 张分盘及 Functional/Shadbala/Kakshya 本地层;tests/test_western_chart_engine.py断言默认挂上已实现西洋 timing;前端consultation-context/consultation-voice-contract/consultation-plan/skill-binding/consultation-methodology断言审计表、全谱系合同、健康清单与商业入口强制工作流进入模型包。skill-registry断言登记哈希与包树一致。 - 防复发:主题相关分盘只决定优先解读,不得再把未算的正式分盘写成 deferred。西洋 natal 咨询不得再要求调用方点名每个 timing 层。模型包缺少
technique_audit_table视为回归。商业 SKILL.md、strict-workflow-router.md与咨询运行时必须是同一套调用面;改入口后必须同步根文件、versions/6.9.14并重算 registry sha256。网页口语不得再禁止使用已交付的原始结构。健康域必须交付health-timing-strict,不得再报「无清单」。 - 相关记录:BUG-286(prompt 摘录,不靠发版修调用)、BUG-284(方法论随工具结果交付)
- 复发自:无
- 修复版本:待提交
BUG-288 | 生时校正一句多事件首轮未能纳入校正依据
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:V10
rectification-propose-evidence/rectification-confirm-evidence/record_agentic_rectification_evidence_batch、Activity「整理事件证据」、新 Case 绑定jyotish-birth-time-rectification@10.0.1 - 用户现象:用户在一句里说出两件带日期的清楚事件后,Activity 显示整理事件证据未完成,事件没有进入校正账本。
- 触发条件:当前轮用户消息包含两件可拆分事件;模型按旧提示逐条 propose+confirm,或使用
education_milestone等 TS 有、SQL CHECK 没有的 kind。 - 根因:系统提示要求同轮逐条 propose+confirm;
confirm_agentic_rectification_evidence_v10会在第一条确认时 resolve 无目标证据的 opening focus,第二条变成focus_not_active。propose在 quote 不匹配时 raise,整轮失败。表 CHECK / batch allowlist 比 TSEVIDENCE_KINDS/EVIDENCE_DOMAINS更窄,工具层过了、插入失败。批量路径本可按项原子写入并直接 confirmed。 - 修复:SQL kind/domain/precision 与 TS 对齐;propose 对
quote_not_grounded/invalid_item返回结构化失败;confirm 允许省略 focusId,且不 resolve opening focus;新事件只走 batch;quote 规范化额外去掉 ASCII,!.;:?。新增 Skill10.0.1。不改 10.0.0 包哈希。 - 验证:
frontend/tests/rectification-ingest-p0.test.ts、frontend/tests/rectification-ingest-p0-database.test.ts(有 Docker 时)、kind allowlist 两边一致、propose 结构化拒绝且 receipt 为 completed、confirm 可无 focusId。 - 防复发:一句两件及以上事件不得再要求逐条 propose+confirm;SQL CHECK 必须覆盖 TS 枚举;quote 失败不得整轮 throw。
- 相关记录:BUG-174、BUG-175
- 复发自:无
- 修复版本:待提交
BUG-289 | 用户确认日后正文把日级说成年级
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:生时校正证据投影、
display_date_label、Skill/系统提示复述规则、revise_agentic_rectification_evidence - 用户现象:用户确认一件日级事件后,公开正文写成「年份已确定为 YYYY」,精度被说粗。
- 触发条件:账本已有
date_precision=day且occurred_from为具体日期;用户回复「是 / 对」。 - 根因:confirm RPC 本来不改日期;Dossier 虽有精度字段,投影和 Skill 没有强制复述必须与服务器精度同级。模型把日级复述降成年。
- 修复:证据投影增加只读
display_date_label(日级YYYY-MM-DD,禁止格式化成年);Skill 与系统提示要求复述用该标签;用户确认不得改date_precision;revise 在更粗且 quote 没有更粗日期表达时拒绝precision_downgrade。 - 验证:日级 label 契约测试;confirm 工具不传日期字段;Docker 下「是」修订年级被拒绝且行仍为 day。
- 防复发:公开复述不得使用模型自拟年份句;日级 label 不得含「年份」。
- 相关记录:BUG-288
- 复发自:无
- 修复版本:待提交
BUG-290 | 生时校正不可分平台未作为服务器字段交给 Agent
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
latest_result工具投影、candidate-comparison.md、系统提示第 9 条 - 用户现象:对照里网页已能说出代表性分钟和约 25 分钟不可分区间,但该宽度不是 Agent 必读的服务器字段,后续容易学成本地 1 分钟尖峰。
- 触发条件:v5 打分器返回 top 簇
tied_minute_count很大、公开候选约 04:45/46/47。 - 根因:投影只有
confirmation_allowed/ 代表时间,没有indistinguishable_width_minutes;Skill 未把「宽度 > 5 或 top tied > 1 必须说不可分区间」写成硬合同。 - 修复:投影增加
indistinguishable_width_minutes;宽度大于maxConfirmationWidthMinutes(5)时投影层关闭confirmation_allowed;Skill 与提示词要求说不可分区间 / 代表性候选,不得说已定位到唯一分钟。v5 打分器不改。 - 验证:04:45/46/47 且 tied=25 时宽度 ≥ 25 且
confirmation_allowed=false;禁止该夹具下confirmation_allowed=true。 - 防复发:平台结果不得把代表分钟说成唯一出生分钟;宽度字段必须随
latest_result交给 Agent。 - 相关记录:BUG-288、BUG-289
- 复发自:无
- 修复版本:待提交
BUG-291 | 生时校正按缺失领域轮询迁居/健康/财务,而不是八大方法层
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
rectification-read-case、method_followup_plan、Skilljyotish-birth-time-rectification@10.0.2 - 用户现象:网页在已有学业事件后继续按迁居 / 健康 / 财务轮询;本地 skill 按方法层问关系、事业、家人。
- 触发条件:已确认带日期事件,SQL
missing_evidence_categories仍列出未覆盖领域。 - 根因:追问由 conversation summary 的缺失领域清单驱动,没有服务器方法层路由。
- 修复:新增
method_followup_plan。下一问按有日期事件 → 关系 → 事业 → 家人;外貌 / 胎记 / 占问跳过。不再用缺失领域轮询。仍只产出候选,不宣布确认。 - 验证:
frontend/tests/rectification-eight-method.test.ts:学业事件后下一问是关系,不是迁居。 - 防复发:Agent 必须跟
method_followup_plan;stop_domain_rotation=true时不得按领域清单继续问。 - 相关记录:BUG-288、BUG-290
- 复发自:无
- 修复版本:待提交
BUG-292 | 收集阶段要等用户说「没有更多了」才打分,Activity 显示 0 项计算依据
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
rectification-record-evidence-batch、rectification-confirm-evidence、工具 receiptexecuted_methods - 用户现象:本轮已写入带日期事件,Activity 仍写「本轮完成 · 0 项计算依据」;要用户说没有更多事件才比较。
- 触发条件:批量或确认写入了可评分证据,Agent 未再调用 compare-candidates。
- 根因:打分只挂在显式 compare 工具上。收集阶段写入成功不触发服务器重算。
- 修复:证据有效变化(账本指纹变了)时由服务器重算并持久化候选;失败不回滚证据写入。receipt 带上实际执行的方法。不因此进入
candidate_ready,也不 offer 采用。 - 验证:批量写入后 persist 被调用且 completed receipt 含方法;引擎失败时 batch 仍 accepted。
- 防复发:不要等「没有更多了」才比较;同一指纹不要再 compare。Agent 不得自己扫分钟。
- 相关记录:BUG-291
- 复发自:无
- 修复版本:待提交
BUG-293 | 分钟窗口扫描若做成 Agent 工具,容易被说成 ±5 分钟确定结论
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:v5
window_scan诊断、rectification-compare-candidates、候选卡投影 - 用户现象:对照本地分钟扫描能给出候选簇;网页要么不扫,要么容易把扫描说成已确认到几分钟。
- 触发条件:窗口内 D9/D10 升一不同;公开工具若再加第 14 个 scan 工具。
- 根因:扫描能力没有作为现有 compare 路径的服务端字段;确认门也没有把扫描与「不得宣布唯一分钟」绑死。
- 修复:Python 诊断增加
window_scan(只计 D9/D10 升一数量与是否不同)。折入现有 compare 与自动重算,结果进候选卡 / 平台语言。公开工具仍为 13 个。confirmation_allowed不因扫描变为 true。 - 验证:诊断测 D9 两个指数则
d9_candidates_differ=true且无星座名;25 分钟平台仍禁止确认。 - 防复发:不得新增第 14 个 rectification 工具;不得把若干事件写成 ±5 分钟确定性结论。
- 相关记录:BUG-290、BUG-291
- 复发自:无
- 修复版本:待提交
BUG-294 | D9/D10 类型表若直接告诉用户会变成性格/配偶/事业标签
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
internal_observations、read-case 投影、Skill 10.0.2 技法路由 - 用户现象:分盘升一不同时,正文可能把用户说成某星座或某类配偶/事业气质。
- 触发条件:窗口扫描发现 D9 或 D10 升一在候选间不一致。
- 根因:类型表若作为用户可见结论,没有强制投影成追问主题。
- 修复:只投影
ask_theme(关系经历 / 事业经历)。解析时丢掉星座名等额外键。Skill 禁止贴标签。 - 验证:含「白羊/天蝎/热情」的扫描输入投影后 JSON 不得出现这些词;D9 不同时下一问是关系主题。
- 防复发:read-case / 候选投影不得输出星座名、配偶类型或事业特质标签。
- 相关记录:BUG-291、BUG-293
- 复发自:无
- 修复版本:待提交
BUG-295 | 咨询把解盘 skill 钉在哈希包上,手动更新 SKILL.md 不会生效
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
skills/skill-package-registry.json、frontend/src/lib/skill-package-registry.ts、frontend/src/mastra/skill-binding.ts、frontend/src/lib/consultation-methodology.ts、frontend/src/mastra/index.ts、frontend/src/instrumentation.ts、deploy/railway-web.Dockerfile。个人盘咨询。 - 用户现象:维护者手动更新仓库根
SKILL.md/references/后,网页咨询仍按钉死的versions/6.9.14哈希包作答;要让更新生效还得双份拷贝并重算 sha256。 - 触发条件:咨询 Agent 绑定方法,或
consultationMethodologyForDomains()取路由清单。 - 根因:BUG-284/285 把咨询绑到 registry 里的
jyotish-vedic-astrology@6.9.14。mastra/index.ts还要求根SKILL.md与该快照逐字节相等,否则进程拒绝启动。这把「手动更新 skill」变成了发版手续。 - 修复:咨询改为读 live 目录
skills/jyotish-vedic-astrology(及可选JYOTISH_SKILL_PATH),不校验 sha256、不钉版本。Mastra 只挂 live 的SKILL.md/references/scripts/assets,不把目录里遗留的versions/快照交给 loader。生时纠正和个人报告仍用哈希包。启动时确认 live 树存在。镜像最终阶段同时拷入/app/SKILL.md、assets、references、scripts,让 live 相对符号链接能解析。 - 验证:
frontend/tests/skill-binding.test.ts、skill-registry.test.ts、consultation-methodology.test.ts;registry 不再把解盘 skill 列为 active hashed package;resolveActiveSkillPackage("jyotish-vedic-astrology")抛错。 - 防复发:咨询路径不得再
resolveActiveSkillPackage("jyotish-vedic-astrology"),不得恢复根入口与versions/的逐字节相等门。哈希身份只留给 rectification 与 personal-report。 - 相关记录:BUG-284、BUG-285、BUG-286、BUG-287
- 复发自:无
- 修复版本:待提交
BUG-296 | 生时校正确认门三层失败原因未投影给 Agent
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
latest_result.confirmation_gate、rectification-confirm-birth-time、Skilljyotish-birth-time-rectification@10.0.3 - 用户现象:相邻分钟分不开、VedAstro 官方分钟敏感校验未跑通、公开 AA holdout 未达门槛时,Agent 仍可能把代表性候选说成唯一出生分钟,或去追更细确认。
- 触发条件:引擎
confirmation_allowed恒为 false,external_validation_status=not_evaluated;v5 平台宽度大于 5;密封 holdout 有效例数低于 20。 - 根因:确认门只把
confirmation_allowed=false交给 Agent,没有结构化写出 VedAstro / 不可分区间 / holdout 三层 blocker。not_evaluated容易被说成 fail;holdout 未就绪时也没有禁止放宽确认门的服务器字段。 - 修复:
latest_result增加只读confirmation_gate。VedAstro 保持not_evaluated(不是 fail);宽度大于 5 时不可分层为blocked;holdout 为not_ready(密封 v2 聚合,不含个案身份)。任一 blocker 未通过则投影confirmation_allowed=false,confirm 工具返回confirmation_blocked。v5 打分器与确认门阈值不改。用户仍可 accepted 代表性候选。 - 验证:
frontend/tests/rectification-confirmation-gate.test.ts:窄宽度且引擎允许时仍因 VedAstro/holdout 禁止确认;25 分钟平台仍禁止;confirm 不调用写入 RPC;投影不含精确分钟宣传或个案身份。 - 防复发:不得把
not_evaluated写成 fail;不得为凑门槛编造 holdout;不得把confirmation_allowed改成 true;公开工具仍为 13 个。 - 相关记录:BUG-064、BUG-290、BUG-294
- 复发自:无
- 修复版本:待提交
BUG-297 | 并列分钟把采用代表性时间当成失败而不是本次校正结果
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
decision_policy.py采用门、session_outcome、method_followup_plan、Skilljyotish-birth-time-rectification@10.0.3、候选卡文案 - 用户现象:相邻分钟分不开时,用户这次校正没有可用结果;Agent 继续追问感情/事业/家人,而不是给出可采用的代表性时间。确认唯一分钟本来就不该通过。
- 触发条件:至少 3 件可评分事件、至少 2 个领域,但 top 候选
tied_minute_count> 1(例如约 25 分钟平台)。确认门三层 blocker 仍未通过。 - 根因:
acceptance_allowed把unique_top与诊断稳定性并进采用门,并列分钟直接关掉selection_allowed,候选卡不出现。推荐徽标还要求confirmationAllowed。method_followup_plan.next_followup在已有可采用结果时仍要求继续补证据。 - 修复:并列分钟与诊断稳定性只写入
confirmation_reasons,不再关闭采用。latest_result.session_outcome=adopt_representative时本轮结果是采用代表性时间;next_followup置空,方法追问进入deferred_followup。确认门保持 fail-closed。正文与采用后文案写明还不能确认唯一分钟。不自动进入candidate_ready。 - 验证:
tests/test_rectification_v5_services.py单领域仍禁止采用,双领域并列分钟允许采用但禁止确认;frontend/tests/rectification-eight-method.test.ts平台结果next_followup=null且session_outcome=adopt_representative;frontend/tests/rectification-confirmation-gate.test.ts锁定该 outcome;推荐徽标不再要求确认门。 - 防复发:不得把
confirmation_allowed改成 true;不得用采用冒充唯一分钟确认;不得在adopt_representative当轮再问next_followup;重算后仍不得自动candidate_ready。公开工具仍为 13 个。 - 相关记录:BUG-120、BUG-292、BUG-294、BUG-296
- 复发自:无
- 修复版本:待提交
BUG-298 | 咨询回答把技法审计表铺进正文,列表和表格也缺少对话样式
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:普通咨询消息渲染、
VISIBLE VOICE、livejyotish-vedic-astrologySKILL.md、Agent 回执 - 用户现象:个人盘回答后,比较表和技法审计表像未排版的管道符墙;末尾约四十行技法表压过口语正文。
- 触发条件:有出生分钟的个人盘咨询,模型按旧合同把
| 技法 | 状态 | 说明 |写进answer.delta。 - 根因:可见语音要求口语末尾贴完整 Markdown 技法审计表;
.markdown-table没有样式;列表项里的段落仍吃正文边距。BUG-011 只禁止内部EvidenceAuditPanel,没有另做用户可读的折叠表。 - 修复:口语不再粘贴审计表。服务端把已交付行放进回执,界面用默认收起的
本轮技法折叠块展示。历史回答若仍带该 Markdown 表,则从正文拆出再折叠。比较表与列表改用对话字号、边距和横向滚动。不恢复内部证据面板、英文状态码或 raw receipt。 - 验证:
frontend/tests/consultation-technique-audit.test.ts、consultation-voice-contract、skill-binding、evidence-audit-panel、chat-bundle-splitting-contract、chat-stream-layout。 - 防复发:聊天消息不得再挂载
EvidenceAuditPanel;口语合同不得要求把技法审计表写进正文;折叠块默认不得带open。 - 相关记录:BUG-011、BUG-287
- 复发自:BUG-011
- 修复版本:待提交
BUG-299 | 今日星语首屏仍失败:证据卡还在等完整星盘
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
/首页“今日星语”卡片、POST /api/daily-starlanguage、/api/daily_guidance - 用户现象:资料完整的账号一进首页就看到“今天的星语没能生成。点这里从今日问起;不会用通用文案顶替。”,没有当日内容。
- 触发条件:出生时间可用于个人星盘的账号打开首页。冷路径必现。
- 根因:BUG-283 不再等模型,但首屏仍串行调用完整
/api/chart(含 VedAstro 总览、Shadbala、瑜伽等)再并行等 Vimshottari、Narayana、D9/D10、过境,最坏约 40 秒,超过路由maxDuration = 30。超时后客户端只剩失败文案。失败文案还写了“点这里”,而整张卡本来就是点击热区。仓库里已有轻量/api/daily_guidance(本命盘 + 今日月亮过境宫 + 当前大运),首页没用。 - 修复:首屏只调
/api/daily_guidance。卡片按今日月亮过境宫与当前大运拼句,不用四句通用文案池,也不用本命月亮宫冒充今日。Agent 润色仍在后台。失败态改成“今天的星语还没写出来。”,行动改为“从今日问起”。引擎超时 8 秒,路由上限 20 秒。 - 验证:
frontend/tests/daily-starlanguage.test.ts锁定/api/daily_guidance、禁止首屏/api/chart、今日过境宫与本命月宫区分、失败文案不含“点这里”;tests/test_daily_guidance_service.py锁定结构化宫位/大运且不同日期会变。 - 防复发:首页日签不得把完整
/api/chart或四层附加计算当作首屏成功条件。失败文案不得写“点这里”,也不得解释内部“不用通用文案”政策。 - 相关记录:BUG-265、BUG-269、BUG-283
- 复发自:BUG-283(不再等模型,但仍把完整排盘当首页可用性)
- 修复版本:待提交
BUG-300 | 网页咨询把已算技法从模型包裁掉,模型用参数知识填洞
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:普通咨询
toModelOutput、_attach_local_consultation_layers、口头回答密度 - 用户现象:线上咨询缺 Yoga、Arudha/UL/A10、Chara、行运等本地 skill 会用的结构,模型用常识补度数和宫位。
- 触发条件:有出生分钟的个人盘咨询。计算工作流已挂上分盘谱、Narayana、功能性吉凶,但发给模型的证据包没有这些层。
- 根因:
toModelOutput的本命投影只保留升一/行星/宫位/Shadbala/AV/功能性吉凶/分盘谱。Yoga、Arudha、Gulika、Kakshya、KP、Chara、行运留在toAgentConsultationContext里却不进模型包。咨询路径也未把 Yoga/Chara/Sade Sati 写入 modules。 - 修复:咨询 attach 补 Yoga、路线相关 Chara、Sade Sati 行运。模型包投影这些层,并给出
must_use_layers已执行短名单。未交付层禁止用常识补算。MEVG / VedAstro 仍诚实blocked。不绑 AGENTS.md 或整份 SKILL。 - 验证:
frontend/tests/consultation-context.test.ts、consultation-spectrum-parity.test.ts、consultation-voice-contract.test.ts;tests/test_consultation_consumer_context.py。 - 防复发:咨询
toModelOutput必须包含 skill 共同基础里本地已执行层;local_layers不得再读引擎未写入的 module 键。 - 相关记录:BUG-279、BUG-287、BUG-298
- 复发自:BUG-279(算了却不交给模型)
- 修复版本:待提交
BUG-301 | 网页咨询前台把 VedAstro 整段跳过,审计只能诚实 blocked
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:普通咨询
POST /api/consult、execute_consultation_workflowforeground、vedastro_gateway、模型包vedastro_cross_check - 用户现象:技法表里 VedAstro 云状态一直是阻塞,即使官方网关和网络可用。
- 触发条件:聊天前台
foreground: true→defer_optional_external_evidence: true。 - 根因:BUG-161 为避免 overview/full snapshot/range scan/gateway 串行超时,前台直接跳过 gateway 并写死
foreground_optional_evidence_deferred。本地层恢复后(BUG-300)仍把可选官方交叉验证当成“本轮不算”。 - 修复:前台仍跳过 VedAstro 主入口 overview 与 range scan。gateway 与本地计算并行,子进程预算约 8 秒,本地完成后最多再等 1.5 秒。赶上且有 official raw 则合并为
official_verified并投影日月升一星座;超时或失败保持official_blocked,本地回答不失败。MEVG / PyJHora / jyotishganit 仍 blocked。不提高AGENT_TIMEOUT_MS。 - 验证:
tests/test_api_server_security.py前台赶上合并与超时降级;tests/test_consultation_consumer_context.py压缩包去掉经纬度;frontend/tests/consultation-context.test.ts、consultation-spectrum-parity.test.ts。 - 防复发:前台不得再同步串联 overview+snapshot+range scan;赶不上不得把未交付官方层标成 executed;压缩交叉验证不得带出生钟点或坐标。
- 相关记录:BUG-161、BUG-159、BUG-300
- 复发自:BUG-161(超时防护把可选官方层整段关掉)
- 修复版本:待提交
BUG-302 | 咨询行运层只挂了 Sade Sati,触发窗从未搜索,模型只能说行运没算
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:普通咨询
_attach_local_consultation_layers的modules.transits、toModelOutputtiming / western_spectrum、口头应期 - 用户现象:skill 共同基础含行运,引擎也有
transit_trigger.search_all_transit_triggers,但网页咨询只能看到土星过月阶段,回答继续写「行运触发未在本轮完整计算」。西洋应期层已算完,发给模型的压缩包却只剩 status。 - 触发条件:有出生分钟的个人盘咨询,尤其问未来数月或行运。
- 根因:attach 只复用
chart.transit_triggers(咨询排盘从不写这个键),从不调用触发搜索。西洋压缩只保留 status/boundary,把 target_date、aspects、duration windows 裁掉。 - 修复:咨询对参考日起 90 天、土星/木星/罗睺/计都对升一与月亮做有界触发搜索,最多 6 条日期窗进模型包;空列表表示该窗无精确接触,不是没算。西洋压缩补日期、aspects、扫描窗,去掉回盘钟点和经度。口头合同:
search_period在场就不得再说行运没搜。不提高AGENT_TIMEOUT_MS,不发明水星逆行。 - 验证:
tests/test_consultation_consumer_context.py锁定搜索窗与触发压缩、西洋窗保留且去掉钟点;frontend/tests/consultation-context.test.ts、consultation-spectrum-parity.test.ts、consultation-voice-contract.test.ts。 - 防复发:
modules.transits必须带search_period;模型包 timing 必须投影triggers;西洋 techniques 不得只留 status。 - 相关记录:BUG-279、BUG-300
- 复发自:BUG-279(副运修好后,待跟进里的行运触发仍未接上)
- 修复版本:80969c9b
BUG-303 | 校正确认门的 VedAstro 分钟快照从未在 V9 评分路径执行,状态永远 not_evaluated
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
/api/rectification/v5/score、/api/rectification/v5/diagnostics、confirmation_gate.vedastro_minute_sensitive - 用户现象:确认门 VedAstro 一行长期
not_evaluated。独立的/api/rectification/v5/vedastro-validate存在,但 V9 评分从不调用分钟身份层;SearchEvents 也不是确认判据。 - 触发条件:V9
rectification-compare-candidates/ diagnostics 产生两名候选且本地acceptance_allowed。 - 根因:
build_decision_receipt把external_validation_status写死为not_evaluated。评分 HTTP 层不附加官方分钟快照。超时若被标成 fail,会把「没跑完」说成「校验失败」。 - 修复:本地接受门通过且有两个不同 HH:MM 时,并行跑官方分钟快照(升一/宫界、D9、D10、Dasha 指纹),不跑 SearchEvents。区分则
passed,完整比较后无法区分则failed;超时、不完整或未就绪保持not_evaluated。引擎confirmation_allowed仍为 false;公开 AA holdout 仍not_ready,不得写唯一分钟。回执不带坐标或 fingerprint。 - 验证:
tests/test_rectification_v5_vedastro_validation.py锁定通过/无法区分/超时/本地未就绪跳过,且不调用 range scan。 - 防复发:V9 评分路径必须写
gates.exact_confirmation.external_validation_status;赶不上不得改成 fail;SearchEvents 不得单独放行确认。 - 相关记录:BUG-301
- 复发自:无
- 修复版本:80969c9b
BUG-304 | 用户说暂时想不到了之后,生时纠正只复述事件,不给结果也不引导下一步
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:V9 生时纠正
rectification-read-case、系统提示第 9 条、时间卡片 - 用户现象:已口述多件带年份经历并说「暂时想不到了」后,回复只有事件复述和「以后再继续」。Activity 为「1 个步骤 · 0 项计算依据」,没有候选时间卡,也没有排盘。
- 触发条件:用户停止补事件。模型本轮只调用 read-case。
- 根因:提示把「没有更多事件」写成可以自然结束。工具投影没有服务器下一步。排盘结果本应是候选时间卡,但事件若未写入账本或未比较,卡片不会出现;正文又被禁止把时间写进聊天,用户因此什么都看不到。
- 修复:read-case 增加
next_user_action/on_user_stop。用户停止时:账本空则 batch 已说的带日期经历再比较;有事件无结果则本轮 compare;已有代表性结果则解释并请采用卡片。禁止只说记下了以后再继续。公开工具仍为 13 个。 - 验证:
frontend/tests/rectification-eight-method.test.ts、rectification-v9-agent.test.ts。 - 防复发:停止收集不得只做口头确认;下一步必须来自
next_user_action.on_user_stop。 - 相关记录:BUG-179、BUG-292、BUG-307
- 复发自:BUG-179(禁止正文写时间后,若卡片未出现就没有任何排盘结果)
- 修复版本:40a59504
BUG-305 | 咨询生成半截结束仍被当成成功回答并扣点
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:普通咨询
POST /api/consult、streamAgentResponse、咨询页 NDJSON 收口、DeepSeek V4 Flash 生成预算 - 用户现象:staging 一次咨询回答停在半截标题,输入框随即恢复可输入,界面看起来像已经答完。服务端把该条助手消息按成功咨询写入会话。
- 触发条件:网页个人咨询,模型为 DeepSeek V4 Flash。计算工具已成功,随后组织回答时输出在句中停止。观测到会话更新约 2.5 分钟后收口,公开回执
stepBudget.truncated=false,客户端收到run.completed。 - 根因:两层。其一,
finish_reason=length与超时中断后的半截正文仍走run.completed→complete_consultation_response,公开回执故意不含modelFinishReason,界面无法区分正常结束与夹断。其二,咨询agent.stream未设可见输出预算,也未关闭 Flash 默认 thinking;隐藏推理与可见正文共用max_tokens,更容易在标题中途length停住。AGENT_TIMEOUT_MS = 110_000与路由maxDuration = 120仍可能在组答阶段掐流,旧逻辑同样把已发出的半截当成功。 - 修复:可见正文在
length结束,或超时/中止时已有输出,改为run.failed/answer_truncated,保留已流出文本,账务走cancel。咨询流设置maxOutputTokens = 8192,并对当前供应商与openai兼容键关闭 thinking。客户端保存半截助手消息并提示未完成、不会扣点,不再要求run.completed才落盘。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts覆盖length与超时半截不得run.completed、不得调用onComplete;consultation-workflow-contract.test.ts锁定输出预算与 thinking disabled;consultation-recovery.test.ts与chat-stream-layout.test.ts锁定半截落盘与提示;agent-observability.test.ts将answer_truncated纳入已知错误码。 - 防复发:有可见正文不等于咨询完成。
finish_reason=length、超时半截不得再映射为run.completed。咨询生成必须显式保留 8192 可见 token 预算。若打开 provider thinking,必须走独立thinking.delta,不得把推理写进answer.delta。公开回执仍不得带modelFinishReason,失败码必须能单独说明夹断。 - 相关记录:BUG-280、BUG-277、BUG-340、BUG-346
- 复发自:无
- 修复版本:待提交
BUG-306 | 普通咨询 Agent 回答下方缺少赞踩复制重跑操作栏
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:普通咨询会话消息列表、
ChatMessageActions、生时校正消息操作栏 - 用户现象:生时纠正每条 Agent 回答下方有赞、踩、复制和重跑图标,普通咨询同一位置没有。
- 触发条件:打开普通咨询会话,查看已完成的助手回答。
- 根因:BUG-049 / BUG-185 只把操作栏接到生时校正消息列表。普通咨询
page.tsx只渲染ChatMessageRow,没有复用同一组操作。 - 修复:抽出共用
ChatMessageActions。普通咨询与生时校正共用赞踩互斥、复制和仅最新回答可重跑。普通咨询重跑去掉最后一条助手消息后走现有/api/consult(会扣点),失败则恢复原文;生时校正仍走免费原位 regenerate。 - 验证:
frontend/tests/chat-message-actions.test.ts、chat-stream-layout.test.ts、rectification-agentic-entry.test.ts。 - 防复发:普通咨询与生时校正的可见操作必须共用同一组件;不得再复制一套图标按钮。源码合同若切片
send()失败回填,必须带着restoreOnFailure守卫,见 BUG-308。 - 相关记录:BUG-049、BUG-094、BUG-185、BUG-308
- 复发自:无
- 修复版本:待提交
BUG-307 | 生时纠正把执行凭证当成结果,正文没有分盘,也看不到宫位表
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:V9 生时纠正对话、
rectification-compare-candidates决策回执、候选快照 - 用户现象:用户看到的是「本轮完成 · N 个步骤 · M 项计算依据」。比较完成后,正文没有一句分盘,也没有十二宫表。
- 触发条件:校正对话完成一轮工具执行,尤其是已经比较候选之后。
- 根因:Activity 折叠占用了分盘披露。提示又把宫位表写成禁止展示。评分已经算出本命盘,但公开投影没有十二宫。
- 修复:对用户隐藏 Activity 折叠和工具过程文案。比较完成后,正文末尾由服务器写一句对照过分盘;界面展示按代表性时间排出的 D1 宫位表。表中只有宫位、星座、入驻,不含经度或坐标。
- 验证:
tests/test_rectification_house_table.py、frontend/tests/rectification-varga-sentence.test.ts、rectification-candidate-result.test.ts、rectification-agentic-entry.test.ts。 - 防复发:生时纠正用户面不得再渲染 CompletedActivityReceipt;分盘句来自公开 executed methods;宫位表来自 decision receipt,缺表不得由模型编造。
- 相关记录:BUG-179、BUG-181、BUG-304
- 复发自:BUG-181(完成凭证成为用户主结果后,分盘和宫位没有独立正文位置)
- 修复版本:
c8af18e9
BUG-308 | 咨询重跑改了草稿回填条件后,源码合同仍切旧 if,staging 质量门 1807/1808
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:Gitea
backend-quality-gate.yml的npm test --prefix frontend、frontend/tests/composer-isolation-contract.test.ts - 用户现象:
c8af18e90a的 validate 在 1808 项前端测试里红 1 项。日志末尾是已通过的 timing-output-guard / truth-source 合同,真正失败是not ok 578 - every external draft writer keeps working through the page-owned setters。 - 触发条件:向
staging推送含 BUG-306 重跑路径的page.tsx。 - 根因:草稿隔离合同用
sourceBetween按字面切if (activeSessionIdRef.current === sessionId) {。重跑把持久化失败回填改成if (!options.restoreOnFailure && activeSessionIdRef.current === sessionId):普通发送失败仍把问题放回输入框,重跑失败则还原被删的助手消息、不改草稿。旧标记在文件里已不存在,indexOf得到-1。产品回填还在,测试锚点过期。 - 修复:切片起点改为当前守卫;继续断言该段含
setDraft(originalQuestion)。 - 验证:
frontend/tests/composer-isolation-contract.test.ts修复前 4/5、修复后 5/5。 - 防复发:切
send()失败回填不得再用无restoreOnFailure的旧 if。源码合同的起止标记必须随被切代码一起改,否则质量门会把无关提交打红。 - 相关记录:BUG-249、BUG-306
- 复发自:BUG-306(加了
restoreOnFailure而未更新草稿隔离切片) - 修复版本:
d434aa48
BUG-309 | Docker npm run build 因会话 schema 与动态 select 类型检查失败
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:
deploy/railway-web.Dockerfile的RUN npm run build、POST /api/sessions、POST /api/daily-starlanguage、POST /api/synastry - 用户现象:staging publish 在 web 镜像构建失败。BuildKit 日志末尾是
skill-package-registry.ts的 Import traces(动态文件访问追踪警告),真正失败是Failed to type check。 - 触发条件:向
staging推送后走 publish 镜像构建。push 上的 validate 跳过npm run build,因此质量门绿、镜像红。 - 根因:两处互不相关的 TypeScript 收口。
limitTranscriptSize()的泛型下界是{ messages: { text: string }[] },.superRefine()之后推断输出被收成这个下界,parsed.data不再有id,也丢失消息上的 receipt 字段。每日星语与合盘把出生列.join(",")成普通string再交给 supabase-js,select结果变成GenericStringError,无法传入AccountBirthRow。 - 修复:
limitTranscriptSize按输入 schema 的Output原样返回z.ZodType<Output>。出生列改为共享字面量ACCOUNT_BIRTH_SELECT,globalBirthProfileFromAccountRow接受unknown再收成账户行。 - 验证:
npx tsc --noEmit无错误(修复前 3 个 route + 2 个测试文件)。tsx --test tests/chat-session-write.test.ts tests/server-owned-birth-profile.test.ts tests/daily-starlanguage.test.ts tests/chart-library-other-profile.test.ts36/36。 - 防复发:创建会话 schema 必须保留
id;出生资料select不得用.join(",")得到非字面量字符串。staging push 的 validate 不跑next build,类型回归只在 publish Docker 暴露。 - 相关记录:BUG-261、BUG-308
- 复发自:无
- 修复版本:
d7887b03
BUG-310 | 生时纠正宫位表贴着头像左缘,没有和 Agent 正文对齐
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:生时纠正对话里的
RectificationHouseTableView、候选时间卡、已采用时间说明 - 用户现象:Agent 正文和赞踩操作栏缩在头像右侧,本命宫位标题和表格贴着对话左缘,和头像齐平。
- 触发条件:校正对话出现本命宫位表。
- 根因:宫位表是
.message-list里消息行的兄弟节点,不走.message-assistant的头像 +gap列,也没有补上同一条起始线。 - 修复:当时用
--assistant-content-inset把头像宽加 gap 补到表上,让它先和正文齐。后续在 BUG-311 改成快照卡片,不再用消息缩进冒充气泡。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts曾锁定 inset;现由 BUG-311 的快照卡片合同接替。 - 防复发:宫位表不是聊天气泡。不要再把它缩进成 Agent 正文;结果块要用独立卡片表面。
- 相关记录:BUG-307、BUG-311
- 复发自:BUG-307(表作为消息兄弟落地,未接入 assistant 列)
- 修复版本:
053e63ef
BUG-311 | 生时纠正宫位表被当成聊天正文,而不是随线索更新的当前盘面
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:生时纠正对话的宫位表
- 用户现象:本命宫位出现在最后一条 Agent 回答下面,看起来像那条回复的一部分;补充经历后表格其实会整表替换,界面没有把这一点说清楚。
- 触发条件:校正已经有 Candidate Snapshot,对话里同时出现 Agent 正文和宫位表。
- 根因:表来自 Case 快照、挂在消息列表末尾,却按聊天气泡的起始线排版,标题也只说“本命宫位”。
- 修复:宫位表单独放在消息列表后的
rectification-snapshot,表面与结果卡同类。标题改为“当前本命宫位”,并写明补充经历后会按新线索重算。时间选择卡不再进这个快照,见 BUG-312。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts锁定快照容器在ChatMessageRow之外、不含候选卡、aria-live、重算文案,以及成功回合后loadCandidate。 - 防复发:宫位表必须在消息气泡外,并作为聊天右侧工作台原地刷新,不得再挂在消息列表底部。文案必须说明随新线索重算。候选卡仍用
--assistant-content-inset对齐 Agent 正文;宫位表不再用 inset 冒充气泡。 - 相关记录:BUG-307、BUG-310、BUG-312、BUG-322
- 复发自:BUG-310(对齐 Agent 正文后更像一条聊天)
- 修复版本:
4d872c68
BUG-312 | 生时纠正时间选择卡在线索未齐时直接出现,且不在 Agent 气泡下方
- 状态:resolved
- 首次发现:2026-08-19
- 最近更新:2026-08-19
- 影响面:生时纠正对话的候选时间卡、
selectionAllowed、Case 快照 - 用户现象:当前可能的出生时间作为独立盘面模块直接出现;没有等线索齐到可以给出代表性时间,也不挂在刚说完的 Agent 气泡下面。
- 触发条件:Case 已有快照或宫位表;引擎尚未
selection_allowed,或 Agent 回合仍在生成。 - 根因:候选卡和宫位表被捆在同一块始终可见的
rectification-snapshot里。宫位表应当随线索更新;时间选择是“可以给出代表性时间”之后的采用动作,应对齐当轮 Agent 回复。 - 修复:候选卡只在
selectionAllowed、存在最近一条已落地且未失败的 Agent 正文、且当前不 busy/不重跑时,画在该条气泡和操作栏下方。生成中隐藏,避免挂在上一条气泡下。--assistant-content-inset只用于这块卡片,不用于宫位表。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts锁定候选卡在消息循环内、操作栏之后、快照容器之外,以及selectionAllowed/latestOfferMessageKey/!busy门。 - 防复发:时间选择卡不得进
rectification-snapshot;不得在selectionAllowed之前、回合生成中、或尚未rectification-offer-candidates时显示。宫位表仍是消息外的 live 快照。 - 相关记录:BUG-120、BUG-179、BUG-304、BUG-311、BUG-313
- 复发自:BUG-120(过早出示采用卡);BUG-311(与宫位表捆成始终可见模块)
- 修复版本:
4d872c68
BUG-313 | 相邻分钟还分不开时,生时纠正就把时间选择卡提前拿出来
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正候选卡、
next_user_action、rectification-offer-candidates - 用户现象:相对支持度约 34 / 33 / 33 的相邻分钟已经出现“当前可能的出生时间”采用卡。文案自己也说还不能确认唯一分钟,但采用动作已经摆在对话里。
- 触发条件:比较已经跑过,引擎
selection_allowed=true,方法层仍有next_followup(例如关系/事业还能区分),Agent 还在收集。 - 根因:
selection_allowed只表示可以采用代表性时间。UI 用它直接画卡;buildNextUserAction也在仍有下一问时把本轮动作写成 adopt,并把 follow-up 推迟。相邻分钟平台因此被当成“现在就选”。 - 修复:仍有
next_followup时会话留在收集,本轮继续问;用户停止才走 adopt。界面只在最近一条已落地 Agent 回复完成rectification-offer-candidates且selectionAllowed时,把卡片挂在该气泡下方。 - 验证:
frontend/tests/rectification-eight-method.test.ts锁定“有 follow-up 时 ask、停止才 adopt”;frontend/tests/rectification-agentic-entry.test.ts锁定 offer-candidates 门。 - 防复发:
selection_allowed不得单独出示采用卡。卡片必须等本轮rectification-offer-candidates。有next_followup时不得 offer。 - 相关记录:BUG-120、BUG-304、BUG-312
- 复发自:BUG-120(过早出示采用卡);BUG-312(用
selectionAllowed当出示门) - 修复版本:
a732ff4b
BUG-314 | 生时纠正宫位表比 Agent 正文更宽、更靠左
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正
rectification-snapshot、RectificationHouseTableView - 用户现象:当前本命宫位灰卡从对话左缘铺开,比上方 Agent 正文更宽、更靠左,无法和回答左缘对齐成一条竖线。
- 触发条件:校正对话出现本命宫位快照。
- 根因:宫位表改成消息外的 snapshot 卡后,容器不再吃
--assistant-content-inset,仍按整列message-list铺宽;Agent 正文在头像加 gap 之后。 - 修复:snapshot 与候选卡同一列:
width: calc(100% - var(--assistant-content-inset)),margin-inline-start用同一 inset。表本身保持卡片描边和内边距,不用padding-inline-start冒充气泡。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts锁定 snapshot 与候选卡共用 inset 宽度和起始线,且.rectification-house-table不写 inset。 - 防复发:宫位表在右侧工作台内,不再与 Agent 正文同列对齐。候选时间卡仍用
--assistant-content-inset对齐气泡。不得把工作台再缩进成消息列。 - 相关记录:BUG-310、BUG-311、BUG-322
- 复发自:BUG-311(去掉消息缩进后未收回正文列)
- 修复版本:
a732ff4b
BUG-315 | Docker npm run build 因候选卡消息可能为 undefined 类型检查失败
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:
deploy/railway-web.Dockerfile的RUN npm run build、生时纠正时间选择卡 - 用户现象:staging publish 在 web 镜像构建失败。BuildKit 日志末尾是
skill-package-registry.ts的 Import traces,真正失败是Failed to type check:latestOfferMessageKey可能为undefined。 - 触发条件:向
staging推送后走 publish 镜像构建。push 上的 validate 跳过npm run build,因此质量门绿、镜像红。 - 根因:
showSelectionCards用Boolean(...)包了一层,TypeScript 不会因此收窄find()的T | undefined。随后showSelectionCards ? latestOfferMessageKey.renderKey在 true 分支仍可能是undefined,next build类型检查退出 1。 - 修复:变量改名为实际类型
latestOfferMessage;取renderKey时用showSelectionCards && latestOfferMessage收窄。 - 验证:
./node_modules/.bin/tsc --noEmit无错误。tsx --test tests/rectification-agentic-entry.test.ts锁定收窄写法。 - 防复发:对
find()结果取属性不得只靠外层Boolean(...);staging push 的 validate 不跑next build,类型回归只在 publish Docker 暴露。 - 相关记录:BUG-309、BUG-312、BUG-313
- 复发自:BUG-309(validate 跳过
next build;日志末尾 Import traces 掩盖类型检查失败) - 修复版本:待提交
BUG-316 | 家人问了也不改候选;外貌/胎记被硬禁止追问
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正评分引擎、
method_followup_plan、Skilljyotish-birth-time-rectification@10.0.4 - 用户现象:已经在问家人变化,候选几乎不动。外貌/胎记根本不问。同一件事业经历看不出同时用了本命第 10 宫和 D10。
- 触发条件:写入带日期的
family_event;或追问走到家人之后;或事业事件进入评分。 - 根因:
family_event被标成背景、不进引擎;D12 未进候选分盘。Skill 10.0.3 与 Agent 提示禁止问外貌/胎记。事业虽已按 D1 第 10 宫 + D10 计分,公开方法层没有同时标出。 - 修复:家人事件可评分(D12 + 三/四/五/九宫)。工具默认
subject=self时按领域纠成family,否则旧家人证据仍进不了引擎。外貌/胎记可问;无日期只覆盖访谈,有日期进上升/一宫辅助降权,不当主评分,也不计入采用/确认的领域数。同一事业事件公开方法层同时给出d1-rashi与d10-dashamsa。新 Case 绑定 Skill 10.0.4。已有 10.0.3 Case 保持原绑定。 - 验证:
tests/test_rectification_family_appearance_scoring.py(含 D12 真实引擎行);frontend/tests/rectification-eight-method.test.ts家人之后问外貌/胎记,占问仍跳过。 - 防复发:
family_event不得再列入背景-only;家人领域必须把 subject 纠成 family。do_not_poll只保留 horary。外貌不得重新变成主评分或 D9/D10 类型标签。 - 相关记录:BUG-291
- 复发自:无
- 修复版本:待提交
BUG-317 | D4 精度阶段混问家人;D5/D7 不算分;采用后不重算也不给技法审计
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正精度阶段、家人/教育计分、采用后本命表、Skill
jyotish-birth-time-rectification@10.0.5 - 用户现象:居所盘换升时却被问家人。子女/伴侣细节和学业成就问了也难改候选。采用某个分钟后,宫位表仍停在代表性时间,也看不到本轮技法执行/阻塞。
- 触发条件:窗口扫描出现 D4 换升;写入家人或教育事件;或采用非代表性候选。
- 根因:
theme_refine把 D4 与家人混成一问。引擎分盘不含 D5/D7,家人只计 D12。采用后界面不按采用分钟切宫位表,也不投影执行账本。 - 修复:精度阶段改为本命上升 → D9 → D10 → D4 居所 → D5 成就。家人走 D12+D7 方法覆盖,不混进 D4。教育事件同时公开 D24 与 D5。采用后按该分钟重算本命宫位,并折叠展示技法审计;KP / VedAstro / 唯一分钟确认保持 blocked。新 Case 绑定 Skill 10.0.5。已有 10.0.4 Case 保持原绑定。
confirmation_allowed仍为 false。 - 验证:
tests/test_rectification_refinement_packet.py(D4 不再是 theme_refine;按候选分钟重算宫位;技法审计含 blocked 唯一分钟);tests/test_rectification_family_appearance_scoring.py(D7/D5 进入可用层);frontend/tests/rectification-eight-method.test.ts(d4 问搬家、d5 问学业);frontend/tests/rectification-candidate-result.test.ts(采用分钟切换宫位表)。 - 防复发:D4 精度阶段不得再问家人。家人计分必须含 D7。采用后宫位表必须跟采用分钟走。技法审计不得把未执行层写成已执行,也不得声称唯一分钟。
- 相关记录:BUG-316
- 复发自:无
- 修复版本:a93a3cc1
BUG-318 | 校时 VedAstro 已跑仍显示尚未评估;确认门写死 false,与密封 holdout 合同脱节
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正确认门、Technique Audit、V9
/v5/vedastro-validate接线、Skilljyotish-birth-time-rectification@10.0.6 - 用户现象:采用后技法审计里 VedAstro 一行仍写「尚未评估」,即使评分路径已经跑过官方分钟快照。界面看不到确认门三行 blocker。
confirmation_allowed在引擎里写死 false,与公开 AA holdout 合同各写各的。 - 触发条件:本地采用门通过且存在两个不同 HH:MM 候选;或打开确认门/技法审计。
- 根因:
build_technique_audit在 attach 之前写死 VedAstro=blocked。attach 只改external_validation_status,不回写审计行。Python 确认门不读密封 holdout。V9 比较候选后不调用/vedastro-validate。SearchEvents 若被当成确认判据会复发 BUG-303。 - 修复:attach 后按
passed/failed/not_evaluated回写 VedAstro 审计行;超时保持 not_evaluated,不等于 fail。确认门改为真实 AND(引擎 granted + 相邻可分 + VedAstro passed + 密封 holdout ready)。前后端同读references/rectification_sealed_holdout.v1.json,当前仍为 1/20not_ready。V9 在selection_allowed且有 primary/runner-up 时调用/vedastro-validate;SearchEvents 只作旁证,不得改confirmation_allowed。结果卡折叠展示确认门三行。新 Case 绑定 Skill 10.0.6;已有 10.0.5 Case 保持原绑定。 - 验证:
tests/test_rectification_v5_vedastro_validation.py;tests/test_rectification_confirmation_and.py;frontend/tests/rectification-confirmation-gate.test.ts;frontend/tests/rectification-v9-engine-contract.test.ts;frontend/tests/rectification-candidate-result.test.ts。 - 防复发:VedAstro 审计行必须与
external_validation_status一致。超时不得写成 fail。SearchEvents 赢了本地第一名不得放行确认。不得把 LOEO/LODO 或空 intake 写成 holdout ready。不得在密封集未达标时把confirmation_allowed设为 true。 - 相关记录:BUG-303、BUG-317
- 复发自:BUG-303(分钟快照已接到 score,但产品审计与确认门未消费该状态)
- 修复版本:55d3119c
BUG-319 | 教育精度忽略 D24 换升;家人事件不算 D3;窗口扫描不展示细分钟层
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正窗口扫描、
d5_refine、家人评分、Skilljyotish-birth-time-rectification@10.0.7 - 用户现象:学业分盘 D24 已计分,但窗口里 D24 换升不会进入学业追问。带日期的家人事件只对照 D12/D7,不对照 D3。候选卡看不到 Nakshatra pada / Hora Lagna / Ghati Lagna 换升。
- 触发条件:教育事件已计分且 D5 升不变、D24 升变化;或提交带日期的家人事件;或窗口内升点跨 Nakshatra pada / 真实日出后的 Hora/Ghati。
- 根因:
window_scan/precision_stage只看 D1/D9/D10/D4/D5/D7/D12。DOMAIN_CONFIG["family"]只有 D12/D7。细分钟层未写入候选 feature。 - 修复:D24 换升并入既有
d5_refine,不新增阶段。家人评分增加 D3。Pada 始终写入窗口扫描;Hora/Ghati 仅在真实日出可算时写入,不用 06:00 假日出,且不阻断ready_to_adopt、不打开确认门。新 Case 绑定 Skill 10.0.7;已有 10.0.6 Case 保持原绑定。评分缓存身份升为rectification-v5-matrix-scoring-5。 - 验证:
tests/test_rectification_refinement_packet.py;tests/test_rectification_family_appearance_scoring.py;tests/test_rectification_technique_contract.py;frontend/tests/rectification-eight-method.test.ts;frontend/tests/skill-registry.test.ts。 - 防复发:D24 单独换升必须得到
d5_refine/education_style。家人public_technique_layers必须含d3-drekkana。Pada/Hora/Ghati 换升只展示。不得把 D16/D20/D27/D40/D45/D60、KP、Gulika、Kunda 或 Nadiamsa 当作确认层。不得用假日出填 Hora/Ghati。 - 相关记录:BUG-317、BUG-318
- 复发自:无
- 修复版本:70efb569
BUG-320 | 财务/健康要用户主动说才进访谈;Bhava/Pranapada 不进换升表
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正方法覆盖、窗口扫描、Skill
jyotish-birth-time-rectification@10.0.8 - 用户现象:D2/D11 财务和 D30 健康已能计分,但访谈不主动追问,几乎只在用户自己开口后才有事件。候选卡看不到 Bhava Lagna / Pranapada Lagna 换升。
- 触发条件:关系/事业/家人已覆盖后继续访谈;或窗口内 Bhava/Pranapada 换升。
- 根因:
method_followup_plan.not_in_rotation把 finance/health 和迁居一起排除。窗口扫描不读 D2/D11/D30,也不写 Bhava/Pranapada。 - 修复:财务(D2+D11)和健康(D30)进入方法覆盖,家人之后、外貌之前追问。仍不得按 SQL
missing_evidence_categories轮询。D2/D11/D30 换升只驱动观察追问,不新增精度阶段、不阻断ready_to_adopt。Bhava 用本命日月;Pranapada 仅在真实 Hora+Ghati 可算时写入。新 Case 绑定 Skill 10.0.8;已有 10.0.7 Case 保持原绑定。 - 验证:
tests/test_rectification_refinement_packet.py;tests/test_rectification_family_appearance_scoring.py;frontend/tests/rectification-eight-method.test.ts;frontend/tests/skill-registry.test.ts。 - 防复发:家人之后下一问必须是财务,再健康,再外貌。D11 或 D30 单独换升不得改
precision_stage。不得把财务/健康改回 SQL 类别轮询。不得用假日出填 Hora/Ghati/Pranapada。 - 相关记录:BUG-291、BUG-319
- 复发自:BUG-291(迁居继续不轮询;财务/健康被一并排除后不再被方法层追问)
- 修复版本:1b93afc5
BUG-321 | Docker npm run build 因宫位表 && 与八方法测试字面量类型检查失败
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:
deploy/railway-web.Dockerfile的RUN npm run build、rectification-candidate-result.ts、rectification-eight-method.test.ts - 用户现象:staging publish 在 web 镜像构建失败。BuildKit 日志末尾是
skill-package-registry.ts的 Import traces。 - 触发条件:向
staging推送 Skill 10.0.8 后走 publish 镜像构建。push 上的 validate 跳过npm run build。 - 根因:日志末尾 Import traces 只是动态文件访问追踪警告,真正失败是
Failed to type check。selectedTime && houseTablesByTime[selectedTime]在string | null下会被推断成"" | HouseTable | null,??不能消掉空字符串。八方法测试对do_not_poll传入"appearance",并对观察层比较永远不存在的"d24"/"d2"。tsconfig包含tests/**/*.ts,因此这些测试错误也会挡住next build。 - 修复:宫位表改用三元取值;测试改为断言
do_not_poll恰好是horary,并用完整观察层列表证明 D24/D2 不单独成层。 - 验证:
./node_modules/.bin/tsc --noEmit;npx tsx --test tests/rectification-eight-method.test.ts。 - 防复发:对
string | null做查找不得用&&当存在性守卫。readonly ["horary"]的.includes()只能传入"horary"。staging push 的 validate 仍不跑next build,类型回归只在 publish Docker 暴露。 - 相关记录:BUG-309、BUG-315、BUG-320
- 复发自:BUG-309(validate 跳过
next build;日志末尾 Import traces 掩盖类型检查失败) - 修复版本:0eebf971
BUG-322 | 生时纠正宫位表和换升观察仍埋在消息列表底部
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正对话布局、
rectification-snapshot、换升时刻 - 用户现象:本命宫位和换升时刻跟在消息列表下面。每次补充经历后表会重算,但要滚过整段对话才能看见变化。
- 触发条件:校正已有 Candidate Snapshot,继续在对话里补充经历。
- 根因:BUG-311 把宫位表移出气泡,但仍放在
.message-list末尾。换升时刻按图层一行一条平铺,不适合侧栏。 - 修复:生时纠正单独做成聊天 | 工作台分栏。宫位表、按分钟合并的换升、确认门和大运对照放在右侧并原地刷新。候选时间卡和“用这个时间看盘”仍留在对话里。窄屏用“当前盘面”打开对话框,不挤第二列。普通咨询 session 不改。
- 验证:
frontend/tests/rectification-agentic-entry.test.ts;frontend/tests/rectification-board-model.test.ts。 - 防复发:
rectification-snapshot不得回到消息循环。换升不得再按user_meaning一层一行平铺。.conversation:not(.is-rectification)的 session 列宽不得套到校正对话。 - 相关记录:BUG-311、BUG-312、BUG-314
- 复发自:BUG-311(移出气泡后仍挂在消息列表底部)
- 修复版本:e07d1bad
BUG-323 | 访谈还在收集时,生时纠正就把时间选择卡提前拿出来
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正候选卡、
session_outcome、rectification-compare-candidates、rectification-offer-candidates、Skilljyotish-birth-time-rectification@10.0.9 - 用户现象:只有学业和感情两块经历、比较已经跑过时,对话里已经出现“当前可能的出生时间”采用卡。文案仍说还不能确认唯一分钟,但卡片已经摆出来。事业、家人、财务、健康都还没问。
- 触发条件:引擎
selection_allowed=true(事件≥3、领域≥2),方法覆盖仍有挡住出牌的下一问(例如事业),用户也没有说“暂时想不到了 / 没有更多 / 先这样”。Agent 在 compare 之后调用 offer-candidates。 - 根因:采用门和提出门被合成一个。
selection_allowed只表示可以采用代表性时间。compare 的session_outcome不看next_followup,offer-candidates 只要有快照就可出卡。追问顺序先重复已覆盖领域的精度阶段,事业等未覆盖领域被饿死。KP_cusps被写死进missing_layers,又被当成确认门必需层,不能用engine_granted当出示卡片的门槛。 - 修复:引擎增加
propose_allowed(事件≥4、领域≥3、唯一领先、宽度≤5、诊断稳定;KP 政策跳过不挡提出门)。大运冲突和 20% 边距仍只挡唯一分钟确认。compare / read-case / offer 共用同一套会话结果:仍有挡住出牌的下一问时保持收集;用户喊停且已过采用门时可走逃生舱。offer 在会话不是 adopt/awaiting_confirmation 时拒绝,且不把 Case 改成candidate_ready。方法覆盖(感情→事业→家人→财务→健康)先于已覆盖领域的精度追问。外貌/疤痕不挡出牌。新 Case 绑定 Skill 10.0.9;已有 10.0.8 Case 保持原绑定。 - 验证:
tests/test_rectification_confirmation_and.py;frontend/tests/rectification-eight-method.test.ts;frontend/tests/rectification-confirmation-gate.test.ts;frontend/tests/skill-registry.test.ts。 - 防复发:
selection_allowed不得单独出示采用卡。有挡住出牌的next_followup时session_outcome必须保持collect_evidence,offer 必须拒绝。不得改已哈希的 10.0.8 包。不得把 KP 政策跳过写成 KP 已执行。 - 相关记录:BUG-120、BUG-297、BUG-313
- 复发自:BUG-313(有 follow-up 时仍可因 compare 投影为 adopt 而 offer);BUG-297(compare/offer 不共享提出门)
- 修复版本:7dc97a49
BUG-324 | 侧栏合同仍断言固定 chat-panel class,staging frontend 测试失败
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:Gitea
Jyotish Skill CI/backend-quality-gate的npm test --prefix frontend、frontend/tests/sidebar-contract.test.ts - 用户现象:staging 推送后质量门禁失败。日志末尾
truth source identity两条都是ok,摘要为# tests 1833 / pass 1832 / fail 1。 - 触发条件:向
staging推送含 BUG-322 生时纠正分栏布局的提交后跑 frontend 全量测试。 - 根因:BUG-322 把
SidebarInset的 class 改成chat-panel加上rectificationSurfaceOpen时的is-rectification。侧栏源码合同仍匹配固定的className="chat-panel",所以not ok 1676 - composes the chat page with the app sidebar shell。 - 修复:侧栏合同改为锁定当前模板 class,并继续要求
inert={modalOpen}。 - 验证:
npx tsx --test tests/sidebar-contract.test.ts。 - 防复发:改
SidebarInsetclass 时必须同步sidebar-contract;生时纠正分栏 class 另由rectification-agentic-entry锁定。 - 相关记录:BUG-322
- 复发自:无
- 修复版本:3970410e
BUG-325 | 经典八方法访谈被财务/健康轮询替换,KP 宫头被政策跳过冒充已观察
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正方法覆盖、
method_followup_plan、KP 宫头观察、窗口扫描、Skilljyotish-birth-time-rectification@10.0.10 - 用户现象:家人问完后下一问变成财务/健康,职业和占问不在访谈里。事业带日期事件被当成已经覆盖职业。KP 技法审计写成政策跳过,候选卡也看不到 KP 子主换升。
- 触发条件:新 Case 走方法覆盖;或比较候选并查看技法审计 / 换升时刻。
- 根因:BUG-320 把财务/健康放进方法轮询,职业并进 D10,Horary 固定
skipped_by_policy。本命盘用 Equal houses,事件引擎把KP_cusps写死进blocked_layers再并入评分missing_layers;BUG-323 用政策跳过让提出门不挡,但没有单独算 Placidus + Krishnamurti 宫头。 - 修复:方法覆盖恢复感情→事业→家人→外貌→疤痕→职业→占问。职业挡出牌,且不由事业事件自动覆盖。占问只问一次、不挡出牌;有问起时间则观察重算,失败写成 blocked 观察。财务/健康离开轮询,用户主动说仍计
D2/D11、D30并保留换升。KP 用 Swiss Ephemeris Placidus + Krishnamurti 单独快照,写入窗口扫描kp1/4/7/10和诚实技法审计;不计分、不挡提出门或确认门。新 Case 绑定 Skill 10.0.10;已有 10.0.9 Case 保持原绑定。评分缓存身份升为rectification-v5-matrix-scoring-6。 - 验证:
tests/test_rectification_kp_cusp_observation.py;tests/test_rectification_confirmation_and.py;tests/test_rectification_horary_observation.py;tests/test_rectification_family_appearance_scoring.py;tests/test_rectification_v5_services.py;frontend/tests/rectification-eight-method.test.ts;frontend/tests/rectification-ingest-p0.test.ts;frontend/tests/skill-registry.test.ts。 - 防复发:家人之后下一问必须是外貌,不得再是财务。事业证据不得把 occupation 标成 covered。Horary 不得挡 offer。KP 不得再写死进评分
missing_layers,也不得把政策跳过写成已观察。不得改已哈希的 10.0.8 / 10.0.9 包。不得把 KP 观察计分或打开确认门。 - 相关记录:BUG-291、BUG-320、BUG-323
- 复发自:BUG-320(财务/健康进入方法轮询是当时的产品决定,本记录故意改回经典八方法);BUG-323(KP 政策跳过只解决提出门,没有做出真实观察)
- 修复版本:84e6191f
BUG-326 | 窄屏盘面对话框在 render 里写 ref,staging ESLint 失败
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:Gitea
backend-quality-gate的npm run lint --prefix frontend、frontend/src/components/rectification-board.tsx - 用户现象:staging 推送后质量门禁失败。摘要为
✖ 11 problems (1 error, 10 warnings)。 - 触发条件:向
staging推送含窄屏<dialog>盘面的提交后跑 frontend lint。 - 根因:
react-hooks/refs禁止在 render 期间更新ref.current。窄屏盘面用onCloseRef.current = onClose保存最新关闭回调,触发Cannot update ref during render。另外 10 条是既有 unused-vars / exhaustive-deps warning,不构成这次失败。 - 修复:关闭监听改为在 effect 里直接调用
onClose,并把onClose放进依赖。 - 验证:
npx eslint src/components/rectification-board.tsx;npx tsx --test tests/rectification-agentic-entry.test.ts。 - 防复发:盘面对话框不得在 render 里写
ref.current;源码合同锁定 close 监听只出现在 effect。 - 相关记录:BUG-322
- 复发自:无
- 修复版本:dbbcb71b
BUG-327 | Web 对话与生时纠正仍露出原生滚动条
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:普通 session
.conversation、生时纠正消息列表、右侧盘面.rectification-board__body - 用户现象:聊天和生时纠正内容超出高度时,直接出现浏览器原生滚动条,和侧栏已有的安静 overlay 滚动条不一致。
- 触发条件:普通对话或生时纠正历史超过可视高度,在桌面 Chromium / Firefox 中滚动。
- 根因:只有侧栏
SidebarContent使用产品内滚动条;会话区和盘面仍是overflow-y: auto并保留scrollbar-gutter,因此继续露出系统滚动条。 - 修复:会话列表、普通对话和生时纠正盘面共用同一套安静 overlay 滚动条:透明轨道、无 gutter、默认隐藏拇指,悬停或键盘焦点后显示低对比圆角拇指。
- 验证:
frontend/tests/sidebar-contract.test.ts、frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:独立滚动容器必须同时覆盖标准 scrollbar 属性和 WebKit 伪元素,且不得再用
scrollbar-gutter给系统滚动条留槽。 - 相关记录:BUG-042
- 复发自:BUG-042(只修了当时的生时纠正消息区,后来的 session 列宽和盘面分栏又回到原生滚动条)
- 修复版本:994f0583
BUG-328 | 移动端盘面以模态挡住聊天且关闭入口弱
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正窄屏盘面、
rectification-board - 用户现象:打开“当前盘面”后,白色面板盖住大部分聊天,几乎读不到刚才的对话;关闭只能靠一段会折行的文字按钮。
- 触发条件:窄于分栏阈值的视口打开宫位表。
- 根因:窄屏用
showModal()对话框加不透明 backdrop,并把聊天设为inert;高度约 86dvh / 100dvh,聊天被遮死。 - 修复:窄屏改为工作区分栏下半屏 sheet(约 46% 高),聊天留在上半屏可继续阅读;关闭改为 44px 图标按钮,Escape 仍可关闭。
- 验证:
frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:关闭控件必须是独立 44px 目标并带
aria-label="关闭盘面"。窄屏分层策略以 BUG-330 为准:必须用高于输入框的覆盖层,不得再与输入框做同层网格分栏。 - 相关记录:BUG-322、BUG-326、BUG-330
- 复发自:BUG-322(分栏后窄屏改成对话框,聊天被挡住)
- 修复版本:994f0583
BUG-330 | 移动端盘面与输入框同层叠字,无法作为弹出层使用
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正窄屏盘面、
rectification-board、输入框 - 用户现象:点开“当前盘面”后,盘面标题与输入区“当前盘面”文案叠在一起;宫位表像嵌在输入框同一层,而不是从下往上盖住聊天列表和输入框。
- 触发条件:窄于分栏阈值的视口打开宫位表。
- 根因:BUG-328 把窄屏盘面改成工作区第二行(约 46% 高)且未设叠层;输入框
.composer-wrap仍是position: relative; z-index: 2。盘面与输入框在同一工作区网格里重叠时,输入框画在盘面标题之上。 - 修复:仅在视口宽度小于 768px 的移动端,把盘面改成工作区内
z-index: 20的底部 sheet 覆盖层;桌面网页仍是右侧分栏,不出现底部弹出。遮罩点击、Escape、44px 关闭按钮可关;聊天列在打开时inert。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:底部 sheet 只允许
matchMedia(max-width: 767px)触发;工作区必须isolation: isolate且z-index: 0,打开时隐藏 composer(含backdrop-filter),覆盖层 z-index 必须高于输入框。不得用聊天区clientWidth把桌面侧栏挤窄误判成移动端。 - 相关记录:BUG-322、BUG-326、BUG-328
- 复发自:BUG-328(当时为了露出聊天改成下半屏分栏,叠层低于输入框)
- 修复版本:9670b661
BUG-331 | Raman 默认岁差后,Lahiri 校准的 CLI smoke 仍按旧盘面断言
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:Gitea
backend-quality-gatePython quick quality gate、tests/test_cli_smoke.py、tests/golden/golden_cases.json只被 JSON 校验通过后进入同一套 pytest - 用户现象:质量门禁打印
json ok tests/golden/golden_cases.json后继续跑audit_*/validate_bphs_invariants/pytest tests/test_cli_smoke.py ...。JSON 本身有效;失败发生在 CLI smoke:Dasha 起始时刻、D9 Jupiter 尊严、Darakaraka 行星与 Navamsa 敌友标签对不上。 - 触发条件:产品默认岁差改为 Raman 后,未给 Lahiri 校准样本显式传
--ayanamsa lahiri,再跑 quick quality gate。 - 根因:这些断言锁的是旧 Lahiri 盘面(例如
1952-03-19T20:24:47、NEECHA_BHANGA、DK=Sun)。默认 Raman 后月亮分宿与分盘换升改变,断言失败。golden_cases.json的 SAV=337 等不变量与岁差无关,JSON 校验和 golden runner 仍通过。 - 修复:Lahiri 校准的 dignity / Dasha / DK 样本显式传
--ayanamsa lahiri。产品默认仍是 Raman;Raman 元数据测试继续显式传raman。 - 验证:
tests/test_cli_smoke.py中上述失败项;tests/test_cli_smoke.py::test_full_reading_golden_cases_cover_user_ready_output。 - 防复发:对照样本必须显式写岁差名,不得依赖引擎默认;改
DEFAULT_AYANAMSA_NAME后必须跑tests/test_cli_smoke.py与 quick quality gate 的 CORE pytest 列表。 - 相关记录:无
- 复发自:无
- 修复版本:4b02f63a
BUG-332 | 校时 skill 呈现改完后,前端质量门仍锁旧分盘句禁令和透传分数账本
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:Gitea
backend-quality-gate的npm test --prefix frontend、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/rectification-candidate-result.test.ts、公开event_dasha_ledger - 用户现象:质量门
tests 1840中pass 1838/fail 2。日志末尾是已通过的 health truth-source 合同;真正失败是校时 Agent 提示合同和候选账本解析。 - 触发条件:把出牌/采用轮改成粘贴
skill_verification_report,并把账本user_meaning透传引擎原文后,再跑完整前端套件。 - 根因:
rectification-agentic-entry仍要求提示词写「分盘句和宫位表由界面展示」,与现行「验证报告进正文、宫位表仍由盘面展示」冲突。parseEventDashaLedger改为原样采用user_meaning后,公开账本会带上12.5 points一类评分泄漏。 - 修复:入口合同改为锁定
skill_verification_report/ D9-D10 类型对照,并明确不得再写旧分盘句禁令。公开账本只从summary、匹配档和结构化gochara重建文案,拒绝points泄漏。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/rectification-v9-agent.test.ts、frontend/tests/rectification-candidate-result.test.ts、frontend/tests/rectification-eight-method.test.ts。完整npm test --prefix frontend由 Gitea quality gate 验收。 - 防复发:校时提示词合同必须与
rectification-v9-agent同向;公开 ledger/window copy 不得透传可能含分数或星座类型标签的引擎user_meaning。 - 相关记录:BUG-307、BUG-308、BUG-331
- 复发自:BUG-307(当时把分盘句和宫位表从正文挪到界面后,入口合同锁死了那句提示词)
- 修复版本:379f0e6f
BUG-333 | staging 发布 API 镜像时 DaoCloud TLS 握手超时
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:Gitea
backend-quality-gate的publishjob、deploy/railway-api.Dockerfile、xiaoxinrunner 拉官方基础镜像 - 用户现象:
validate通过后Build and publish exact-SHA ACR images在 API Dockerfile 第一行失败:Head "https://m.daocloud.io/v2/docker.io/library/python/manifests/3.12-slim"TLS handshake timeout。 - 触发条件:向
staging推送后 publish 构建deploy/railway-api.Dockerfile。 - 根因:API 基础镜像仍走 DaoCloud 代理。同一 runner 已验证可达华为 SWR / ECR / ACR;Web 镜像早已改用
swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/library/node:22-alpine,API 未跟上。 - 修复:API
FROM改为与 Web 相同的华为 SWRdocker.io/library路径,指向python:3.12-slim。不改运行时端口、pip/apt 镜像或 ACR 目标仓库。 - 验证:
tests/test_railway_deployment.py、frontend/tests/staging-backend-workflows.test.ts。远端 publish 由本提交后的 Gitea quality gate 验收。 - 防复发:API/Web 官方基础镜像必须走华为 SWR
ddn-k8s/docker.io/library,合同禁止daocloud。不要为了重试再加一套for attempt in 1 2 3,workflow 里该循环次数已被合同锁死为 5。 - 相关记录:BUG-149、BUG-150、BUG-332
- 复发自:无
- 修复版本:9c4b64c9
BUG-334 | 移动端盘面覆盖层仍被输入框 backdrop-filter 盖住
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正窄屏盘面、
.composer-wrap、.rectification-board-overlay - 用户现象:打开“当前盘面”后,输入区预览条仍叠在弹层标题上;覆盖层和输入框像同一层,聊天列表也被挡住看不清。
- 触发条件:移动端视口打开宫位表。iOS Safari 上更明显。
- 根因:
.composer-wrap带z-index: 2和backdrop-filter。iOS 会把该滤镜层合成到覆盖层之上。BUG-330 只把 overlay 设为z-index: 20,工作区没有独立层叠上下文,也没有在打开时关掉输入框滤镜。 - 修复:工作区
isolation: isolate; z-index: 0锁住输入框层叠;覆盖层z-index: 50。打开盘面时隐藏 composer(visibility: hidden、关掉backdrop-filter),并收起预览条。sheet 高度改为 72%,上方聊天列表可从遮罩后看见。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:窄屏覆盖层必须高于 composer;打开时 composer 不得再带
backdrop-filter。源码合同锁定isolation: isolate、overlayz-index: 50、visibility: hidden。展开入口不得放在输入框上方,必须挂在页头右上角。 - 相关记录:BUG-328、BUG-330、BUG-335
- 复发自:BUG-330(覆盖层 z-index 未锁住 iOS backdrop-filter 合成层)
- 修复版本:8b8e5214
BUG-335 | 移动端当前盘面入口仍在输入框上方
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:生时纠正窄屏盘面入口、
.chat-header-actions、.rectification-board-peek - 用户现象:点开盘面的按钮在输入框上方,占掉打字区;希望放到右上角。
- 触发条件:窄屏生时纠正会话。
- 根因:
RectificationBoardPeek渲染在.composer-wrap里,输入框上方整条通栏。 - 修复:页头
chat-header-actions增加挂载点;窄屏未打开盘面时把预览按钮 portal 到余额按钮左侧。输入框上方不再放展开入口。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:源码合同锁定
createPortal、data-rectification-header-slot,并禁止 composer 内出现RectificationBoardPeek。 - 相关记录:BUG-330、BUG-334
- 复发自:BUG-330(窄屏入口一开始就放在输入区)
- 修复版本:8b8e5214
BUG-336 | 初始化对话列表与资料卡不在同一栏
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:首页 onboarding
.welcome、出生资料卡、输入框 - 用户现象:新用户初始化时,对话气泡和出生日期卡片左右对不齐;称呼阶段输入框也比上面的对话更窄。
- 触发条件:未完成资料的新会话,尤其是填写出生日期与时间的步骤。
- 根因:
.welcome被后续规则拉到 1040px 且无水平居中;资料卡max-width: 680/760px靠左。会话正文和输入框已经收成 760px 栏,初始化没有走同一套栏宽。 - 修复:未完成资料时给会话加上
is-onboarding。欢迎区和资料卡使用--session-column-width,与正式会话、输入框同一栏。 - 验证:
frontend/tests/session-conversation-layout.test.ts。 - 防复发:源码合同锁定
is-onboarding、欢迎区栏宽和资料卡width: 100%; max-width: none。 - 相关记录:无
- 复发自:无
- 修复版本:8b8e5214
BUG-337 | 事件吻合已达提出门槛后,生时纠正仍继续 A/B/C/D 追问且不出时间卡
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:生时纠正提出门、精度阶段、
method_followup_plan、rectification-offer-candidates - 用户现象:已确认事件与主限/行运高度吻合(事件吻合率≥80%),代表性时间已挤进约 14 分钟不可分区间,但对话仍继续 A/B/C/D 主题问卷,不出时间选择卡,也不自动采用。继续补事件会把已收敛的候选窗问偏。
- 触发条件:申报窗口跨多个本命上升,但得分簇已落在同一上升的十余分钟平台;可评分事件和领域已过提出门槛。用户没有说“暂时想不到了 / 没有更多 / 先这样”。
- 根因:
precision_stage用整段申报窗口扫描,跨上升时一直停在lagna_frame。该方法追问插在方法覆盖之前,且isOfferBlockingFollowup把精度阶段的dasha_events当成挡牌问。BUG-323 又把唯一领先和宽度≤5写进propose_allowed,14 分钟平台永远不能提出代表性时间。并列分钟本应只挡唯一分钟确认。 - 修复:精度阶段改为扫描候选簇(按不可分宽度扩到代表分钟附近),不再用整段申报窗决定是否还要拆上升。
propose_allowed看事件≥4、领域≥3、诊断稳定,或事件吻合率≥80%;唯一领先和宽度≤5只进确认门。方法覆盖(感情→事业→家人→外貌→疤痕→职业→占问)先于lagna_frame。精度阶段追问不挡出牌;职业仍挡。不自动accepted/candidate_ready,仍要用户点时间卡。不改哈希冻结的 Skill 10.0.10 包。 - 验证:
tests/test_rectification_refinement_packet.py(整段窗三个上升、候选簇一个上升时precision_stage不是lagna_frame);tests/test_rectification_confirmation_and.py(14 分钟并列簇propose_allowed为真、confirmation_allowed为假);frontend/tests/rectification-eight-method.test.ts(lagna_frame先问未覆盖事业;经典八法覆盖后lagna_frame不挡出牌)。 - 防复发:不得把确认门的唯一领先或宽度≤5重新写进
propose_allowed。precision_stage不得再用整段申报窗决定lagna_frame。精度阶段追问不得挡住rectification-offer-candidates。不得把校时 skill 从 10.0.10 改哈希包。 - 相关记录:BUG-297、BUG-313、BUG-323
- 复发自:BUG-323(把确认宽度写进提出门);BUG-297(并列分钟应给出代表性时间,而不是继续当收集失败)
- 修复版本:a88467ff
BUG-338 | 发布新版本后,旧网页停在「正在载入账户」转圈进不去
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:首页 bootstrap、
///loginHTML 缓存、NextdeploymentId、发布后已打开的旧标签页 - 用户现象:旧聊天页或已打开的网页,在 push 新版本之后一直停在「正在载入账户 / 同步个人资料与对话记录」,进不到对话。Safari 可能同时提示降低高级隐私保护。
- 触发条件:Web 镜像切换后,用户仍使用上一发布的 HTML/JS(后台冻住的标签、浏览器缓存的首页,或 401 跳转登录页时新 chunk 404)。
- 根因:首页 SSR 在
hydrated完成前就画引导动画。新镜像的/_next/static文件名已变,旧客户端加载失败后 hydration 和 8 秒超时都不跑。next.config.ts未设置deploymentId,HTML 也可被浏览器缓存。账户 401 会replace("/login")并清掉超时、不写accountError,登录页脚本同样 404 时界面条件仍是「没账户也没错误」,继续转圈。 - 修复:构建时把 Git SHA 写入 Next
deploymentId,让跨发布客户端导航整页重载。/与/login响应Cache-Control: private, no-store。根 layout 对 chunk 加载失败和冻在引导层的 pageshow 做一次硬刷新。401 跳转不再清掉 bootstrap 超时,登录没走成时落到可重试错误页。成功载入后清掉刷新标记,避免死循环。 - 验证:
frontend/tests/stale-client-recovery.test.ts;frontend/tests/membership-page.test.ts;frontend/tests/staging-backend-workflows.test.ts。 - 防复发:Web 镜像必须在
next build时注入NEXT_DEPLOYMENT_ID且next.config.ts写入deploymentId。首页 HTML 不得长期缓存。引导动画不得在「无账户且无错误」时成为发布失败的终态。 - 相关记录:BUG-160、BUG-204
- 复发自:BUG-204(报告页 chunk 偏斜已修,首页引导层仍会把同一失败画成永久 loading)
- 修复版本:50e1a4ac
BUG-339 | staging quality gate 因盘面入口 effect 同步 setState 失败
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:Gitea
backend-quality-gatevalidate、rectification-agentic-chat窄屏盘面入口 portal - 用户现象:向
staging推送后 quality gate 失败,镜像未发布,站点仍停在上一版。日志是react-hooks/set-state-in-effect。 - 触发条件:
npm run lint --prefix frontend。自8b8e5214起,gate run 2019/2020/2021 均因此失败。 - 根因:窄屏盘面入口用
useLayoutEffect里querySelector后立刻setHeaderSlot。eslint-config-next禁止在 effect 同步 setState。BUG-335 把入口 portal 到页头时引入该写法。 - 修复:页头挂载点改用 callback ref,把 DOM 节点作为
headerSlot传给校时会话。仍createPortal到data-rectification-header-slot,不再在 effect 里查 DOM 或 setState。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts;npm run lint --prefix frontend -- src/components/rectification-agentic-chat.tsx。 - 防复发:盘面入口不得在 effect 里
querySelector后同步 setState。源码合同锁定 callback ref 与headerSlotprop,并禁止 chat 内setHeaderSlot。 - 相关记录:BUG-335、BUG-334
- 复发自:BUG-335(把入口 portal 到页头时用了 effect 同步 setState)
- 修复版本:f4619df2
BUG-340 | 生时纠正未关闭 thinking,半截回答仍可能被当成完成
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:
POST /api/rectification/agent、runV9AgentTurn、生时纠正与普通咨询等待态 - 用户现象:Agent 写到一半停止,界面却像已经答完;等待期间只有「正在处理…」,看不出在做什么。
- 触发条件:当前会话模型默认开启 thinking / reasoning;可见正文与隐藏推理共用输出预算,或墙钟超时后仍发出
finish。 - 根因:BUG-305 只修了咨询路径。纠正
agent.stream没有maxOutputTokens、没有关闭 thinking,并把任意finish当成成功。进度事件tool.activity started被客户端丢掉,所以长计算期间用户只能干等。 - 修复:咨询与纠正共用
agentGenerationSettings(8192 可见 token,thinking disabled)。纠正在finish_reason=length时走answer_truncated、不扣点、不把半截写入成功 Turn;客户端保留已流出正文并提示未完成。等待态改为公开工具进度(正在比较候选时间等)和 8 秒后的已用时,不展示模型思维链。 - 验证:
frontend/tests/rectification-v9-stream.test.ts的 length 夹断不得run.completed;frontend/tests/rectification-v9-agent.test.ts锁定纠正流的输出预算与 thinking disabled;frontend/tests/agent-activity-progress.test.ts、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/consultation-workflow-contract.test.ts。 - 防复发:纠正与咨询必须走同一套 generation settings 与 8192 可见预算。不得为了等待体验打开 provider thinking。若打开 thinking,必须走独立
thinking.delta,不得把推理写进answer.delta。tool.activity started必须驱动 Orb 文案。finish_reason=length不得映射为run.completed。 - 相关记录:BUG-305、BUG-282、BUG-329、BUG-345
- 复发自:BUG-305(咨询已修,纠正仍用默认 thinking 与任意 finish)
- 修复版本:
6a44c778
BUG-329 | 生时纠正 Agent 回答在结算后一次性出现,推理中无法停止
- 状态:resolved
- 首次发现:2026-08-20
- 最近更新:2026-08-20
- 影响面:
POST /api/rectification/agent、runV9AgentTurn、生时纠正输入框 - 用户现象:发送经历后接口长时间无增量文字,工具活动与回答在计费完成后才整段出现;推理过程中发送按钮不可用,也无法停止。
- 触发条件:任意生时纠正
message/opening流,尤其是rectification-set-focus同轮重试的长等待。 - 根因:成功 attempt 的 tool.activity 与
answer.delta被攒到billing.complete和持久化之后才发给浏览器。前端 fetch 没有 AbortSignal,输入区也没有停止按钮。 - 修复:工具活动和回答增量在生成过程中即时发布;失败重试先发
attempt.reset清掉弃用 attempt 的可见文字。前端在推理中显示停止按钮,中止请求;已流出的文字保留。 - 验证:
frontend/tests/rectification-v9-stream.test.ts、frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:
answer.delta必须在billing.settled之前出现在公共流里;生时纠正 composer 在busy时必须提供停止按钮并绑定 fetch abort。 - 相关记录:BUG-039、BUG-067
- 复发自:BUG-039(当时为了 attempt 隔离把增量攒到结算后,V9 流式合同被一起推迟)
- 修复版本:994f0583
BUG-341 | 无准确出生分钟时普通咨询被整题拒绝并叉到生时校正
- 状态:resolved(已提交,待 staging 验收)
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:无具体出生分钟用户的普通 session、
POST /api/consult一般咨询与声明窗口咨询、首页「每日运势」与主题卡、General Agent / Window Agent、个人报告入口、无分钟输出 guard - 用户现象:初始化只填了日期/地点或大致时段、没有准确到分钟的出生时间后,在普通咨询里问「今天的运势」会被回复「当前一般咨询模式不能生成个人星盘结论」,并要求在一般知识和生时校正里二选一。用户再点「出生时间校正」仍留在普通 session,模型把服务端当前时间误认为用户提供了出生时间,然后改口给公共日盘。时段用户也无法进入事业/婚恋等主题咨询。未校正但已填报到分钟的用户无法生成个人报告。
- 触发条件:
birth_time_source为period_only/unknown等无具体分钟状态;或已有reported_birth_time但状态仍是reported/candidate;从首页「每日运势」或普通 session 发出今日运势或主题问题;或在同一普通 session 里发送「出生时间校正 / 生时校正」类标签。 - 根因:产品把生时校正当成功能门,而不是可选增强。无分钟用户被压进
general_no_birth_time,General Agent 对任何本命预测整题拒绝并给出「百科 / 生时校正」二选一;首页无分钟日运还把daily_starlanguage入口丢掉。报告入口只认accepted/confirmed,把未校正填报分钟也拦掉。这是 BUG-202 的回归叠加产品能力分层缺失。 - 修复:校正改为可选。未校正填报分钟走
unverified_birth_time,可用咨询、今日星语和完整报告(标明未校正)。只有日期+时段或未知时刻走declared_birth_window:约 4 个探针比较稳定层/变动层,禁止把中点、00:00、中午或探针写成出生分钟;主题咨询用窗口工具,每日运势仍走公共 Panchanga。无分钟百科咨询保留给真正没有日期/地点的情况。报告使用reported_birth_time且不把 reported 映射成 accepted。普通 session 的生时校正标签打开校正会话。 - 验证:
frontend/tests/consultation-entrypoint.test.ts、frontend/tests/consultation-birth-time-mode.test.ts、frontend/tests/consultation-route-service.test.ts、frontend/tests/declared-birth-window.test.ts、frontend/tests/starter-questions.test.ts、frontend/tests/personal-report-api.test.ts、frontend/tests/timing-output-guard.test.ts、tests/test_declared_window_chart.py。 - 防复发:
period_only/unknown不得再进无盘死胡同。窗口探针不得用时段中点、00:00或任意单一分钟冒充出生时间。窗口请求不得携带hour/minute。未校正填报分钟可出报告,窗口用户仍不得出完整本命报告。无分钟「每日运势」不得把daily_starlanguage丢掉。普通 session 不得把生时校正标签当一般咨询原文。General Agent 不得再写「exactly two safe next steps」。 - 相关记录:BUG-009、BUG-073、BUG-202、BUG-273
- 复发自:BUG-202(服务端公共日盘已修好,首页无分钟入口后来又把 entrypoint 丢掉,普通 session 仍走整题拒绝;校正还被当成功能门)
- 修复版本:9958e00a
BUG-342 | BUG-341 未更新咨询源码合同,staging quality gate 连续失败
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:Gitea
backend-quality-gatevalidate、frontend/tests/application-billing-contract.test.ts、frontend/tests/consultation-stream-recovery.test.ts、frontend/tests/chat-navigation-a11y-contract.test.ts、frontend/tests/composer-isolation-contract.test.ts;点选卡提交因此无法发布 - 用户现象:向
staging推送后质量门失败,镜像未发布,站点仍停在e7f4030e。run2026(BUG-341)与 run2027(点选卡)均是validate失败、publish跳过。前端测试摘要# tests 1871 / pass 1865 / fail 6。 - 触发条件:
npm test --prefix frontend。BUG-341 增加声明窗口咨询路径并改了首页草稿变量后,精确计数合同仍按两条 agentic 路径与setDraftEntrypoint(entrypoint)断言。 - 根因:咨询路由现有三条 agentic 首流(公共/百科、声明窗口、本命)及对应重试,
continueAfterDisconnect、abortSignal、settleRunonError/onCancel 都变成 3/5。首页send()把草稿 entrypoint 收成consultEntrypoint,且校正交接会在积分检查前清一次草稿。合同仍用send()里第一次setDraft("")当发送清草稿标记,切片失效。这是 BUG-308 同类:产品改了字面,源码合同没一起改。 - 修复:把计量次数改成三条路径的实际次数;失败回填断言
consultEntrypoint;积分不足合同改为对照真正发送清草稿(conversationAnchor.anchorToLatest()之后),不再误伤校正交接。 - 验证:上述四份合同测试本地 35/35;此前失败的 6 项均通过。
- 防复发:再增加咨询 agent 路径时必须同步
usages.push、retryForAnswer、continueAfterDisconnect、abortSignal: agentAbortSignal与settleRunonError/onCancel 的精确计数。切send()清草稿不得用文件里第一次setDraft("")。失败回填的 entrypoint 变量名必须随send()一起改。 - 相关记录:BUG-308、BUG-341
- 复发自:BUG-308(源码合同锚点过期把无关提交打红);BUG-341(第三条咨询路径与草稿变量未改合同)
- 修复版本:6a0394aa
BUG-343 | 生时纠正点选卡 effect 同步 setState,staging lint 失败
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:Gitea
backend-quality-gatevalidate、rectification-agentic-chat案例快照加载、rectification-choice-card换题重置 - 用户现象:BUG-342 合同修过后
npm test1871/1871 通过,但npm run lint因react-hooks/set-state-in-effect失败。run2028validate9m39s 失败,publish1 秒跳过,站点仍停在e7f4030e。 - 触发条件:
npm run lint --prefix frontend。挂载校时会话或换一道 A/B/C/D 题。 - 根因:挂载时
useEffect直接调用含setState的loadCaseSnapshot();点选卡用 effect 在question_id变化时setSelectedKey("")。eslint-config-next禁止 effect 同步 setState。同类于 BUG-339。 - 修复:案例快照改为
fetch().then回调里应用 payload(事件处理仍走loadCaseSnapshot)。点选卡用key={question_id}换题重挂,不再在 effect 里清选中态。 - 验证:
npm run lint --prefix frontend0 error;frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:校时 UI 不得在 effect 里直接
setState或调用会setState的 helper。换题重置必须用key重挂。合同禁止void loadCaseSnapshot()和setSelectedKey("")。 - 相关记录:BUG-339、BUG-342
- 复发自:BUG-339(盘面入口 effect 同步 setState;点选卡与快照加载又写了同一模式)
- 修复版本:a4ab229e
BUG-344 | 声明窗口模式未收窄本命类型,staging publish 镜像构建失败
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:Gitea
backend-quality-gatepublish、consultation-route-service.serverChartFromProfile、isNatalMinuteConsultationMode - 用户现象:run
2029validate12 分钟通过,publish在npm run build失败,镜像未发。站点仍停在e7f4030e。 - 触发条件:staging push 的 publish 镜像构建跑
next build。validate 对 staging push 跳过 production build。 - 根因:BUG-341 给
ConsultationBirthTimeMode增加了declared_birth_window,但isNatalMinuteConsultationMode仍返回boolean,TypeScript 无法收窄成verified_chart | unverified_birth_time,serverChartFromProfile在next build报 TS2345。 - 修复:把该函数改成
mode is NatalMinuteConsultationMode类型谓词。 - 验证:
frontend/tests/consultation-birth-time-mode.test.ts;本地tsc --noEmit通过。 - 防复发:本命分钟模式判定必须是类型谓词。新增咨询模式时不得把窗口/百科模式传入
serverChartFromProfile。staging push 的类型错误只会在 publish 的next build暴露。 - 相关记录:BUG-341、BUG-343
- 复发自:BUG-341(第三条咨询路径扩大了 mode 联合类型,未改类型谓词)
- 修复版本:eeee0e49
BUG-345 | 生时纠正把工具重试自述当成回答,且 kind 在 schema 里是自由字符串
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:
POST /api/rectification/agent、runV9AgentTurn、证据工具proposedKind/domainschema、生时纠正对话 - 用户现象:用户补了一条升学经历后,结算气泡里是英文工具重试自述(大意:proposedKind 被拒、education 不是合法 kind、改用 education_start),没有中文跟进问句。棋盘已推进,说明计分跑过,失败的是用户可见正文。
- 触发条件:用户给出升学类经历,模型把
missing_evidence_categories里的领域名education当成proposedKind调用证据工具。 - 根因:与 BUG-278 / BUG-277 同类。
proposedKind与domain在 Zod schema 里是自由字符串,白名单只在execute的isEvidenceKind()里;模型在 JSON schema 里看不到education_start。被拒后把重试过程写进text-delta。纠正 runner 立刻把每个text-delta发成answer.delta,没有咨询路径那种契约门。BUG-340 关闭 provider thinking 后,过程自述更容易落到正文通道。 - 修复:证据 kind/domain 改为枚举,模型在调用前能看到
education_start。工具失败未恢复成功前的正文、以及纯英文过程自述,不得进入answer.delta。中文思维链走独立的thinking.delta(来自reasoning-delta或契约未绿的中文片段);英文过程自述丢弃。纠正打开 provider thinking。界面灰色「思考过程」,有正文后默认折叠。 - 验证:
frontend/tests/rectification-v10-tool-contract.test.ts拒绝proposedKind: "education";frontend/tests/rectification-v9-stream.test.ts英文重试自述不得出现在公共流,用户只拿到中文回答;思维链映射与折叠 UI 的源码合同。 - 防复发:模型必须遵守的词汇写在 schema 枚举里,不要只写在 execute。
answer.delta不得承载工具重试自述。思维链必须是独立事件类型。thinking.delta不得写入 run_phase 表。 - 相关记录:BUG-277、BUG-278、BUG-340、BUG-346
- 复发自:BUG-277(纠正未做契约门)、BUG-278(kind 未枚举)、BUG-340(关 thinking 后自述改走正文)
- 修复版本:4e247c11
BUG-346 | 普通咨询未打开思维链通道,灰色折叠 UI 只在生时纠正生效
- 状态:resolved
- 首次发现:2026-08-21
- 最近更新:2026-08-21
- 影响面:
POST /api/consult、streamAgentResponse、咨询页ChatMessageRow - 用户现象:生时纠正已有灰色「思考过程」、正文出来后折叠;普通咨询仍无思维链。
- 触发条件:网页个人咨询 / 无分钟百科 / 声明窗口咨询。
- 根因:BUG-305 / BUG-340 为保住可见 token 预算,把咨询
consultationGenerationSettings固定为 thinking disabled,并丢弃reasoning-*。BUG-345 只给纠正打开独立thinking.delta与折叠 UI。 - 修复:咨询同样启用 provider thinking,8192 可见预算不变。
reasoning-delta经中文清洗后发thinking.delta;契约未绿的text-delta仍按 BUG-277 丢弃,不升格为思维链。咨询页把thinkingText接到共用ChatMessageRow,有正文后默认折叠。后续 BUG-347 把思维链写入会话落盘,并在失败时保留已展示的思考过程。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts中文 reasoning 进 thinking 通道、英文过程自述不进正文;consultation-workflow-contract.test.ts锁定thinking: "enabled";chat-stream-layout.test.ts锁定咨询页与折叠 UI。 - 防复发:咨询与纠正的思维链必须是
thinking.delta,不得混进answer.delta。BUG-277 的契约前正文仍须丢弃。打开 thinking 不得降低 8192 可见预算,finish_reason=length仍不得当完成。 - 相关记录:BUG-277、BUG-305、BUG-340、BUG-345
- 复发自:BUG-305(咨询关 thinking 保预算)、BUG-345(只修了纠正)
- 修复版本:4e247c11
BUG-347 | 咨询失败或刷新后思维链被清掉
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:咨询流式 UI、
complete_consultation_response落盘、chatSessionWriteSchema、会话读取 - 用户现象:开始出回答时思维链消失;若随后报「Agent 回答未完成」或刷新,已展示的思考过程一起没了。
- 触发条件:普通咨询流式生成中出现
run.failed、空正文,或成功后刷新页面。 - 根因:思维链只活在
streamingReply.thinkingText。失败路径回滚到提问前的会话并清掉 streaming;成功路径虽然把 thinking 写进内存消息,但结算 RPC 与会话 PATCH 都不保存该字段,读取时也会丢掉额外消息字段。 - 修复:失败时保留用户问题、已有思考过程和失败说明,不再把思维链卸掉。成功结算把清洗后的
thinkingText写入p_response_message;客户端会话读写契约同步接收该字段。界面改成时间线思考块,完成步骤一行一项。 - 验证:
frontend/tests/chat-session-write.test.ts接受 thinkingText;consultation-stream-recovery.test.ts锁定结算消息含思维链;chat-stream-layout.test.ts锁定时间线 UI;失败路径保留thinkingText的源码合同。 - 防复发:咨询结算消息与会话 PATCH 必须能保存
thinkingText。失败不得用回滚抹掉已展示的思考过程。读取会话时不得只保留role/text。 - 相关记录:BUG-277、BUG-346
- 复发自:BUG-346(打开思维链通道但明确不落盘)
- 修复版本:59559d4b
BUG-348 | 生时纠正一进场就出 A/B/C/D,比纯提问更慢
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:staging 生时纠正开场、
method_followup_plan、rectification-choice-card、Skilljyotish-birth-time-rectification@10.0.11、decision receiptdiscriminating_event_probes - 用户现象:账本还没有任何已确认证据时,开场就出现 A/B/C/D 点选卡(学业 vs 工作)。用户点 A 之后仍没有具体年份事件,下一问又变成“2016 年前后,更像哪一件?”——A/B 是认真关系 vs 职责加重,或“时间大致对得上 vs 略偏”。比之前直接问一件带年份的经历更慢,也无法筛时间。
- 触发条件:新建或恢复空账本生时纠正会话;不确定范围约半小时,例如 04:45–05:15。有账本后候选仍分不开时继续出冲突排序卡。
- 根因:conversation-strategy 把“降低回忆负担”写成开场就从候选簇差异生成点选卡。
buildMethodFollowupPlan对空账本的dasha_events以及后续方法覆盖一律挂choice_frame;系统提示强制把 A/B 写成两套盘前事。区分阶段用lifePeriodLabel()把别的事件年份贴到当前主题,并让用户给冲突排序,而不是收集可评分生平。评分链路已有_score_event,但没有逆运算把两套代表分钟的年差/激活差写成可问题干。 - 修复:收集阶段用自然语言问一件带大概年份的经历,
choice_frame为 null,界面不出点选卡。候选已经分不开时,服务器生成discriminating_event_probes(锁定年份和事件家族,不写死题干)。Agent 把题干和 A/B/C/D 写成自然语言;A/B 是同一件事的吻合程度。新 Case 绑定 Skill 10.0.11;已有 10.0.10 Case 保持原绑定,但服务器不再给空账本发点选框,也不再问两套盘哪个更像。 - 验证:
tests/test_rectification_event_probes.py锁定高考性质追问、年龄带退路、D4 激活差问搬家、大运起年只差几天不得写成新年、缺 Narayana 不得声称 dasha 年;frontend/tests/rectification-choice-card.test.ts锁定空账本无卡、探针题干不含“更像哪一件”、跨主题不复用年份;frontend/tests/rectification-eight-method.test.ts锁定方法覆盖用自然语言、分盘差异仍出 A/B/C/D。 - 防复发:空账本或
collect_method_evidence不得挂choice_frame。区分卡不得写“更像哪一件 / 两套盘 / 可能性”。年份必须来自探针或同主题账本/年龄带,禁止跨主题lifePeriodLabel。不得把已哈希的 10.0.10 改包;未提交的 10.0.11 可折进同包。已有 10.0.10 Case 继续可解析。 - 相关记录:BUG-020、BUG-337
- 复发自:BUG-020(语言问答被包装成高阻力卡片;后来又把开场改成强制 A/B/C/D)
- 修复版本:9873ba42
BUG-349 | 唯一分钟确认门被 holdout 吊着,会话没有收口
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:确认门投影、
session_outcome、采用卡文案、Skilljyotish-birth-time-rectification@10.0.11、decision receiptunique_minute_path - 用户现象:采用代表性时间之后,界面和 Agent 仍说“还不能确认唯一分钟”,像下一步还要等 VedAstro / 公开 holdout / 宽度 ≤ 5 分钟。holdout 当前是 1/20
not_ready,确认门永远打不开,访谈没有终点。 - 触发条件:生时纠正已经可以出示或采用代表性时间;密封 holdout 为
not_ready。 - 根因:确认门三层 blocker 是诚实的 fail-closed,但产品把
confirmation_allowed=false写成待完成的下一步,而不是本会话的终点。candidate_accepted还写着“可进入确认门”。 - 修复:不放开
confirmation_allowed。confirmation_gate.unique_minute_path=closed_at_representative时,本会话以代表性时间收口;不得调用 confirm,不得把唯一分钟确认当下一步。文案从“还不能确认”改为“本会话以代表性时间收口,不确认唯一分钟”。折进未提交的 Skill 10.0.11,不改已哈希的 10.0.10。 - 验证:
frontend/tests/rectification-confirmation-gate.test.ts锁定 holdoutnot_ready时unique_minute_path=closed_at_representative且confirmation_allowed=false;tests/test_rectification_confirmation_and.py锁定 14 分钟并列簇提出门开、确认门关、路径为收口。 - 防复发:不得把
confirmation_allowed改成 true 来假装收口。holdout 未达标时不得把确认写成下一步。不得为把不可分区间问到 5 分钟以内而继续 A/B/C/D。 - 相关记录:BUG-303、BUG-318、BUG-348
- 复发自:BUG-318(确认门与密封 holdout 对齐后,产品仍把确认当未完成步骤)
- 修复版本:9873ba42
BUG-350 | 采用代表性时间后没有按该分钟核对前事,也无法改选
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:
method_followup_plan采用后路由、next_user_action、生时纠正候选卡、Skilljyotish-birth-time-rectification@10.0.11 - 用户现象:用户采用一个代表性时间后,会话直接请看盘;盘外核对也不计分、不改候选。没有用该分钟去核几件还没提过的前事,对不上也不能改选其他冲突时间。
- 触发条件:生时纠正已经采用代表性时间;decision receipt 仍有
discriminating_event_probes(大运年界/同年激活差或年龄带退路)。 - 根因:
buildMethodFollowupPlan({ accepted: true })把下一问清空,只挂不计分的oos_blind。buildNextUserAction在 adopted 后一律start_consultation。候选卡又要求!selectedTime,采用后改选入口消失。 - 修复:采用后最多核两件剩余探针前事(跳过已确认/已拒绝领域和
known_event_quality),next_followup.method_id=reverse_verify且计分。A 写入账本并重算;C 关闭该问;对不上或核对结束后可改选。不打开唯一分钟确认门。折进未提交的 Skill 10.0.11。 - 验证:
frontend/tests/rectification-eight-method.test.ts锁定采用后verify_adopted_time与无剩余探针时start_consultation;frontend/tests/rectification-choice-card.test.ts锁定核对卡计分且不是盘外核对;frontend/tests/rectification-agentic-entry.test.ts锁定有点选卡时先核前事、否则在已 offer 过的会话里显示改选。 - 防复发:采用后不得把不计分盘外核对当默认下一步。
showSelectionCards不得因selectedTime永久藏改选;有前事点选卡时不得同时出示时间卡。不得把confirmation_allowed改成 true。 - 相关记录:BUG-120、BUG-348、BUG-349
- 复发自:BUG-120(采用后无法改选;后来又把采用后的核对做成不计分盘外问句)
- 修复版本:9873ba42
BUG-351 | 候选时间冲突时反推前事被方法轮询和出牌 defer 挡住
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:
method_followup_plan、isOfferBlockingFollowup、Skilljyotish-birth-time-rectification@10.0.11 - 用户现象:账本已有带年份经历、两套代表分钟已经分不开时,Agent 仍继续问下一条感情/事业,或直接请采用代表性时间。不会按冲突分钟反问「某年前后有没有搬家/入职」,答案也无法先筛窗。
- 触发条件:生时纠正已有至少一件可评分事件;decision receipt 含
dasha_boundary或dasha_activation探针;方法覆盖尚未齐,或propose_allowed已开。 - 根因:第一件带日期事件之后下一问固定走感情 → 事业 → 家人 → 职业。精度阶段/分盘差异问句不挡出牌,
session_outcome=adopt_representative时把它们放进deferred_followup。探针年份锁在 choice_frame 里,但问句轮不到。 - 修复:已有带日期事件且仍有大运冲突探针时,
source=event_probe先问该年前事并挡住出牌。A 写入并重算以筛窗。年龄带探针不插队。空账本仍只自然语言收集。不打开唯一分钟确认门。折进未提交的 Skill 10.0.11。 - 验证:
frontend/tests/rectification-eight-method.test.ts锁定教育事件 + 事业激活探针先问 2018 年入职且session_outcome保持收集;年龄带探针不插在感情收集前。 - 防复发:大运冲突探针在采用前必须挡住出牌。不得在空账本挂
choice_frame。不得把confirmation_allowed改成 true。不得在采用门事件/领域未齐时插队(见 BUG-386)。 - 相关记录:BUG-348、BUG-350、BUG-386
- 复发自:BUG-348(收集后再区分;区分问句被方法轮询和出牌 defer 排到采用之后)
- 修复版本:9873ba42
BUG-352 | 个人报告失败被压成 report_schema_invalid,列表把 Date 显示成时间未知
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:个人报告中心
/reports、GET /api/reports、generatePersonalReport、自托管timestamptz投影、报告 writer 绑定 - 用户现象:生成完整报告后记录为「未完成」,右侧只显示
report_schema_invalid;卡片时间显示「时间未知」。出生时钟在创建时是可用的,失败发生在后台生成之后。 - 触发条件:自托管 PostgreSQL 上创建个人完整报告;writer 输出与 section plan 不完全一致,或生成中被中止;列表读取
created_at。 - 根因:
generatePersonalReport把 writer JSON、plan 绑定、组装、终态 parse 和 abort 全部吞成同一个report_schema_invalid,且不记录内层 token。evidenceRefs 还要求顺序全等。自托管queryValue()只把date收成字符串,timestamptz仍是Date,列表投影只接受 string,UI 便显示「时间未知」。 - 修复:schema 失败返回 allowlist
innerReason并打无 PII 日志;abort 向上抛出,不再伪装成 schema 失败。plan 绑定失败走现有那一次 repair;evidenceRefs 改为集合相等。queryValue把timestamptz/timestamp收成 ISO,列表投影同时接受Date。 - 验证:
frontend/tests/personal-report-generation-v2.test.ts、frontend/tests/personal-report-generation.test.ts、frontend/tests/personal-report-api.test.ts、frontend/tests/local-postgres-query-value.test.ts、frontend/tests/personal-report-worker.test.ts。 - 防复发:用户可见码可以仍是
report_schema_invalid,但生成结果和日志必须带 allowlist 内层 token。lease abort 不得再断言成 schema 失败。列表时间必须能从Date或 ISO 字符串格式化。不得把模型原文、出生资料或路径写进失败日志。 - 相关记录:BUG-154、BUG-188、BUG-217
- 复发自:无
- 修复版本:97620634
BUG-353 | 不确定时间只扫五档时段,补充描述里的钟点纠正用不上
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:出生时间表单、
profiles.declared_window_start/end、V9 Case 候选窗、普通咨询 declared window、账户 PATCH - 用户现象:用户已把不确定时间改成 14:30–15:00,或写在补充描述里,生时纠正和 Agent 仍按下午 12:00–17:59 扫描。
- 触发条件:
birth_time_source=period_only且用户记得的是一段钟点而不是五档粗时段;旧资料把钟点写在birth_time_clue。 - 根因:
period_only只把birth_time_period映射成五档固定窗。birth_time_clue不进 Case fingerprint、不进candidate_range、也不被解析。Case 窗在创建时冻结,聊天工具不能改。正则解析备注会把“6—8 点”这类歧义话当成扫描分钟。 - 修复:不确定路径改为开始/结束钟点,不再收集备注。保存
declared_window_start/end;自定义钟点优先于五档时段;开始结束相同则拒绝;允许跨午夜。旧afternoon等行回填对应钟点,用户可以收紧到 14:30–15:00。收紧后 fingerprint 变化,首页开新 Case。不解析 clue,保存时把 clue 写成 null。 - 验证:
frontend/tests/birth-time-intake.test.ts锁定开始结束表单、14:30–15:00 持久化且不留下 afternoon 桶;frontend/tests/declared-birth-window.test.ts锁定自定义钟点覆盖 afternoon;frontend/tests/rectification-v9-case-service.test.ts锁定 Case 开窗 14:30–15:00 且 fingerprint 变化;frontend/tests/consultation-route-service.test.ts锁定咨询窗;frontend/tests/account-api.test.ts锁定 PATCH 接受开始结束、拒绝同一分钟。 - 防复发:不得把备注正则解析成扫描窗。不得只扫五档时段而丢掉用户选择的开始结束。不得把自定义窗的中点写成
reported_birth_time。改窗必须开新 Case,旧会话继续展示冻结窗。 - 相关记录:BUG-197、BUG-198
- 复发自:BUG-197(不确定入口恢复后仍用五档时段加备注,备注从未进入扫描窗)
- 修复版本:8ed6118b
BUG-354 | 咨询 thinking 抢占正文额度,思考过程被清洗成英文碎片且没有回答
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:
POST /api/consult、streamAgentResponse、咨询页思考/分析 UI、组答maxOutputTokens - 用户现象:咨询页灰色「思考过程」出现断裂英文碎片(如
Let me at the.、The for),正文为空;流在技法审计表标题处因finish_reason=length结束。 - 触发条件:网页个人咨询,尤其是多领域 Level 2 组答;供应商默认打开 thinking,且
max_tokens与可见正文共用。 - 根因:BUG-346 在 8192 可见预算不变的情况下重新打开 provider thinking。Flash 的
reasoning_content占满额度后content为空。公开thinking.delta又按 token 清洗英文,把连贯 CoT 剪成碎片。Skill 体积走的是输入上下文,不是这次截断的原因。 - 修复:咨询与纠正组答改回
thinking: disabled。组答正文预算改为独立的 16384。计算仍一次完成,不按领域再开模型。服务器用已执行领域、required_blocks和must_use_layers生成thinking.section步骤树。finish_reason=length且已有正文时关 thinking 续写一次;两次仍空或不增长才answer_truncated,不扣点。咨询页按领域渲染可折叠「思考」和「分析」,不再把模型 CoT 当主思考通道。 - 验证:
frontend/tests/consultation-workflow-contract.test.ts锁定 thinking disabled 与 16384 正文预算;frontend/tests/consultation-agentic-runtime.test.ts锁定reasoning-delta不进公开流、length续写可完成、不增长则截断;frontend/tests/consultation-thinking-plan.test.ts锁定中文步骤树不含工具 id;frontend/tests/chat-stream-layout.test.ts锁定思考/分析 UI。 - 防复发:咨询组答不得再打开 provider thinking 来充当思考 UI。打开 thinking 必须同时拆开或加大正文预算,且不得把
reasoning-delta经逐 token 清洗后当作用户可见思考过程。finish_reason=length且半截正文必须续写,不得直接run.completed。步骤树只使用中文产品文案,禁止把run-jyotish-*、JSON 键或 Skill 正文送进思考 UI。 - 相关记录:BUG-277、BUG-305、BUG-340、BUG-345、BUG-346、BUG-347
- 复发自:BUG-305(thinking 与正文抢
max_tokens)、BUG-346(8192 预算不变就重新打开 thinking) - 修复版本:
0ca7da99
BUG-355 | staging web 镜像 next build 被 declared window 类型检查挡住
- 状态:resolved
- 首次发现:2026-08-22
- 最近更新:2026-08-22
- 影响面:
deploy/railway-web.Dockerfile的RUN npm run build、GET/PATCH /api/account、declared window 类型收口 - 用户现象:xiaoxin 不可用时本机按 publish 合同构建 web 镜像,
next build在 TypeScript 阶段失败,staging 无法换上已推送的90db4098。 - 触发条件:当前
staging头包含 BUG-353 的列缺失回退,且走 Dockernext build。push 上的 validate 跳过npm run build,因此测试绿、镜像红。 - 根因:列缺失回退把
declared_window_start/end写成undefined或省略,对不上 supabase 行类型。isBirthClockText不是类型谓词,收窄后仍是string | null | undefined。Boolean(focus) && focus.intent不能收窄focus。 - 修复:缺失列回退写成
null。isBirthClockText改为value is string。焦点判断改为focus && (...)后再使用focus.intent。 - 验证:
npx tsc --noEmit通过。tsx --test tests/declared-birth-window.test.ts tests/profile-persistence.test.ts14/14。 - 防复发:账户列缺失回退必须补齐 declared window 字段为
null,不得用undefined。staging push 的类型回归仍只在 publish Docker 暴露,本机/xiaoxin 镜像构建失败不得改 SHA 蒙混上线。 - 相关记录:BUG-309、BUG-353、BUG-354
- 复发自:BUG-309(validate 跳过
next build,publish 才暴露类型错误) - 修复版本:
707327ef
BUG-356 | 咨询思考与正文交错,完成步骤仍显示「还有 N 项」
- 状态:resolved
- 首次发现:2026-08-23
- 最近更新:2026-08-23
- 影响面:咨询页
ConsultationThinkingReport、思考步骤树、回答 Markdown 列表、技法审计折叠 - 用户现象:思考与「分析」看起来像同一段;领域步骤树插在正文中间和文末;已完成步骤仍显示空心圆「还有 N 项」;行运/适合推进等并列项是挤在一起的段落,技法表与「本轮技法」重复。
- 触发条件:带
thinking.section的本命咨询回答,尤其模型未按领域 H2 切开、或步骤超过 4 项时。 - 根因:报告按每个 thinking section 交错渲染思考+切片正文。
visibleThinkingSteps把第 5 项起收成「还有 N 项」且固定空心圆。模型用「标签:解释」段落而不是列表。正文已贴审计表时仍再叠一层折叠表。 - 修复:一轮只保留一个可折叠「思考」和一个完整「分析」。结算后未写出的步骤标为完成并展开全部步骤。并列「标签:解释」提升为 Markdown 列表。正文已有完整审计表时不再重复折叠副本。组答要求并列项用 bullet list。
- 验证:
frontend/tests/consultation-thinking-plan.test.ts、frontend/tests/chat-definition-lists.test.ts、frontend/tests/chat-stream-layout.test.ts、frontend/tests/consultation-technique-audit.test.ts - 防复发:咨询思考不得按领域把正文切片后反复插入步骤树。完成态不得用空心圆「还有 N 项」代替未展示步骤。正文已含完整技法表时不得再渲染折叠副本。
- 相关记录:BUG-354
- 复发自:BUG-354(按领域交错思考/分析,步骤树截断)
- 修复版本:
995b752e
BUG-357 | 生时纠正思考与结论同一样式,过程自述进了正文
- 状态:resolved
- 首次发现:2026-08-23
- 最近更新:2026-08-23
- 影响面:生时纠正对话、
runV9AgentTurn、ChatMessageRow、灰色思考折叠 - 用户现象:生时纠正里思考和模型结论是同一样式;用户可见气泡里先出现长段内部权衡(日期精度、batch 写入、skill 规则),最后才是确认口误和下一个追问,分不清哪段是思考、哪段是给用户的话。
- 触发条件:生时纠正已读取 Case 且工具成功后,模型把中文过程自述写进
text-delta。 - 根因:BUG-354 为保住正文预算关闭了纠正 provider thinking。
reasoning-delta不再出现,中文自述改走text-delta。契约门只在 Case 未加载或工具失败时把正文改送到thinking.delta;工具成功后全部当作回答。界面上思考折叠和正文也没有咨询页那种「思考 / 回复」分层。 - 修复:保持
thinking: disabled与 16384 正文预算。对已加载 Case 的text-delta按段落拆成过程自述与口语结论:自述进thinking.delta,结论进answer.delta并作为落盘正文。界面与咨询对齐:折叠「思考」(13px 次要色,有正文后默认收起)和完整「回复」。 - 验证:
frontend/tests/rectification-spoken-answer.test.ts、frontend/tests/rectification-v9-stream.test.ts、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/chat-stream-layout.test.ts;tsc --noEmit通过。口语/思考拆分的emit类型必须与RunV9AgentTurnInput.emit同为Promise<void> | void,否则 Dockernext build会在 publish 阶段失败。 - 防复发:纠正组答不得在不拆开正文预算的情况下重开 provider thinking 来充当思考 UI。
answer.delta不得承载 skill/schema/batch 过程自述。思考与回复必须分层渲染,不得共用同一段正文样式。 - 相关记录:BUG-345、BUG-354、BUG-356、BUG-373
- 复发自:BUG-345(关 thinking 后自述改走正文)、BUG-354(纠正与咨询一并关闭 provider thinking)
- 修复版本:
f03a2706
BUG-358 | 生时纠正离开页面再回来,当轮回答被另一段开场覆盖
- 状态:resolved
- 首次发现:2026-08-23
- 最近更新:2026-08-23
- 影响面:生时纠正会话恢复、
shouldStartOpening、Turn 水合、聊天组件 remount key - 用户现象:当轮已经看到 Agent 追问,跳到别的页面再回来后,原来的回答不见了;会话里多出一段新的助手正文(例如从教育追问变成感情追问)。
- 触发条件:生时纠正 Case 已有用户+助手同一物理 Turn;离开校正表面后再进入。常见路径是侧栏切走再切回,客户端仍留着创建时的
shouldStartOpening=true。 - 根因:聊天组件用最后一条 Turn id 做 React key,新回合会整棵重挂,
openingStarted被清掉。创建 Case 时的shouldStartOpening在开场成功后仍保持 true;切走再回来若不再走 open RPC,就会对已有账本再发一轮opening。模型读到已有证据后写出下一条追问,盖过用户刚看过的当轮正文。已落盘的过程自述也会原样回到气泡。 - 修复:有持久化 Turn 时禁止自动 opening;开场一旦发出就把
shouldStartOpening收成 false。水合 key 只在空历史/ready之间切换,不跟最后一条 id。回合成功后刷新 Case turns。恢复时把已落盘过程自述拆进折叠「思考」,正文只留口语结论。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/rectification-spoken-answer.test.ts - 防复发:已有 Turn 的校正表面不得因 remount 再发 opening。
shouldStartOpening只能表示「这个 Case 还没有第一轮」;开场发出后必须立刻关掉。水合 key 不得使用会随新回合变化的 Turn id。 - 相关记录:BUG-176、BUG-345、BUG-357
- 复发自:BUG-176(同一物理 Turn 的助手正文在恢复时丢失;后来又用最后一条 id 做 key,把 opening 状态一起冲掉)
- 修复版本:
f03a2706
BUG-359 | 生时纠正把截断的过程自述落成完成回复
- 状态:resolved
- 首次发现:2026-08-23
- 最近更新:2026-08-23
- 影响面:生时纠正对话、普通咨询会话、
runV9AgentTurn、streamAgentResponse、Turn / 会话水合 - 用户现象:工具已写入带日期经历并完成评分后,折叠「思考」和「回复」出现同一段第三人称过程自述;回复在句中被截断,Turn 仍显示完成并扣点。普通咨询在 thinking 关闭时也会把过程写进正文。
- 触发条件:供应商 thinking 关闭后,模型把中文过程自述写进
text-delta并在写完口语结论前结束。 - 根因:BUG-354 为保住正文额度关闭了纠正和咨询的 provider thinking。
reasoning-delta不再出现,过程自述改走正文。BUG-357 再用正则从正文里切「思考 / 回复」,分类漏了「用户在上一轮 / 批量工具」,finalize 还把单段自述回灌成完成回复。 - 修复:纠正与咨询重新打开 provider thinking。
reasoning-delta进折叠「思考」,text-delta进「回复」。输出预算改为正文 16384 + 思考 8192,避免 CoT 抢正文额度。length续写仍关闭 thinking。正则只作正文漏检网和旧 Turn 水合,不再当主分流。没有口语结论时走empty_stream重试。 - 验证:
frontend/tests/rectification-spoken-answer.test.ts、frontend/tests/rectification-v9-stream.test.ts、frontend/tests/rectification-v9-agent.test.ts、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/consultation-agentic-runtime.test.ts、frontend/tests/consultation-workflow-contract.test.ts、frontend/tests/agent-activity-progress.test.ts、frontend/tests/chat-stream-layout.test.ts - 防复发:纠正与咨询组答若打开 thinking,必须把思考预算加进
maxOutputTokens,且不得把reasoning-delta混进answer.delta。不得用正则当思考/回复主分类器。length续写必须关 thinking。纯过程自述不得run.completed。 - 相关记录:BUG-345、BUG-346、BUG-354、BUG-357、BUG-358
- 复发自:BUG-354(关 thinking 后过程进正文)、BUG-357(用正则从正文里切两栏)
- 修复版本:
04463e9a
BUG-360 | 生时纠正后段思考被写进第一段「思考」
- 状态:resolved
- 首次发现:2026-08-23
- 最近更新:2026-08-23
- 影响面:生时纠正对话、
AgentActivityStatus、流式thinking.delta与tool.activity - 用户现象:界面先列出「读取校正记录」「正在整理多条事件证据…」,再给一整块「思考」。工具之后的思维链被追加进第一段思考,而不是出现在「正在整理…」下面。
- 触发条件:纠正回合先出现
thinking.delta,再tool.activity,之后继续thinking.delta。 - 根因:
AgentActivityStatus先渲染completedTrail和当前活动,再渲染整段thinkingText。流式处理把所有thinking.delta拼进同一个thinkingRaw,无法按事件顺序把后段思考放到工具行之后。 - 修复:纠正流按事件维护
activityTrace。工具开始会结束当前思考行;工具之后的thinking.delta开新的思考行。有轨迹时不再把拼接后的thinkingText单独渲在工具列表下面。 - 验证:
frontend/tests/agent-activity-trace.test.ts、frontend/tests/chat-stream-layout.test.ts、frontend/tests/consultation-run-timeline.test.ts - 防复发:纠正流不得把全部
thinking.delta拼成一块再画在活动列表下方。工具后的思考必须是新的 trace 行。咨询时间线同样不得把后段thinking.deltaupsert 回第一条思考。 - 相关记录:BUG-095、BUG-357、BUG-359
- 复发自:BUG-095(步骤轨迹只证明工具发生过,没有与思维链按时间交错)
- 修复版本:
6b225a28
BUG-361 | 生时纠正职业无日期证据卡死,反推前事问不出来,时间卡也不出
- 状态:resolved
- 首次发现:2026-08-23
- 最近更新:2026-08-23
- 影响面:
record_agentic_rectification_evidence_batch、method_followup_plan、isOfferBlockingFollowup、choice_card、discriminating_event_probes、生时纠正系统提示 - 用户现象:方法层已经问到职业,用户说了职业类型但没有日期。候选分钟在窗口里来回跳,Agent 反复问职业,从不按冲突年份做 A/B 反推,也不出示时间选择卡。
choice_card一直是 null。 - 触发条件:经典八法已覆盖带日期教育/感情/事业/家人;职业是无日期
occupation_note;引擎propose_allowed已开但分钟仍不可分;receipt 里有 dasha 年界/激活探针,同时教育质量探针占满 3 个槽。 - 根因:三层叠加。(1) 批量写入把
date_precision=unknown/ 缺occurred_from一律写成draft+needs_clarification=["date"],覆盖要求confirmed,职业层永远 uncovered,offer-candidates被拒。(2)remainingConflictProbes/remainingReverseVerifyProbes按已确认领域整段跳过,Python 质量探针又用已回答过的高考发挥题占满MAX_PROBES=3,反推年份进不了next_followup。(3) 引擎在 31 分钟窗、同分、宽度 7、holdout 1/20 时本来就不能唯一分钟;产品却继续访谈,而不是在方法覆盖齐且propose_allowed时出示代表性时间。 - 修复:无日期
occupation_note写入即确认,并回填已有 draft;路由把 draft/pending 职业笔记也算覆盖,忽略已经覆盖领域上的过期 collect focus。方法覆盖未齐时仍按 BUG-351 让 dasha 探针插队并挡出牌;挡住出牌的方法层齐了且propose_allowed时session_outcome=adopt_representative,event_probe不再挡出牌。采用后按「领域+年份」而不是整段领域跳过反推探针,并跳过已编码的考试质量题。不改已哈希 Skill10.0.11。 - 验证:
frontend/tests/rectification-eight-method.test.ts锁定 draft 职业笔记覆盖并放行出牌、过期职业 focus 不再追问、覆盖齐后event_probe不挡出牌、采用后核未出现过的事业年份;frontend/tests/rectification-choice-card.test.ts锁定可出牌时 GET A/B 卡为 null、采用后核对卡仍显示;frontend/tests/rectification-ingest-p0.test.ts锁定 dateless SQL;tests/test_rectification_event_probes.py锁定已编码高考质量题不再占满探针槽。 - 防复发:职业笔记不得因缺日期停在 draft。事业带日期事件仍不能代替职业层。方法覆盖未齐时 dasha 探针必须挡住出牌。覆盖已齐且
propose_allowed时不得为筛到唯一分钟继续访谈。不得把已回答的考试质量题再问一遍。不得改已哈希 Skill10.0.11来绕过服务器路由。 - 相关记录:BUG-348、BUG-349、BUG-350、BUG-351
- 复发自:BUG-351(冲突探针插队挡出牌后,质量探针占槽、职业 draft 永不覆盖,覆盖已齐时仍不收口)
- 修复版本:
75dbab08
BUG-362 | 生时纠正持续提问但不更新候选后验,无法收敛
- 状态:resolved
- 首次发现:2026-08-23
- 最近更新:2026-08-24
- 影响面:
rectification-agentic/core、method_followup_plan、discriminating_event_probes、rectification-v9-tools、decision receiptinference_state - 用户现象:纠正会话一直在收集经历并提问,但候选时间不固定、不淘汰,轮次增加后仍给不出可信区间或代表性时间。覆盖齐后低信息探针不再挡出牌,高信息未答探针仍可能挡出牌。
- 触发条件:出生时间范围为若干分钟;已有带日期事件;引擎给出多枚候选;Agent 继续用自然语言追问。
- 根因:收集、提问、打分和收口都挤在对话 Agent 里。没有版本化候选集、事件×候选评分账本、由候选差异算出的探针,也没有统一收敛评估。Prompt 要求“提出冲突问题”并不更新后验;职业/领域覆盖被误当成收敛。
- 修复:新增服务端推断状态机:固定
candidate_set_id、事件分层 holdout、等价分钟聚类、纯函数applyProbeOutcome、按信息增益选探针、统一evaluateConvergence。分数只由 reducer 更新。Python 探针带information_gain/expected_outcomes;followup 只改写程序给出的探针。decision receipt 持久化inference_state。点选 C/D/B 且没有新证据时,rectification-resolve-focus当场把后验写入追加的推断 transition,读路径 overlay 到引擎回执,不再等下一次带日期事件重算。高信息未答探针在覆盖齐后仍可挡出牌;无information_gain的旧探针保持 BUG-361 不挡出牌。无法区分时返回可信区间,最大轮次不是converged。 - 验证:
frontend/tests/rectification-inference-machine.test.ts锁定有效回答降低熵、淘汰不可复活、重复切分禁用、最大轮次≠成功、等价分钟返回区间、holdout 不参与训练、赢家需两轮稳定、C 无新证据立刻更新后验、D 只记录已问切分、盘外核对与收集拒答不写探针;frontend/tests/rectification-v10-conversation-focus.test.ts锁定 resolve-focus C 走append_agentic_rectification_inference_transition而不是候选缓存或原地 patch;frontend/tests/rectification-eight-method.test.ts锁定同领域不同年份仍问冲突探针、高信息探针覆盖后仍挡出牌、无增益探针覆盖后不挡出牌。 - 防复发:禁止 Agent 直接改候选分数或宣布收敛。禁止把方法覆盖或职业笔记当成
result_status=converged。同一semantic_key/candidate_split_hash不得连问。淘汰候选不得在同一candidate_set_id复活。最大轮次只能是max_rounds_reached。点选 C/D/B 且没有新证据时必须走append_agentic_rectification_inference_transition追加不可变 transition,禁止jsonb_set已落库的引擎decision_receipt,禁止走候选 persist RPC 的证据指纹缓存来写后验。读路径必须 overlay 最新 transition。盘外核对与收集阶段拒答不得写探针。下一轮 followup 必须读已答切分,避免把同一探针再问一遍。不得改已哈希 Skill10.0.11。 - 相关记录:BUG-361、BUG-351、BUG-363
- 复发自:BUG-361(覆盖门槛和出牌行为修了,但仍没有候选后验更新闭环)
- 修复版本:
29750d38
BUG-363 | 生时纠正推断后验原地改 receipt,persist-v2 只认 evidence fingerprint
- 状态:resolved
- 首次发现:2026-08-24
- 最近更新:2026-08-24
- 影响面:
agentic_rectification_inference_transitions、append_agentic_rectification_inference_transition、compose_agentic_rectification_decision_receipt、persist_agentic_rectification_candidate_v2、get_agentic_rectification_case_dossier、rectification-resolve-focus - 用户现象:C/D 回答可以改后验,但数据库里只剩更新后的分数,无法回放哪次回答改变了它;证据没变时 persist-v2 仍可能把旧 receipt 当成当前世界。
- 触发条件:点选 C/D 且没有新增带日期证据;随后读 Case、命中 persist-v2 缓存、刷新或重试同一回答。
- 根因:BUG-362 热修复用
patch_agentic_rectification_inference_state对最新decision_receipt.inference_state做jsonb_set。执行回执被原地修改。缓存键仍是 evidence/range/policy fingerprint,不含推断修订。 - 修复:追加不可变
agentic_rectification_inference_transitions。C/D 走append_agentic_rectification_inference_transition(乐观锁expected_revision、stale_probe、幂等键)。引擎结果行不再被 patch。persist-v2 HIT/MISS 与 dossier/get_case 用compose_agentic_rectification_decision_receiptoverlay 与当前result_id匹配的最新 transition。decision_state_fingerprint存在 transition 上,不进入 persist-v2 等值匹配。patch_agentic_rectification_inference_state改为inference_patch_retired。不改已哈希 Skill10.0.11。P0 inference ledger implemented, verification pending。 - 验证:
frontend/tests/rectification-inference-machine.test.ts锁定 C 立刻改后验且revision+1、evidence fingerprint 不变时 decision-state fingerprint 与 posterior 变、重复 D 幂等、C→D 新 revision 且旧 transition 仍在、旧 probe 为stale_probe、过期 revision 为revision_conflict、reducer replay 等于当前 posterior、候选集变化的 persist-v2 MISS 仍 replay 已答探针、append SQL 不 UPDATE 引擎行;frontend/tests/rectification-v10-conversation-focus.test.ts锁定 resolve-focus C 走 append RPC;frontend/tests/rectification-v9-agent.test.ts锁定 persist-v2 查找键不含p_decision_state_fingerprint,HIT 返回 overlay 后的inference_state;frontend/tests/rectification-v9-case-service.test.ts锁定 409stale_probe/revision_conflict/inference_patch_retired。P0 inference ledger implemented, verification pending。 - 防复发:禁止
jsonb_set已落库引擎decision_receipt来写后验。禁止把 inference revision 加进 persist-v2 等值匹配。有效 C/D 必须追加 transition。读inference_state必须 overlay 最新且result_id匹配的 transition。重复提交必须幂等。过期probe_id必须 409,不得改当前 posterior。不得改已哈希 Skill10.0.11。 - 相关记录:BUG-362
- 复发自:BUG-362(C/D 写入后验修了,但用原地 patch 绕过缓存,receipt 不可审计)
- 修复版本:待发布
BUG-364 | staging quality gate 在 Mac Docker runner 上 npm ci 因 bind mount 被拒
- 状态:investigating
- 首次发现:2026-08-24
- 最近更新:2026-08-24
- 影响面:Gitea
backend-quality-gate.yml的validate「Install dependencies」步骤、publish(因needs: validate被 skip)、staging 发布 - 用户现象:向
staging推送后质量门失败;测试步骤未执行。publishskipped。 - 触发条件:job 被调度到
jesse-mac-xiaoxin(Docker Desktop 里的ubuntu:24.04act_runner,labelxiaoxin:host,使用宿主机docker.sock)。Linux 宿主 runnerxiaoxin未接走该 job。已在 run2051(29750d38)和2052(909de8b8)复现。 - 根因:pip 安装成功。随后
docker run --volume "$workdir:$workdir"跑npm ci时,workdir 是 runner 容器内的/root/.cache/act/<id>/hostexecutor。该路径不在 Docker Desktop 的 Mac 文件共享里,daemon 返回mounts denied,步骤以 exit 125 失败。不是 ledger 测试或 Python 依赖错误。最近一次完整成功是 run2038,跑在 Linuxxiaoxin上。 - 修复:未修复。候选路径是让 Linux
xiaoxin重新在线并接runs-on: xiaoxin的 job,或不要用套了宿主机 docker.sock 的 Mac 容器跑这个 workflow。不要靠在 Docker Desktop 里共享 Linux 容器内部路径。 - 验证:Gitea job
5135日志含Successfully installed全套 pip 包,随后docker: Error response from daemon: mounts denied与frontend npm ci failed with status 125。job5133(run2051)同一错误。尚未在 Linuxxiaoxin上重跑909de8b8。 - 防复发:
runs-on: xiaoxin不得由无法给 job workdir 做 bind mount 的嵌套 Docker runner 抢占。质量门失败不得在未读 Install dependencies 日志时当成业务测试失败。 - 相关记录:BUG-136、BUG-149、BUG-266、BUG-363、BUG-365
- 复发自:无
- 修复版本:未修复
BUG-380 | 覆盖完成后仍用整窗 D9/D24 追问已入账日期
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
session_outcome、candidate_contrast_packet、method_followup_plan、record_agentic_rectification_evidence_batch、生时纠正系统提示 - 用户现象:方法覆盖已齐、引擎探针已空、候选仍是并列区间时,访谈继续问整窗学业/感情盘,并再次追问已入账的入学或分手日期。同一日感情结束用不同原句会写入第二行。
- 触发条件:挡住出牌的方法层已齐;剩余候选分钟共享 D9;学业质量与职业备注已入账;引擎
discriminating_event_probes为空;用户用另一句话重复已记日期。 - 根因:(1) 收集计划把整窗
varga_observation当成区分探针,discriminatorProbe: null被??回退成该追问,会话无法进入provisional_range。(2) 剩余分钟拆分仍问已编码的高考发挥与职业类型,且 D4 不在剩余层顺序里。(3) 证据批处理只按 quote 做幂等。 - 修复:区分探针只来自引擎探针和当前候选分钟的剩余分盘;已编码的学业质量/职业备注跳过 D24/D10;加入 D4 剩余拆分。没有剩余拆分时
next_followup为空并落实offer_provisional_range。同 case、同 kind、同发生日的确认行按日期去重。不改 Skill10.0.11。 - 验证:
frontend/tests/rectification-eight-method.test.ts、frontend/tests/rectification-decide-next-action.test.ts、frontend/tests/rectification-inference-machine.test.ts、frontend/tests/rectification-v9-agent.test.ts、frontend/tests/rectification-v9-migration.test.ts。 - 防复发:覆盖完成后不得把整窗 D9/D10/D24 观察当成区分探针。已入账的入学、分手日期和考试质量不得再问。没有剩余分钟拆分时必须给并列区间,不得继续访谈。不得改已哈希 Skill
10.0.11。 - 相关记录:BUG-192、BUG-351、BUG-366、BUG-375、BUG-379
- 复发自:BUG-379(邻近年学业存在性探针已跳过,但仍用整窗观察和剩余 D24 题干继续问入学/高考)
- 修复版本:待发布
BUG-381 | 生时纠正进度条与点选卡重叠、分盘名消失
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:生时纠正聊天气泡、阶段进度、点选卡、「回到最新」
- 用户现象:有工具进度的回复看起来比上一句更往里缩;阶段和正文之间空一大截;进度条只剩「读取校正记录 / 整理证据」,看不到 D 盘对照;点选卡最下面的 D 和「先这样」被「回到最新」挡住。
- 触发条件:本轮有公开工具进度;证据写入触发了重算但比较工具没单独出场;用户略微离开底部看点选卡。
- 根因:(1) 有进度又有正文时套了咨询页
consultation-thinking-report,格子间距 24px,进度条自己还有下边距。(2) 已落盘回合不回放工具进度,最新一轮有勾选列、上一轮没有,看起来像缩进。(3) 证据批处理把executed_methods放在rescore里,公开活动只读顶层字段,技法句又写在口语后面。(4) 「回到最新」贴在输入框上方居中,离开底部 96px 就出现,正好盖住点选卡下沿。 - 修复:进度和正文改用 8px 紧凑叠放。已落盘回执回放工具步骤。证据重算的分盘名并进进度条,不写进口语。点选卡仍在视口下沿时不显示「回到最新」,按钮改到末行外侧。不改 Skill
10.0.11。 - 验证:
frontend/tests/chat-stream-layout.test.ts、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/rectification-answer-choice.test.ts、frontend/tests/rectification-v9-stream.test.ts、frontend/tests/agent-activity-progress.test.ts。 - 防复发:有进度的纠正气泡不得用咨询页 24px 报告间距。技法句属于进度条,不得再进
answer.delta。点选卡仍可见时不得用居中浮层挡住 D / 「先这样」。 - 相关记录:BUG-373、BUG-376、BUG-377
- 复发自:BUG-376(关掉思考正文后,进度条只剩工具名,技法句不再出现;回到最新仍按咨询页浮层)
- 修复版本:待发布
BUG-382 | 点选题与剩余切开不是同一题型,选题不是按剩余最大切开
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
discriminating_event_probes、candidate_contrast_packet、choice_card、method_followup_plan、window_scan transitions - 用户现象:生时纠正点选卡一律问「某年前后有没有这类事」。口语在问相处或职责时,卡仍问存在性。剩余分钟其实还能被家人盘切开时,下一问仍按写死的学业/事业/感情/搬家顺序出。
- 触发条件:覆盖已齐、候选仍并列;剩余 D9/D10 换升,或 D12/D7 切开而 D9 已齐;或大运年界与剩余切开同时存在。
- 根因:(1) 选择题走
eventHypothesis存在性模板,D10 内部职责三分仍显示成有没有这件事。(2) Python 探针按整窗*_candidates_differ和精度阶段优先选题,每个领域取第一个能分开的年。(3) TS 剩余层写死D24→D5→D10→D9→D4,并用假 IG 让学业压过事业/感情。 - 修复:计分领域按当前剩余分钟仍切开的层进池,财务/健康仍要用户先提过。Python 对剩余分钟打分,按分组熵加置信度取最高年。TS 按分组熵选题。年界差问存在性;D9/D10 换升问类型表相处/职责;学业问考试质量。两路 style 的「都不像」计
unsure,不把另一组分钟当「没有这件事」。不改 Skill10.0.11。 - 验证:
tests/test_rectification_event_probes.py、tests/test_rectification_refinement_packet.py、frontend/tests/rectification-candidate-contrast-packet.test.ts、frontend/tests/rectification-choice-card.test.ts、frontend/tests/rectification-inference-machine.test.ts。 - 防复发:剩余分钟选题不得写死学业优先。点选卡题型必须跟
choice_kind与expected_outcomes一致。两路 style 的 C 不得淘汰另一组分钟。不得改已哈希 Skill10.0.11。 - 相关记录:BUG-375、BUG-379、BUG-380
- 复发自:BUG-375(区分卡已落到剩余分钟,但题型仍是存在性,选题顺序仍写死)
- 修复版本:待发布
BUG-383 | 点选卡开关在 render 里写 ref,staging ESLint 失败
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:Gitea
backend-quality-gate的npm run lint --prefix frontend、frontend/src/components/rectification-agentic-chat.tsx - 用户现象:向
staging推送f5e73ef3后 quality gate run2073失败。validatejob5170前端测试 2042/2042 通过,随后 lint✖ 17 problems (1 error, 16 warnings)。publishjob5171因needs: validate跳过。 - 触发条件:含 BUG-381「回到最新」避让点选卡的提交进入完整
xiaoxinrunner 的 frontend lint。 - 根因:
react-hooks/refs禁止在 render 期间更新ref.current。BUG-381 用choiceCardsOpen.current = showChoiceCards给滚动回调最新点选卡开关,写在 render 里。父提交7a4360d8的 run2072已因同一行失败。 - 修复:把 ref 写入现有
useLayoutEffect,再调用updateFollowState。不改 Skill10.0.11。 - 验证:
npx eslint src/components/rectification-agentic-chat.tsx;frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:点选卡可见状态不得在 render 里写
ref.current;源码合同锁定该赋值只出现在 layout effect。 - 相关记录:BUG-326、BUG-381
- 复发自:BUG-326(窄屏盘面
onCloseRef.current = onClose同样在 render 里写 ref) - 修复版本:待发布
BUG-384 | 记下事件后口语与点选卡不是同一题
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
rectification-record-evidence-batch、projectTurnDecision、persistServerOwnedFocus、bindSpokenToOpenQuestion、生时纠正运行器 - 用户现象:刚记下大学毕业后,气泡问「2016 年前后入学考试有没有发挥失常」,点选卡却是「2023 年前后有没有入职或职责加重」。题干和选项是服务器模板,没有跟口语对齐。
- 触发条件:账本已有带日期学业事件;引擎发出 dasha 冲突探针并盖戳点选卡;本轮只调了
record-evidence-batch,没有compare-candidates。 - 根因:(1) 证据写入后的自动重算会
persistServerOwnedFocus,但 batch 工具结果没有把open_question.prompt回给模型。(2)turn_decision.current_question只有 id,没有题干。模型按上一轮学业对话另起了考试质量问,界面 GET 却展示已盖戳的事业卡。(3) 选题必须用刚算完的 decision receipt;若只信 persist RPC 回包,探针可能被丢掉。(4) 用系统提示逐条禁止高考发挥/入学年份堵不住用户回答的多种写法。点选卡仍由服务器出,保证点选直接改后验。 - 修复:batch / confirm 重算后返回
open_question.prompt;选题用刚写入的 receipt。有已盖戳题干时,运行器丢掉口语里的其它问句并接到这句题干,不在提示词里枚举禁止主题。不改 Skill10.0.11。 - 验证:
frontend/tests/rectification-eight-method.test.ts、frontend/tests/rectification-answer-choice.test.ts、frontend/tests/rectification-v9-agent.test.ts、frontend/tests/rectification-v9-stream.test.ts。 - 防复发:有点选卡时口语由运行器接到
open_question.prompt/current_question.prompt,不得靠提示词枚举禁止追问。证据写入后的工具结果必须带回已持久化题干。不得改已哈希 Skill10.0.11。 - 相关记录:BUG-375、BUG-379、BUG-382
- 复发自:BUG-375(点选卡已由服务器盖戳,但模型仍按探针列表另写一问)
- 修复版本:待发布
BUG-385 | 「回到最新」贴在右下角,没有水平居中
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:生时纠正「回到最新」
- 用户现象:向上翻看历史时,「回到最新」出现在输入框右上角,而不是聊天区水平居中。
- 触发条件:对话不在底部;BUG-381 之后的布局。
- 根因:BUG-381 为了不挡住点选卡的 D / 「先这样」,把按钮改成
justify-content: flex-end。点选卡仍在视口下沿时本来就会隐藏该按钮,不必靠右。 - 修复:按钮改回水平居中。点选卡仍占 overlay 带时继续隐藏。不改 Skill
10.0.11。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/rectification-answer-choice.test.ts。 - 防复发:「回到最新」必须水平居中;不得为了避让点选卡改到右下角,避让靠隐藏阈值。
- 相关记录:BUG-381
- 复发自:BUG-381(避让点选卡时把居中改成了靠右)
- 修复版本:待发布
BUG-386 | 采用门未齐就按冲突时间出反推前事点选卡
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
method_followup_plan、meetsAcceptanceEventQuality、生时纠正系统提示 - 用户现象:刚记下学业等一两件带日期经历后,点选卡就按 dasha 冲突年反问另一领域「某年前后有没有入职」。方法层感情/事业还没问完,采用门也还没到 3 件可评分事件、2 个领域。
- 触发条件:账本已有至少一件可评分事件,但不足 3 件或不足 2 个领域;decision receipt 含
dasha_activation/dasha_boundary探针。 - 根因:BUG-351 在第一件带日期事件之后就让冲突探针插队并盖戳点选卡。引擎采用门仍要 3 件/2 领域,访谈却提前进入反推前事。
- 修复:冲突探针插队改到与采用门相同的事件质量门槛:至少 3 件主评分事件、至少 2 个领域。
occupation_note与外貌/疤痕/占问不计。未齐时继续感情→事业→家人→职业自然语言收集,不盖冲突卡。齐了之后仍按 BUG-351 插队并挡出牌。不改 Skill10.0.11,不打开confirmation_allowed。 - 验证:
frontend/tests/rectification-eight-method.test.ts、frontend/tests/rectification-v9-agent.test.ts、frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:不得在采用门事件/领域未齐时把 dasha 冲突探针写成
source=event_probe或挂choice_frame。不得把occupation_note算进 3 件。不得改已哈希 Skill10.0.11。 - 相关记录:BUG-351、BUG-361、BUG-379
- 复发自:BUG-351(一件带日期事件后冲突探针插队,早于采用门)
- 修复版本:待发布
BUG-387 | staging publish 的 next build 被口语绑定 chunk 类型挡住
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
openQuestionPromptFromToolResult、runV9AgentTurn、deploy/railway-web.Dockerfile的RUN npm run build - 用户现象:向
staging推送97b02aef后 quality gate run2080的 validate 通过,publish 在 web 镜像next build失败。没有 dispatchDeploy staging。公网仍为上一成功 SHA。 - 触发条件:staging push 的 publish 镜像构建跑
next build。validate 对 staging push 跳过 production build。 - 根因:运行器把流式 chunk 传给
openQuestionPromptFromToolResult,函数把payload收成{ result?: unknown; output?: unknown }。流式 chunk 的 payload 是toolName/text/args,TypeScript 判为 TS2345。日志末尾 Import traces 只是动态文件访问警告。 - 修复:函数改收
payload?: unknown,再从 result/output/object 读已盖戳题干。不改 Skill10.0.11。 - 验证:
npx tsc --noEmit不得再报agent-run.ts的 TS2345;frontend/tests/rectification-v9-stream.test.ts。 - 防复发:改
runV9AgentTurn流式 chunk 形状后必须跑npx tsc --noEmit或等价的next build。staging push 的类型回归仍只在 publish Docker 暴露。 - 相关记录:BUG-309、BUG-365、BUG-371、BUG-384
- 复发自:BUG-309(validate 跳过
next build,publish 才暴露类型错误);BUG-384 口语绑定收窄了 payload - 修复版本:待发布
BUG-388 | 证据已写入并重算后整轮仍被 105 秒 attempt 超时打成 run_timeout
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
runV9AgentTurnattempt 超时、record-evidence-batch自动重算、compare-candidates、公开run.failed、已盖戳open_question - 用户现象:自由文本回答当前追问后,界面已显示读取 Case、记下证据、比较候选(两次工具完成事件都带同一套已执行方法),随后
run.failedcode=run_timeout,文案「服务端运行超时,状态已记录。」没有口语气泡,也没有点选卡。 - 触发条件:
POST /api/rectification/agentaction=message;本轮record-evidence-batch已accepted并自动重算;模型接着调用compare-candidates,再以相同参数第二次read-case;attempt 墙钟超过 105 秒。本轮没有已盖戳点选卡可点。 - 根因:(1) 运行器 attempt 超时是 105 秒,路由
maxDuration120 秒;Python 评分单次上限 60 秒。scoreAndPersistCurrentEvidence每次都先打引擎,指纹缓存只发生在随后的 persist RPC,因此 batch 自动重算之后的 compare 会再跑一轮完整评分。(2) 第二次read-case只有completed没有started,符合相同参数重复 tool-call 被跳过 started 回执。(3) 超时走failedAttempt,发生在把已盖戳题干接到口语之前,所以工具已提交的状态不会变成可见回复。 - 修复:attempt 超时提到 210 秒,agent / regenerate 路由
maxDuration提到 240 秒。证据指纹和候选窗指纹都未变时 compare 直接复用已落盘快照,不再打 Python。超时且本轮已盖戳open_question时仍落该题干并完成本轮,而不是空失败。不改 Skill10.0.11,不打开confirmation_allowed。 - 验证:
frontend/tests/rectification-v9-status-security.test.ts、frontend/tests/rectification-v9-stream.test.ts、frontend/tests/rectification-answer-choice.test.ts。 - 防复发:compare 在相同证据/范围指纹上不得再调用
runV9CandidateScore。attempt 超时必须小于路由maxDuration。超时后若工具已盖戳题干,不得把整轮打成空run_timeout。不得把 Cookie、JWT、案例 ID 或用户原文写入本记录。 - 相关记录:BUG-078、BUG-368、BUG-384、BUG-389
- 复发自:BUG-078(评分已完成后叙事超时把整轮打成失败)
- 修复版本:待发布
BUG-389 | 已记下学业年后发挥质量追问没有点选卡
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-09-10
- 影响面:
method_followup_plan、persistServerOwnedFocus、known_event_quality、askedKeysForSameYearDedup - 用户现象:记下带年份的学业经历后,口语会问那次考试有没有发挥失常,界面却没有 A/B/C/D 点选卡,只能打字。dasha 冲突反推卡仍按采用门等待,不在此列。
- 触发条件:账本已有该年学业等可评分事件;decision receipt 含
known_event_quality;采用门 3 件/2 领域未齐,下一方法层仍是感情收集。 - 根因:
remainingReverseVerifyProbes跳过known_event_quality。采用前只有 dasha 冲突探针能变成event_probe点选卡,且还要等 3 件/2 领域。发挥质量探针留在 receipt 里,模型用自然语言问,服务器不盖choice_frame。即便盖了,information_gain为 0 也会被shouldSkipDiscriminatorFollowup丢掉。 - 修复:已记下对应年份后,
known_event_quality在方法轮换之前出event_quality点选卡,不要求采用门。摘要已编码发挥质量则不再出卡。零信息增益不再挡住发挥质量卡。dasha 冲突探针仍等 3 件/2 领域。不改 Skill10.0.11。2026-09-10:账本派生的domain.year键不得再把发挥质量题判成same_year_asked(见 BUG-637)。 - 验证:
frontend/tests/rectification-eight-method.test.ts、frontend/tests/rectification-server-focus.test.ts、tests/test_rectification_event_probes.py;2026-09-10 补rectification-probe-year-dedupe-20260906.test.ts、rectification-engine-convergence.test.ts。 - 防复发:已记下年份的发挥质量探针必须挂
choice_frame并持久化。不得把 dasha 冲突探针的采用门门槛套到发挥质量卡上。不得把confirmation_allowed改成 true。账本domain.year键只拦存在性探针。 - 相关记录:BUG-348、BUG-384、BUG-386、BUG-388、BUG-592、BUG-637
- 复发自:BUG-384(口语问发挥质量,点选卡却被冲突探针占住;采用门修好后变成完全没有卡);2026-09-10 再次复发自 BUG-592(见 BUG-637)
- 修复版本:待发布
BUG-390 | 事业经历未过采用门就出高考发挥点选卡
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
method_followup_plan、event_probes._quality_probes、choice-cardevent_quality、GETchoice_card - 用户现象:记下同年实习入职和离职后,点选卡直接问「那年前后高考或重要考试有没有发挥明显失常」。采用门仍是 2 件/1 领域,方法层感情还没问。
- 触发条件:账本只有事业可评分事件,不足 3 件或不足 2 个领域;引擎把该年事业写成
known_event_quality。 - 根因:BUG-389 让
known_event_quality在采用门前插队并盖戳。Python_quality_probes对每个已记领域都发质量探针,事业的quality_family与存在性相同。choice-card又把所有event_quality写成高考发挥题,事业探针被盖成考试卡。 - 修复:质量探针只对学业(
QUALITY_HINTS)发出。Followup 只让学业质量卡在采用门前插队;事业质量探针继续走方法轮换。点选题干不再套高考句,也不再回退「有没有明显…」存在性模板;服务器只锁period · family。dasha 冲突探针仍等 3 件/2 领域。不改 Skill10.0.11,不打开confirmation_allowed。 - 验证:
frontend/tests/rectification-eight-method.test.ts、frontend/tests/rectification-choice-card.test.ts、tests/test_rectification_event_probes.py。 - 防复发:不得把非学业
known_event_quality写成采用门前点选卡。不得把高考发挥题干套到事业/感情探针。不得把存在性「有没有明显…」句当成用户可见题干。不得把occupation_note算进 3 件。 - 相关记录:BUG-351、BUG-386、BUG-389
- 复发自:BUG-389(学业发挥质量卡免检插队后,事业年也被当成发挥质量并套用高考题干)
- 修复版本:待发布
BUG-391 | 生时纠正点选题干走模板,Agent 自己写的问句被丢掉
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
bindSpokenToOpenQuestion、agentic-rectification系统提示、choice-cardevent_quality、GETchoice_card - 用户现象:记下事业经历后,界面和口语都问「那年前后高考或重要考试有没有发挥失常」。Agent 即使按探针写了入职问句,运行器也会丢掉,改贴服务器模板。
- 触发条件:工具返回了
open_question.prompt;模型正文里另有一句问句。 - 根因:BUG-384 为了对齐点选卡,把已盖戳题干当成唯一追问,
bindSpokenToOpenQuestion删除所有带问号的模型句。event_quality又把所有发挥质量卡写成高考题。系统提示要求「不要另写追问」。 - 修复:年份和事件家族仍由探针锁定,A/B/C/D 仍由服务器出。Agent 自己写追问;问对年份且未改领域时保留原句,并盖到点选卡题干。服务器只持久化
period · family锁,不代写「有没有明显…」题干;问错时只留应答,不把锁贴进口语。发挥质量选项标签仅学业用失常档。不改 Skill10.0.11,不打开confirmation_allowed。 - 验证:
frontend/tests/rectification-v9-stream.test.ts、frontend/tests/rectification-v9-agent.test.ts、frontend/tests/rectification-choice-card.test.ts、frontend/tests/rectification-eight-method.test.ts。 - 防复发:不得再要求模型只复读已持久化题干。不得把高考发挥句套到非学业探针。不得把存在性模板句当成用户可见题干。问错领域时不得把锁贴进口语。
- 相关记录:BUG-348、BUG-384、BUG-390
- 复发自:BUG-384(点选卡与口语句不一致时,改成强制模板,连对得上的 Agent 问句也丢掉)
- 修复版本:待发布
BUG-392 | 生时纠正把存在性模板句当成用户题干
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
choice-cardeventHypothesis、bindSpokenToOpenQuestion、persistServerOwnedFocus、GETchoice_card - 用户现象:点选卡和口语仍是「那年前后,有没有明显入职…」或高考发挥句。用户要求 Agent 自己出题,服务器只锁年份和事件家族。
- 触发条件:工具盖戳
open_question.prompt后,GET 或运行器把已落盘 copy 当作用户可见题干。 - 根因:
eventHypothesis把锁写成${period},有没有明显${family}?。persistServerOwnedFocus在 Agent 开口前盖这句。bindSpokenToOpenQuestion在模型没问或问错时把这句贴进口语。 - 修复:事件卡只持久化
period · family。Agent 写追问;问对则口语和点选卡都用该句。问错或超时只留应答/记下了。,不把锁当问句。旧库里带问号的题干仍可作最后兜底。不改 Skill10.0.11,不打开confirmation_allowed。 - 验证:
frontend/tests/rectification-choice-card.test.ts、frontend/tests/rectification-v9-stream.test.ts。 - 防复发:不得把「有没有明显…」或高考发挥句写成
serverOwnedChoiceCopy.prompt。不得把无问号的锁贴进answerText。 - 相关记录:BUG-348、BUG-384、BUG-391
- 复发自:BUG-391(保留 Agent 问句后,仍用存在性模板句当服务器兜底题干)
- 修复版本:待发布
BUG-393 | 生时纠正把点选卡当成纠正完成,未按候选差异驱动
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
discriminating_event_probes、candidate_contrast、method_followup_plan、decision receipt、inference transitions、Skill 10.0.11 运行时合同 - 用户现象:纠正流程以“显示一张选择题卡”为完成标准。质量探针混进区分题,信息增益为 0、候选映射为空。一条事件后只保留三个相邻分钟,Agent 自行发明年份或候选映射。
- 触发条件:账本可评分事件不足 3 件或不足 2 个领域;或引擎对完整出生窗做特征签名聚类之前就截成 top-3 相邻分钟。
- 根因:(1)
known_event_quality与age_band混入discriminating_event_probes且role=distinguish。(2)build_candidate_decisions用ranked[:3]。(3) 前端discriminatorFromFollowup伪造left/right映射。(4) inference 轮次没有一等字段保存熵与淘汰名单,零信息回答仍可被当成有效区分。 - 修复:探针拆成 evidence_collection / event_clarification / candidate_discriminator / holdout_validation。质量探针只进 clarification。区分探针必须
candidateIds>=2、expectedOutcomes>=2、informationGain>0,hash 含真实分组和candidateSetVersion。门未开只收集缺失领域。完整出生窗按 D9/D10/D24/D4/D12 签名聚类。引擎输出CandidateContrastOpportunity,Agent 只改写。用户回答写入 inference round(posterior/delta/entropy/eliminated);不改变候选则low_information。CI 禁止空映射或非正增益的 distinguish 探针。不改 Skill10.0.11。 - 验证:
tests/test_candidate_discriminator_contract.py(已列入CORE_PYTEST_TARGETS)、tests/test_rectification_event_probes.py、frontend/tests/rectification-distinguish-contract.test.ts。 - 防复发:不得把
known_event_quality放进discriminating_event_probes。不得在一条事件后只留三个相邻分钟。不得让 Agent 发明年份、事件事实或候选映射。role=distinguish且informationGain<=0或候选/结果为空时 CI 必须失败。 - 相关记录:BUG-375、BUG-379、BUG-386、BUG-389、BUG-390、BUG-391、BUG-392
- 复发自:BUG-386(采用门前发出区分题);BUG-375(slice-3 / 相邻分钟)
- 修复版本:待发布
BUG-394 | 选择题身份、决策器、holdout 与区间收口未闭合
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
answer_choice、ConversationFocus、RectificationDecision、credible range、per-case holdout、公开流、聊天滚动、生时纠正完成路径 - 用户现象:刚返回的点选卡立刻
stale_question;A/B/C/D 被模型重新理解;覆盖完成被当成可确认唯一分钟;可信范围只覆盖第一名;收集阶段不留 holdout;普通用户卡在 exact-minute 门上无法结束;流式输出抢走滚动并泄漏思考过程。 - 触发条件:GET 卡片用派生
questionId当库身份;precision_stage/selection_allowed在 decision 之外独立判断;credibleRange取 rank=1 簇;holdout 要到 4 条才预留;确认门 fail-closed 后没有completed_with_range。 - 根因:选择题把派生
question_id当成 focus 主键。业务状态有多处 reducer。评分和 Probe 吃全部已收事件。无法区分唯一分钟时仍按 exact-minute 拦完成。 - 修复:GET/POST 统一
focusIdUUID +caseRevision;stale_question只表示 focus 非 active。单一decideRectification计算 phase / nextAction / canOfferRange / canAdopt / canConfirmExactMinute。credibleRange并集仍有效并列簇。收集满 2 条带日期事件即预留 holdout,不参与初筛、评分和 Probe。收敛后必须holdout_validation,失败回到区分。无法唯一分钟时允许completed_with_range。点选同一事务写 receipt / 后验 / focus / inference / revision / NextAction,actionId幂等。Narrator 失败不回滚状态。公开流禁止 reasoning / thinking / 工具步骤正文。近底部才跟随滚动,上滑后显示「回到最新」。不改已哈希 Skill10.0.11。 - 验证:
frontend/tests/rectification-answer-choice.test.ts、frontend/tests/rectification-hidden-e2e.test.ts、frontend/tests/rectification-decide-next-action.test.ts、frontend/tests/rectification-inference-machine.test.ts、tests/test_candidate_discriminator_contract.py。 - 防复发:不得用派生
questionId当数据库身份。刚返回且 Case 未更新的卡片不得stale_question。不得在 decision 之外独立判断precision_stage/selection_allowed/active_focus。不得只用 rank=1 生成credibleRange。holdout 不得进入评分或区分 Probe。不得因 exact-minute 未过就让普通用户无法完成。确定性点选错误不得attempt.reset。点选验收必须看到后验变化和 holdout/收口,只出选择题卡不算通过。 - 相关记录:BUG-367、BUG-368、BUG-366、BUG-393、BUG-379
- 复发自:BUG-367(点选身份与滚动);BUG-366(覆盖完成被当成收敛);BUG-393(出卡即完成)
- 修复版本:待发布
BUG-395 | staging publish 的 next build 被 holdout 类型和重复 re-export 挡住
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:Gitea
backend-quality-gate.ymlpublish、deploy/railway-web.Dockerfile的RUN npm run build、core/index.ts、method-followup.ts - 用户现象:向
staging推送a0ce55f0后 quality gate run2088validate 通过,publish 在 web 镜像next build失败。没有 dispatchDeploy staging。公网仍为上一成功 SHA7718317d。第一次失败是 publish exact-SHA checkout 三次超时;重跑后 checkout 成功,类型检查才暴露。 - 触发条件:staging push 的 validate 跳过
npm run build;publish 才在 Docker 里跑 TypeScript。 - 根因:(1)
core/index.ts对decide-next-action.ts和rectification-decision.ts都export *,两者都导出offerSessionKinds/sessionKindFromNextAction,触发 TS2308。(2) BUG-394 的 holdout 追问写成method_id: "holdout_validation"和ask_theme: "holdout",未加入MethodFollowup联合类型。 - 修复:barrel 只具名导出
decideNextAction及其输入类型,session helpers 只从rectification-decision.ts再导出。MethodFollowup联合类型补上holdout_validation与holdout。不改 Skill10.0.11。 - 验证:
npx tsc --noEmit不得再报core/index.tsTS2308 或method-followup.tsTS2322;frontend/tests/rectification-decide-next-action.test.ts。 - 防复发:
core/index.ts不得再export *两个都导出 session helpers 的模块。holdout 追问的method_id/ask_theme必须在MethodFollowup联合类型里。改决策 barrel 或 holdout followup 后必须跑npx tsc --noEmit或等价的next build。staging push 的类型回归仍只在 publish Docker 暴露。 - 相关记录:BUG-309、BUG-365、BUG-371、BUG-387、BUG-394
- 复发自:BUG-309(validate 跳过
next build,publish 才暴露类型错误);BUG-394 重复导出 session helpers 且 holdout 种类未进联合类型 - 修复版本:待发布
BUG-396 | Holdout 门槛、用户停止收口与决策入口未钉死
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:training 门槛、
decideRectification、session_outcome、/api/health数据库版本、Skill 10.0.11 示例 - 用户现象:3 条事件预留 1 条 holdout 后可能既不能出 Probe 又以为事件够了;用户主动停止被写成已验证完成;健康检查只证明镜像 SHA,不证明迁移已执行。
- 触发条件:满 2 条即预留 holdout,同时区分门按全部已收事件计 3 条;用户停止与 holdout 通过共用
completed_with_range;/api/health只有deployment.gitCommit。 - 根因:区分门槛把 holdout 算进可评分事件。用户停止与验证通过共用收口状态。公开决策字段仍可能读 snapshot 而不是 reducer。
- 修复:区分和冲突探针只计 training 事件:3 training / 2 training 领域才开门。2 条继续收集;3 条(2 training + 1 holdout)继续收集;4 条(3 training + 1 holdout)才进入区分。用户停止为
provisional_range_user_stopped,holdout 通过为validated_range,不得把停止写成已完成验证。GET/点选/刷新/工具投影的selection_allowed等从decideRectification派生。/api/health增加database.latestMigration与rectificationContractVersion=v3。Skill 10.0.11 仍不改写;后续新建 10.0.12 时用{timeWindow}/{semanticTarget}/{domain}抽象示例。 - 验证:
tests/test_candidate_discriminator_contract.py、frontend/tests/rectification-decide-next-action.test.ts、frontend/tests/rectification-decision-authority.test.ts、frontend/tests/rectification-hidden-e2e.test.ts、frontend/tests/health-deployment.test.ts。GET/api/rectification/cases/[caseId]的latest_result.selection_allowed与interview由decideFromDossier覆盖,不得回退 snapshot。 - 防复发:不得用含 holdout 的总事件数开区分门。不得把用户停止标成
validated_range或“最终校正结果”。不得在 interview / answer_choice / turn_decision / Case GET 里独立判断selection_allowed。不得只凭gitCommit声称迁移已执行。不得原地改 Skill10.0.11。后续 Skill10.0.12只用{timeWindow}/{semanticTarget}/{domain}。 - 相关记录:BUG-394、BUG-395、BUG-393、BUG-366
- 复发自:BUG-394(holdout 预留后未把门槛改成 training);BUG-395(staging publish 被 holdout 类型和重复 re-export 挡住)
- 修复版本:待发布
BUG-397 | staging web 因 health 直查 migration 账本而不健康
- 状态:resolved
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
GET /api/health、deploy-staging.ymlrun2094、app_runtime权限、生时纠正 schema 合同 - 用户现象:quality gate run
2093成功后自动 Deploy staging。web 容器一直不健康,部署回滚。公网仍为上一成功 SHA0511bc48。 - 触发条件:self-hosted web 用
APP_DATABASE_URL(app_runtime)跑健康检查;compose healthcheck 把非 2xx 当成失败。 - 根因:
app_runtime按 foundation 合同不得SELECT migration.schema_migrations。health 直查该表后权限失败被标blocked,接口 503,Docker 判定 web unhealthy。 - 修复:新增
20260826030000_rectification_migration_ledger_health.sql,SECURITY DEFINER函数rectification_schema_migration_filenames()只把文件名暴露给app_runtime。health 改查该函数。账本可读且缺合同文件才blocked/503;查询失败为degraded,不把容器打挂。合同升到v4。不把业务 SQL 拷进frontend/db/migrations。 - 验证:
frontend/tests/health-deployment.test.ts。staging 必须先Migrate Staging Database再Deploy staging;GET /api/health的deployment.gitCommit等于本次 SHA,且database.requiredMigrationsPresent=true、rectificationContractVersion=v4。 - 防复发:health 不得直查
migration.schema_migrations。app_runtime不得获得该表 SELECT。缺迁移仍由 checker 拦应用发布。不得只凭gitCommit声称迁移已执行。 - 相关记录:BUG-396、BUG-144、BUG-127
- 复发自:BUG-396(health 要证明迁移,但走了
app_runtime禁止的账本查询) - 修复版本:待发布
BUG-379 | 生时纠正已记入学后仍编造高考年并再问入学
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
discriminating_event_probes、method_followup_plan、serverOwnedChoiceCopy、生时纠正系统提示 - 用户现象:已经记下入学年月后,下一问仍假定某年高考、再问大学哪年入学或毕业。点选卡题干变成空泛的「有没有这件事」。
- 触发条件:账本已有带日期
education_start;引擎仍按出生年+17 发出邻近年学业存在性探针;模型把探针年说成已经发生的事实。 - 根因:(1) 学业存在性探针只跳过同一日历年,高考/入学同一学年的前一年仍会出题。(2) 冲突探针在方法覆盖未齐时插队,挡住事业等下一层。(3) 系统提示只写「不得发明年份」,没有禁止把探针年说成事实,也没有禁止再问已入账的入学/分手日期。(4) 服务器点选题干写成「有没有这件事」,丢掉事件家族。
- 修复:学业存在性探针跳过已记学业年的 ±1 年;质量探针仍按精确年。Followup 对邻近年存在性探针不再插队。追问提示带上已记入学/毕业/感情起止,并禁止把未证实年份说成已经发生。点选题干改用假设句里的事件家族。不改 Skill
10.0.11。 - 验证:
frontend/tests/rectification-eight-method.test.ts、frontend/tests/rectification-choice-card.test.ts、frontend/tests/rectification-v9-agent.test.ts、tests/test_rectification_event_probes.py。 - 防复发:已有入学证据时不得再问入学年,也不得把年龄带推算的高考年说成事实。邻近年学业存在性探针不得挡住下一方法层。点选题干必须带事件家族,不能只写「有没有这件事」。不得改已哈希 Skill
10.0.11。 - 相关记录:BUG-192、BUG-351、BUG-375
- 复发自:BUG-192(已说外地上大学后仍换词重问迁居;本次是已记入学后按年龄带编造高考年再问入学)
- 修复版本:待发布
BUG-378 | staging 质量门 2021/2022:activity 合同仍要求 settled.spoken
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:Gitea
backend-quality-gate.ymlvalidatenpm test --prefix frontend、rectification-activity-receipt.test.ts、staging 发布 - 用户现象:向
staging推送2e8c748b后 quality gate run2069失败。validatejob5163前端 2021 通过、1 失败。publishjob5164因needs: validate1 秒跳过。 - 触发条件:完整
xiaoxinrunner 跑前端全量测试。 - 根因:BUG-377 把直播
answer.delta改成原样显示raw,聚焦套件未覆盖rectification-activity-receipt.test.ts。该合同仍扫描state: settled.spoken ? "streaming" : "thinking"。 - 修复:合同改为锁定
state: raw.trim() ? "streaming" : "thinking"和text: raw,并禁止再出现settled.spoken。流式时仍保留正在组织回答…,不得把activeActivity清掉。 - 验证:
frontend/tests/rectification-activity-receipt.test.ts。 - 防复发:改生时纠正直播
answer.delta渲染时必须跑rectification-activity-receipt,不能只跑 stream/entry 聚焦套件。 - 相关记录:BUG-369、BUG-370、BUG-377
- 复发自:BUG-377(直播回复改成模型正文后,activity 源码合同未同步)
- 修复版本:4a400bff103ce0d1d966a8dad8596395933c6927
BUG-377 | 生时纠正直播回复必须是模型正文,不得用正则或 Case 模板顶替
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
runV9AgentTurn、纠正组thinking、公开answer.delta、生时纠正聊天气泡 - 用户现象:关 thinking 后模型把规划写进
text-delta。漏检网按句式切口语,换一种说法就再次把规划当回复。用服务器按 Case 拼「已经记下」会丢掉模型自己的访谈措辞。 - 触发条件:纠正组
thinking: disabled;公开流不发thinking.delta。工具完成后的最终text-delta混有规划和对用户说话,或直播路径用正则 / Case 模板决定回复。 - 根因:规划没有稳定表面形式,词表无法从同一段散文里拆出口语。关掉 provider thinking 后模型没有隐藏思维链通道,只能写进正文。用服务器叙述顶替正文等于不用大模型写对用户说的话。
- 修复:直播用户可见回复是模型终端
text-delta原样。打开 hidden thinking(reasoning-delta),正文预算与思维链预算分开,规划离开正文。公开流仍丢弃thinking.delta。纠正 UI 不渲染thinkingText,阶段仍来自工具进度。服务器叙述只在工具后没有正文时兜底。正则只用于旧 Turn 水合,不得再当直播分流。不改 Skill10.0.11。 - 验证:
frontend/tests/rectification-v9-stream.test.ts、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/rectification-spoken-answer.test.ts。 - 防复发:禁止在
runV9AgentTurn里用splitRectificationSpokenAndThinking决定answer.delta。禁止用 Case 模板替换已有模型正文。禁止把thinking.delta发给纠正公开流或写成用户可见思考块。 - 相关记录:BUG-357、BUG-368、BUG-373、BUG-374、BUG-376
- 复发自:BUG-376(继续加正则切规划;随后一度误用服务器叙述顶替模型回复)
- 修复版本:5e34dd69cefc10520219326d499d1733b9b8b3a3
BUG-376 | 生时纠正再次把中文规划写进回复:账本/草稿/探针/我应该
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
POST /api/rectification/agent、splitRectificationSpokenAndThinking、生时纠正聊天气泡 - 用户现象:工具阶段(读取校正记录、整理证据、比较候选)之后,回复气泡里先出现大段自我规划:账本几件已确认、草稿待确认、当前探针、current_question、我应该继续访谈,最后才是对用户说的话。有一轮整段都是规划,没有口语。
- 触发条件:纠正组
thinking: disabled;公开流不发thinking.delta。模型把中文规划写进最终text-delta。句式不在 BUG-374 漏检网里。 - 根因:漏检网只认上一轮字段名(occupation_note / candidate_contrast_packet / 这意味着)。新规划改用「账本 / 草稿 / 探针 / 用户后面 / 我应该 / 根据规则 / current_question」。整段匹配失败后被当成口语并落盘。界面还把拆出的 thinkingText 渲成灰色思考块。
- 修复:纠正聊天不再渲染思考正文,只保留阶段进度和口语。漏检网补上账本/草稿/探针等句式,仅用于旧 Turn 水合,不得当直播分流。直播根因由 BUG-377 处理:打开 hidden thinking,用户可见回复用模型
text-delta。不改 Skill10.0.11。 - 验证:
frontend/tests/rectification-spoken-answer.test.ts、frontend/tests/rectification-v9-stream.test.ts、frontend/tests/rectification-agentic-entry.test.ts。 - 防复发:纠正 UI 不得把
thinkingText当作用户可见输出。直播路径不得再靠加词表从正文切思考。规划必须走reasoning-delta。 - 相关记录:BUG-357、BUG-368、BUG-373、BUG-374、BUG-377
- 复发自:BUG-374(关 thinking 后用正则从正文切思考;句式换了就漏)
- 修复版本:5e34dd69cefc10520219326d499d1733b9b8b3a3
BUG-375 | 生时纠正分不开时 A/B/C/D 出不来,职业回答不计分,「没有了」不停问
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
persistServerOwnedFocus、stampChoiceSchemaWithProbe、GET/api/rectification/cases/[caseId]choice_card、candidate-contrast-packet、event_probes.py、answersFromEvidence、latestUserStoppedCollecting - 用户现象:覆盖已齐、候选仍并列且
selection_allowed已开,点选卡一直是空。Agent 反复用自然语言问已答过的职责倾向。用户说「没有了」后仍继续问,没有出示并列区间。 - 触发条件:剩余候选已落在同一段 D10;窗口扫描真正切开的是 D24。引擎探针只剩已答的教育质量题。职业回答写成
occupation_note,不计分。 - 根因:(1)
stampChoiceSchemaWithProbe用引擎最高增益探针盖 schema,把对比探针换成已答教育题;event_probe缺probe_id时被当成已答,不建焦点。GET 投影不传contrastPacket/userStopped。(2) 对比探针按整窗 D10 星座建题,supportsCandidateIds是星座名不是分钟;askedKeys不含自然语言答过的职责倾向。(3) 质量探针先占领域,挡住 dasha 年界;代表对取整窗第一次换升。answersFromEvidence把同年入学当成考试失常。(4) 停问词匹配不到「没有了」。 - 修复:对比探针用自己的
semantic_key盖戳,缺引擎probe_id时仍建焦点;GET 与工具侧同一套 plan 输入。剩余候选按 D24/D10 分钟切开;职责倾向记入askedKeys。质量探针不得挡住 dasha;代表对取当前候选集。入学不再自动回答质量探针。停问词加上「没有了」等,停问且可出牌时出并列区间。不把occupation_note改成主评分事件,不打开confirmation_allowed,不改 Skill10.0.11。 - 验证:
rectification-spoken-answer、rectification-server-focus、rectification-choice-card、rectification-decide-next-action、rectification-eight-method、rectification-inference-machine、tests/test_rectification_event_probes.py。 - 防复发:覆盖已齐且候选并列时必须落 A/B/C/D;对比探针的
supports/conflicts必须是剩余候选分钟。点选必须改后验。停问且可出牌时走offer_provisional_range,不得再问已答职责题。2026-09-16 复发(BUG-912):无探针的分盘风格题借最高增益探针。2026-09-17(BUG-915):给风格题自建探针只解决了盖戳,自建探针没有像月宿边界探针那样在每个读 state 的地方重建,答题 / GET / idle 三处读持久化 receipt 都找不到它;产品同日拍板风格题不再作为计分判别题。 - 相关记录:BUG-348、BUG-350、BUG-351、BUG-366、BUG-373、BUG-374、BUG-912、BUG-915
- 复发自:BUG-348 / BUG-366(区分卡与覆盖≠收敛已写过,焦点持久化和剩余候选切开未接到这条会话)
- 修复版本:d7afe5b50de488d98a2ca666763784158e7c3be2
BUG-374 | 生时纠正规划句再次漏进正文:candidate_contrast_packet / 这意味着
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
POST /api/rectification/agent、splitRectificationSpokenAndThinking、已落盘 Turn - 用户现象:方法覆盖已齐后,回复气泡先出现「这意味着:方法资料已齐」「服务器给了 candidate_contrast_packet」「第 7 条边界 / 不得 offer」等规划句,然后才是对用户的追问。
- 触发条件:纠正组
thinking: disabled;模型把内部规划写进text-delta。句式不在 BUG-373 漏检网里;\bcandidate_contrast\b匹配不到candidate_contrast_packet。 - 根因:漏检网只覆盖了上一轮已见的方法覆盖 / occupation_note / 本轮对照了。新规划句复制了
candidate_contrast_packet、choice_frame和「这意味着 / 服务器给了 / 不得 offer / 第 N 条边界」。 - 修复:漏检网补上这些句式与
candidate_contrast(?:_packet)?。混有规划和对用户提问时只留口语。不重开 provider thinking,不改已哈希 Skill10.0.11。 - 验证:
frontend/tests/rectification-spoken-answer.test.ts锁定规划段进思考、口语追问保留;candidate_contrast_packet不得出现在 spoken。 - 防复发:thinking 关闭时漏检网必须覆盖服务器字段名和新的中文规划句,不能只认上一轮的
occupation_note/本轮对照了。对用户说话的追问不得被一起丢掉。 - 相关记录:BUG-357、BUG-368、BUG-373
- 复发自:BUG-373(关 thinking 后用正则从正文切思考;句式换了就漏)
- 修复版本:d7afe5b50de488d98a2ca666763784158e7c3be2
BUG-373 | 生时纠正把中文过程自述当成回答,工具完成标签和 occupation_note 漏进正文
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
POST /api/rectification/agent、splitRectificationSpokenAndThinking、已落盘 Turn 水合 - 用户现象:工具进度(读取校正记录、整理证据、比较候选)之后,回复气泡里先出现大段自我规划:方法覆盖、occupation_note、open_question、还不能出牌、本轮对照了哪些分盘,最后才是对用户的一句追问。看起来像思考过程被当成推理结果。
- 触发条件:provider thinking 关闭;模型在工具完成后的最终
text-delta里先写中文过程自述,再写对用户的问题。 - 根因:BUG-368 维持
thinking: disabled。漏检网只认旧的datePrecision/用户提到/让我调用 batch句式。新的规划句复制了公开工具完成标签和服务器字段名,整段被当成口语并落盘。末尾「本轮对照了…」与界面已有的技法句重复。 - 修复:漏检网补上工具完成标签回声、occupation_note / method_followup_plan / open_question / not_separated 等内部字段,以及方法覆盖、不可分宽度、让我继续访谈等规划句。混有过程句和对用户提问时只保留口语。不改已哈希 Skill
10.0.11。 - 验证:
frontend/tests/rectification-spoken-answer.test.ts锁定规划段进思考、入职追问留在口语;frontend/tests/rectification-v9-stream.test.ts锁定最终text-delta只把口语发成answer.delta。 - 防复发:禁止把工具完成标签、内部字段名或「本轮对照了」写进
answer.delta。thinking 关闭时漏检网必须覆盖中文规划句,不能只认英文 / batch / datePrecision。对用户说话的追问不得被一起丢掉。 - 相关记录:BUG-357、BUG-359、BUG-368、BUG-360
- 复发自:BUG-357(关 thinking 后用正则从正文切思考;句式换了就漏)
- 修复版本:32f7d774b64466bd900089f8535864ab35386339
BUG-372 | 生时纠正同一轮重复工具调用把已成功写入打成失败
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:
POST /api/rectification/agent、runV9AgentTurn观察器、公开 NDJSON - 用户现象:同一轮已读盘、写入证据、跑完诊断和候选比较后,流以
run.failed/repeated_tool_call结束,文案「本轮没有完成,状态已记录。」,没有助手叙述。公开事件里每个工具只出现一次 started/completed。 - 触发条件:
action=message的自由文本经历回合;模型在rectification-compare-candidates(或同类只含 caseId 的公开工具)完成后,又发出一次相同工具名与相同参数的tool-call。 - 根因:BUG-368 P0-3 把「相同工具参数」做成观察器硬上限 1,第二次相同
toolName + args在发布tool.activitystarted 之前抛错。该码不在自动重试集合,recoverable=false。证据与比较已经落库,只是本轮被标失败且跳过 dossier 叙述器。rectification-set-focus已从 Agent 工具列表移除,这条防线不再对应真实循环。 - 修复:相同公开工具调用视为幂等,跳过重复的 started/phase 收据,不 abort、不
attempt.reset。真循环仍由maxSteps与超时约束。无模型正文时走既有服务器叙述。不改已哈希 Skill10.0.11。 - 验证:
frontend/tests/rectification-v9-agent.test.ts锁定重复 read-case 完成叙述且无run.failed;frontend/tests/rectification-v9-stream.test.ts锁定重复 set-focus 继续出回答、诊断后重复 compare 走叙述且无attempt.reset。 - 防复发:禁止对第二次相同公开
tool-call抛repeated_tool_call或失败整轮。禁止把幂等工具调用放进自动重试。禁止用观察器 abort 代替maxSteps/ timeout。 - 相关记录:BUG-368、BUG-367
- 复发自:BUG-368(去掉模型驱动 set-focus 后,仍用 abort-on-second-identical-call 防循环,误杀正常比较重发)
- 修复版本:01ac4b9b561229998d106d6f4fca3220c467fb52
BUG-371 | staging publish 的 next build 找不到 createServerSupabaseClient
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:Gitea
backend-quality-gate.ymlpublish、deploy/railway-web.Dockerfile的RUN npm run build、POST /api/rectification/agent、staging 镜像发布 - 用户现象:向
staging推送c41c0e17后 run2058validate 通过,publish 在 web 镜像next build失败。没有 dispatchDeploy staging。 - 触发条件:staging push 跳过 validate 里的
npm run build;publish 才在 Docker 里跑 TypeScript。 - 根因:BUG-368 改 agent 路由时丢掉
createServerSupabaseClient的 import,调用仍在。源码扫描测试不跑tsc,所以 2006 项测试和 lint 都绿。 - 修复:补回
@/lib/supabase/server导入。计费合同与入口合同断言该 import 与await createServerSupabaseClient()同时存在。 - 验证:本机
npx tsc --noEmit退出 0;application-billing-contract与rectification-agentic-entry共 45/45。 - 防复发:改
frontend/src/app/api/**/route.ts后必须跑npx tsc --noEmit或等价的next build,不能只跑字符串合同。staging push 的类型回归仍只在 publish Docker 暴露。 - 相关记录:BUG-309、BUG-355、BUG-365、BUG-368、BUG-370
- 复发自:BUG-365 / BUG-309(validate 跳过
next build,publish 才暴露类型错误);BUG-368 丢掉 import - 修复版本:3519cf253ea0240654479e37598e74bad8ceabd0
BUG-370 | staging 质量门测试全绿后被 prefer-const ESLint 挡住
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:Gitea
backend-quality-gate.ymlvalidate 的npm run lint --prefix frontend、frontend/tests/rectification-inference-machine.test.ts、staging 发布 - 用户现象:向
staging推送76743683后 quality gate run2057失败。前端测试 2006/2006 通过,随后 lint 以 exit 1 结束。publish未构建镜像。 - 触发条件:
npm run lint --prefix frontend。推断 ledger 测试夹具用let engineReceipt且从未再赋值。 - 根因:BUG-367 引入的 ledger helper 写成
let,prefer-const报 error。BUG-369 只修了测试合同,没有跑 lint。既有 16 条 warning 不阻断;这一条 error 会。 - 修复:改为
const engineReceipt。不改生产代码,不放宽 ESLint。 - 验证:本机对该文件
eslint0 error;rectification-inference-machine11/11;全量npx eslint0 error / 16 既有 warning、exit 0。 - 防复发:生时纠正合同改动在推 staging 前必须跑
npm run lint --prefix frontend,不能只跑聚焦测试。 - 相关记录:BUG-339、BUG-367、BUG-369
- 复发自:BUG-339(质量门在测试之后跑 lint;本次是
prefer-const而不是 effect setState) - 修复版本:d2d7542df1730efdb240b4902e7eb5aeb7a827c0
BUG-369 | staging 质量门 2003/2006:计费合同、思考轨迹与业务表清单未跟上 367/368
- 状态:resolved
- 首次发现:2026-08-25
- 最近更新:2026-08-25
- 影响面:Gitea
backend-quality-gate.ymlvalidatenpm test --prefix frontend、application-billing-contract.test.ts、chat-stream-layout.test.ts、database-local-business.test.ts、staging 发布 - 用户现象:向
staging推送1082338c后 quality gate run2056失败。publish因needs: validate未构建镜像。公网仍为上一成功 SHA。 - 触发条件:完整
xiaoxinrunner 跑前端全量测试;Docker PostgreSQL fixture 应用全部frontend/supabase/migrations。 - 根因:三处源码/schema 合同未与 BUG-367/368 同步。(1) 计费合同仍要求
action只有opening|message|read_only,忽略已加入且不计费的answer_choice/stop_and_review。(2) 布局合同仍要求生时纠正聊天把thinking.delta写入appendActivityTraceThinking,与禁止公开思考流冲突。(3) self-hosted 精确public表清单仍缺agentic_rectification_choice_actions与agentic_rectification_inference_transitions。本机先前只跑了聚焦套件,未跑这三项。 - 修复:计费合同改为完整 action enum,仍要求只有
message走 reserve/complete/release。布局合同改为禁止appendActivityTraceThinking和thinking.delta,保留共享 Agent action bar 与工具步骤轨迹。全迁移测试补 applied ledger(含 turn origin)和两张新表,并断言 origin 列与record_agentic_rectification_turn_origin仅 service_role 可执行。不把业务 SQL 拷进frontend/db/migrations。 - 验证:本机
application-billing-contract与chat-stream-layout共 12/12;database-local-business在 Docker PostgreSQL fixture 上 1/1。 - 防复发:新增业务表必须同步
database-local-business的 applied ledger 和精确表集合。生时纠正路由 enum 变更必须更新计费合同。禁止再把公开thinking.delta写回纠正聊天合同。 - 相关记录:BUG-127、BUG-144、BUG-367、BUG-368
- 复发自:BUG-127(新业务表未更新精确表清单);BUG-367/368 的合同测试未覆盖计费 enum 与思考轨迹扫描
- 修复版本:2325b30227136323035f377dac13957c49d8868b
BUG-368 | 工具步骤英文规划被当成回答,set-focus 失败又整轮重放证据
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-24
- 最近更新:2026-08-24
- 影响面:
POST /api/rectification/agent、MastrafullStream步骤边界、rectification-set-focus、Evidence Batch、用户消息 origin、推荐问题 - 用户现象:用户说「2020年4月开始实习、6月转正、10月离职」后,界面先刷出残缺英文(
Let me、_probe、_gain)。rectification-set-focus连续失败后整轮重跑,已成功的证据再提交一次,只落地实习、另外两条被引文拒。随后聊天里出现一条用户从未输入的「2002 年发生什么了」。 - 触发条件:自由文本经历回合;Agent 在调用工具前输出规划文本;
set-focus因重复探针或零信息增益被拒;失败运行的推荐问题被写进历史。 - 根因:三组独立回归。(1) 禁止
thinking.delta后,把每个 step 的text-delta都公开成answer.delta,工具前规划变成用户正文;再对碎片做英文过滤,句子被剪成残片。(2)compare-candidates已算出下一问,仍让模型自己调set-focus;确定性校验失败后attempt.reset重放已成功的 Evidence 写入。(3) 未完成运行的 suggestion / 角色映射把「2002 年发生什么了」写成 user 消息。2002 不是引擎从 2020 算出来的。 - 修复:按 step 缓冲,只发布无工具且
stop/length的终端文本;reasoning-delta不下发。Agent 工具列表去掉set-focus,由 compare/read-case 在服务端持久化open_question。duplicate_focus等域错误不整轮重试。同一流里相同公开工具参数视为幂等,不得 abort 整轮;循环由maxSteps/ timeout 约束。用户消息记录origin/clientActionId/content_hash。Evidence quote 用源消息 offset,按条返回 created/already_exists/quote_mismatch。推荐问题只在run.completed后解析,纠正 UI 仍无 suggestion chip。不改已哈希 Skill10.0.11。 - 验证:
frontend/tests/rectification-step-answer.test.ts、frontend/tests/rectification-v9-stream.test.ts、frontend/tests/rectification-server-focus.test.ts、frontend/tests/rectification-evidence-quote.test.ts、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/rectification-v10-tool-contract.test.ts。 - 防复发:禁止把含工具调用的 step 的
text-delta发给浏览器。禁止把thinking.delta改名为answer.delta。禁止模型驱动set-focus。禁止对duplicate_focus/quote_mismatch/zero_information_gain做attempt.reset。禁止对第二次相同公开工具调用 abort 整轮(见 BUG-372)。禁止未完成运行解析或自动提交 suggestion。禁止模型改写 Evidence quote。 - 相关记录:BUG-345、BUG-354、BUG-357、BUG-367、BUG-372
- 复发自:BUG-367(关掉公开
thinking.delta后,中间 step 的text-delta被整段当成回答) - 修复版本:fe87a9ecdb71eae4eb79664664cc20c534177c19
BUG-367 | 点选 A 被当成聊天,部分写入后长推理失败并抢走滚动
- 状态:resolved
- 首次发现:2026-08-24
- 最近更新:2026-08-24
- 影响面:
POST /api/rectification/agent、选择题卡、applyRectificationChoice、推断 ledger、公开流、生时纠正对话滚动 - 用户现象:点选 A 后界面把「A. 是,大概就在那段时间」当普通聊天发给 Agent。模型重读整份 Case、思考上万字,随后「本轮处理未完成」。再点一次又看到证据已存在但问题仍开放。流式输出期间无法往上滑。
- 触发条件:当前有 A/B/C/D 问题卡;点击选项或「先这样」;流式过程中向上滚动。
- 根因:四点叠加。(1) 没有
answer_choice协议,点选被转成action: "message"。(2) 证据/后验写入与关闭当前问题不在同一事务,自然语言失败被当成整轮失败。(3) 公开流发送thinking.delta,持续触发强制scrollTo。(4) 用户原话引用可能写成问题里的年份。 - 修复:点选与停止走
answer_choice/stop_and_review,服务端按questionId + optionId + actionId原子写入 receipt、后验、关闭 focus。同一actionId幂等回放。公开流只发白名单,含粗粒度activity.changed。滚动改为近底部才跟随,并提供「回到最新」。read-case默认turn_decision。失败返回具体finishReason文案,不把「本轮处理未完成」写入聊天。不改已哈希 Skill10.0.11。 - 验证:
frontend/tests/rectification-answer-choice.test.ts、frontend/tests/rectification-inference-machine.test.ts、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/rectification-v9-stream.test.ts。 - 防复发:禁止把点选全文当
message交给语言模型。禁止thinking.delta进入生时纠正公开流。禁止在用户离开底部后强制scrollTo/scrollIntoView。禁止用助手题干年份充当用户 quote。选择题写入必须带unique(case_id, action_id)并在同一事务关闭当前问题。 - 相关记录:BUG-357、BUG-360、BUG-362、BUG-363
- 复发自:BUG-362(C/D 无证据后验已走确定性写入,A/B 点选仍被当成聊天)
- 修复版本:3659519bd00e05ffa88e83823ac56ad4d4a747ab
BUG-366 | 方法覆盖完成被当成候选收敛,职业笔记又让候选卡立刻失效
- 状态:resolved
- 首次发现:2026-08-24
- 最近更新:2026-08-24
- 影响面:
decideNextAction、conversationalSessionOutcome、isOfferBlockingFollowup、method_followup_plan、rectification-offer-candidates、候选 snapshot 指纹、生时纠正系统提示、验证报告 - 用户现象:用户一句话里同时给出长期职业和带日期创业事件后,状态终于会动,但直接跳到
adopt_candidate。Agent 按协议出牌。职业无日期occupation_note不参与评分却把方法覆盖标成完成;候选仍是 34/33/33。随后候选卡被前端判定失效,提示「出生资料已变化」。系统没有按 D9/D10 差异反问前事。 - 触发条件:经典方法层已齐或职业笔记刚补齐;引擎
propose_allowed已开;候选相对支持只差 1 分;Agent 在同一轮先写带日期事件再写occupation_note再offer-candidates。 - 根因:两层概念被焊在一起。(1)
methodCoverageAll && propose_allowed直接变成session_outcome=adopt_representative,覆盖完成被当成收敛;盘面差异只写进最终报告,不进入决策。(2) 无评分occupation_note推进工作流版本,候选 snapshot 仍按上一版证据读取,失效文案又把所有 409 翻译成出生资料变化。 - 修复:新增
decideNextAction。覆盖完成只进入ask_candidate_discriminator。34/33/33 为not_separated。D9/D10 差异生成CandidateContrastPacket。未通过 holdout 不得ready_to_adopt。候选卡只认出生资料 + 可评分证据 + 推断修订 + 候选集;occupation_note仍覆盖职业层,但不让 snapshot 过期。失效原因拆开。不改已哈希 Skill10.0.11。 - 验证:
frontend/tests/rectification-decide-next-action.test.ts锁定覆盖≠收敛、并列必须出区分探针、无评分职业笔记不改 scoreable fingerprint、holdout 未过不得 adopt、报告不得无calculationResultId写 executed;frontend/tests/rectification-eight-method.test.ts锁定职业笔记覆盖后不再直接 adopt、覆盖后剩余探针仍走区分。 - 防复发:禁止
methodCoverageAll直接变成adopt_representative。禁止把事件拟合率当成候选区分。禁止无评分证据让候选卡失效。禁止把所有 stale 提示写成「出生资料已变化」。不得改已哈希 Skill10.0.11。 - 相关记录:BUG-361、BUG-362、BUG-363
- 复发自:BUG-361(职业笔记覆盖和出牌门修了,但覆盖完成被当成可以 adopt)
- 修复版本:待发布
BUG-365 | staging web 镜像 next build 被推断类型检查挡住
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-24
- 最近更新:2026-08-24
- 影响面:
deploy/railway-web.Dockerfile的RUN npm run build、probeFromEngine、method-followup反推探针、rectification-v9-toolswindow scan - 用户现象:Linux
xiaoxin离线时在本机构建linux/amd64web 镜像,next build在 TypeScript 阶段失败,无法把909de8b8换上 staging。 - 触发条件:当前
staging头含推断 ledger,且走 Dockernext build。staging push 的 validate 跳过npm run build,因此测试可绿、镜像红。 - 根因:
EngineProbeFields把expected_outcomes.answer_class收成AnswerClass,与引擎探针的string相交后不能map(probeFromEngine)。反推探针用字面量比较dasha_boundary,在source收窄成age_band后被判无交集。windowScanFromDecisionReceipt可为null,调用方仍读.transitions。 - 修复:
probeFromEngine接受引擎探针并校验AnswerClass。反推探针改走Set<string>。window scan 用可选链,缺省空数组。 - 验证:本机
npx tsc --noEmit仅余测试夹具多余字段,已改为Object.assign;Dockernext build待用新 SHA 重跑。 - 防复发:staging push 的类型回归仍只在 publish Docker 暴露;本机/xiaoxin 镜像构建失败不得改 SHA 蒙混上线。
- 相关记录:BUG-355、BUG-363、BUG-364
- 复发自:BUG-355(validate 跳过
next build,publish 才暴露类型错误) - 修复版本:待发布
BUG-398 | 已持久化点选题因不是全局最高探针被误判 stale
- 状态:resolved(本地修复,待发布)
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
applyChoiceWithoutEvidence、生时纠正 A/B/C/D 点选、推断 revision ledger - 用户现象:页面刚读取到的当前点选题提交后返回
stale_probe,即使focusId、probeId与 revision 都没有变化。 - 触发条件:数据库已持久化的 active focus 不是当前推断状态按信息增益重新计算出的第一名探针。
- 根因:点选入口已经校验 active focus,但
applyChoiceWithoutEvidence又用nextProbe(state)建立第二套“当前题”真源,把合法的低信息增益已打开题判成过期;探针 schema 的多个 identity 字段又是逐个命中,互相矛盾时仍可能落到其中一个探针。 - 修复:移除对重新计算全局最高探针的限制;只要显式
probe_id、semantic_key、candidate_split_hash同时匹配同一个 state probe 就允许应用。显式 identity 矛盾或找不到时仍返回stale_probe;无显式 identity 才保留原 domain/highest-gain fallback。ledger 的 open probe 与 revision 校验不变。 - 验证:
npm_config_cache=/tmp/jyotisha-npm-cache npx --yes tsx --test tests/rectification-inference-machine.test.ts,14/14 通过。 - 防复发:已持久化 focus 可以回答低于全局最高信息增益的探针;互相矛盾的 schema identity 必须 fail closed。
- 相关记录:BUG-367
- 复发自:无
- 修复版本:待发布
BUG-399 | 感情区分探针可落在未成年年份
- 状态:resolved(本地修复,待发布)
- 首次发现:2026-08-26
- 最近更新:2026-08-28
- 影响面:
scripts/rectification/event_probes.py、relationshipdasha_boundary探针 - 用户现象:感情卡可能询问出生后很早、明显不符合“认真关系进入或结束”语义的年份。
- 触发条件:候选在未成年年份附近存在高区分度 Vimshottari 或 Narayana 边界,且该年份没有已记录事件挡住。
- 根因:boundary 候选只使用全局
birth_year + 5下限,没有应用 relationship catalog 的年龄下限。 - 修复:relationship 的 dasha boundary 从
birth_year + age_lo开始;不使用age_hi作硬上限,避免排除成年后的真实关系事件。其他允许童年事件的领域保持原范围。 - 验证:
/opt/anaconda3/bin/python3.12 -m pytest tests/test_rectification_event_probes.py -q,15/15 通过;回归锁定未成年 relationship 年份被排除且 family 童年边界行为不回退。 - 防复发:relationship boundary 不得早于 catalog
age_lo;不得把age_hi当事实有效期。 - 相关记录:BUG-379、BUG-417
- 复发自:无
- 修复版本:待发布
BUG-400 | 已打开点选题时 Agent 只确认事实,没有问出题干
- 状态:resolved(本地提示契约补强,待发布)
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:生时纠正 Agent 系统提示、口语与 choice card prompt 投影
- 用户现象:工具已经持久化下一道点选题,但 Agent 正文只说已记下事实,没有自然语言追问;卡片只能显示服务器的“年份 · 事件家族”锁标签。
- 触发条件:工具返回
open_question/current_question后,模型只输出确认语,没有输出带问号且符合锁定年份/领域的问句。 - 根因:BUG-391/392 已保证服务器不代写问题并优先投影 Agent 问句,但提示只要求“自己写一句自然语言追问”,没有明确禁止该轮仅确认事实。
- 修复:补强现有同一条提示:有 open/current question 时,本轮正文必须包含对应自然语言追问,不能只回复“记下了”或只做事实确认。不新增服务器题干模板,不改变 A/B/C/D 评分语义。
- 验证:
./node_modules/.bin/tsx --test tests/rectification-v9-agent.test.ts tests/rectification-choice-card.test.ts,42/42 通过;静态契约锁定强制追问语句,既有 Agent 问句投影测试保持通过。 - 防复发:有 active open question 的轮次必须口语问出同一年份和事件家族;服务器锁标签仍只作为 fail-closed 显示,不得伪装成 Agent 问句。
- 相关记录:BUG-391、BUG-392
- 复发自:BUG-391
- 修复版本:待发布
BUG-401 | 零信息增益质量卡伪造 probe,点选永远 stale 且一条线索就打断采集
- 状态:resolved(本地修复,待发布)
- 首次发现:2026-08-26
- 最近更新:2026-08-26
- 影响面:
method_followup_plan、stampChoiceSchemaWithProbe、persistServerOwnedFocus、生时纠正 A/B/C/D 卡片 - 用户现象:只记入一条「2016 年 9 月上大学」后就出现高考发挥选择题;题干在 Agent 正文和卡片重复,点击任一选项返回
stale_probe。 - 触发条件:账本只有一条学业事件;引擎 receipt 生成
known_event_quality,但该条目information_gain=0、candidate_ids=[]、expected_outcomes=[],且没有进入inference_state.probes。 - 根因:BUG-389 让
known_event_quality在 3 条训练事件 / 2 个领域门槛之前插队;stampChoiceSchemaWithProbe在已有 inference state 却找不到匹配探针时仍合成probe_id。持久化 focus 因而引用不存在的探针,点选链路按正确的 fail-closed 校验返回stale_probe。卡片又可视化重复渲染 Agent 已问出的题干。 - 修复:删除采用门前的零增益质量卡分支;所有 scoring focus 统一要求正信息增益、真实候选映射和同一 inference probe,匹配失败不再合成 identity、也不持久化。读取旧案例时忽略遗留
clarify_eventfocus 并继续收集其他领域事件。卡片 legend 保留给无障碍读取,但视觉上只显示 A/B/C/D,题干由 Agent 正文展示。 - 验证:6 个定向套件 152/152;
npm run lint0 error(17 条既有 warning);next build --webpack完整通过。默认 Turbopack 在隔离 worktree 因node_modules指向工作区外的符号链接而报环境错误,非源码错误。 - 防复发:scoring choice schema 在已有 inference state 时必须绑定真实 probe;零增益或没有候选映射的条目不得成为点选题;一条事件后继续跨领域收集,题干不得在正文与卡片重复。
- 相关记录:BUG-389、BUG-390、BUG-398、BUG-400
- 复发自:BUG-389
- 修复版本:待发布
BUG-402 | 生时纠正点选成功后不续问,确认文案永久停在“正在准备下一步”
- 状态:resolved(代码修复完成,staging 发布与验收待确认)
- 首次发现:2026-08-26
- 最近更新:2026-08-27
- 影响面:
POST /api/rectification/agent的answer_choice、生时纠正聊天续跑、区分题自然语言、Skill 包注册 - 用户现象:点击 A/B/C/D 后服务端已记录选择并更新候选,但页面只显示“正在准备下一步”,不再出现下一问;职业题干容易直接复述“入职、升职或职责明显加重”等固定标签。
- 触发条件:结构化选择成功后决策器返回
ask_fact_collection、ask_candidate_discriminator或ask_holdout_validation;或 Agent 根据event_probes.py的固定事件家族 brief 写点选题。 - 根因:前端只消费
narration,忽略成功响应里的nextAction,且把临时 loading 句持久化成最终 assistant turn。探针 brief 又要求围绕固定事件家族写一句是/否题,模型容易轻度改写后原样输出。 - 修复:选择仍保持独立、幂等、非模型事务;仅当
nextAction需要继续提问时复用现有read_onlyAgent turn,其他动作只刷新 Case 快照。删除持久化 narration 中的“正在准备下一步”。探针 brief 改为锁定时间范围、领域和语义目标,要求结合最近对话只选一个口语入口,不逐字复述或堆叠标签示例。发布 immutable Skill10.0.12,10.0.11保留为 deprecated 供旧 Case 精确解析。 - 验证:
frontend/tests/rectification-answer-choice.test.ts、frontend/tests/rectification-agentic-entry.test.ts、frontend/tests/skill-registry.test.ts、相关 V9 Skill 合同测试、tests/test_rectification_event_probes.py。 - 防复发:结构化命令成功响应新增或变更决策字段时,客户端必须有消费合同;不得把 loading 状态写成持久化回答。服务器探针可锁定语义,不得把内部分类标签当成最终题干模板。
- 相关记录:BUG-391、BUG-392、BUG-394、BUG-398、BUG-401
- 修复版本:10.0.12
BUG-403 | 生时纠正候选后验、展示排名和停止语义不一致
- 状态:resolved(本地修复,未提交)
- 首次发现:2026-08-27
- 最近更新:2026-08-27
- 影响面:生时纠正停止意图、方法追问、候选区分探针、结构化选择评分、候选结果投影、Profile freshness、候选采用 RPC、采用后反向核验续跑、动态选项、唯一分钟确认门、VedAstro 缓存验证
- 用户现象:用户在当前领域回答「没有了」后可能误结束整个 Case 并提前展示候选;已有感情起止证据仍重复询问是否谈过恋爱;既有教育证据误抑制尚未问过的 D24 高信息量区分机会;一次结构化选择几乎淘汰全部候选;候选卡排名、
representative_time、credible_range与 inference 后验互相矛盾;首次采用候选返回candidate_profile_changed;采用后虽已有verify_adopted_time计划但前端没有发起下一轮;选择题依赖静态模板兜底;确认链路又被入口硬编码的false永久关闭,并显示机械式代表时间收口提示。 - 触发条件:局部否定被当作全局停止;方法覆盖只按领域而不看已确认事件语义;候选对比把 Evidence 内容推断成已问 probe;单轮冲突使用硬淘汰;持久化候选与 inference 各自投影;Profile freshness 把展示地点和历史
active_birth_time当作本轮计算输入,却漏掉声明时间窗;候选采用只刷新快照,没有复用结构化选择的read_only续跑;动态选项缺失时回退固定职业、感情、学业或考试文案;Agentic 决策入口和 VedAstro 投影无条件写死不允许精确分钟确认。 - 根因:停止语义、语义去重、asked-probe 账本、评分更新、公开候选投影、Profile freshness、外部验证和确认门分别维护了不兼容规则。尤其 Evidence 只能证明事件存在,不能证明某个候选分组问题已经问过;展示地点和历史
active_birth_time也不是本轮计算输入变化;临时外部验证失败不应永久污染同一候选结果。 - 修复:停止整个校正不再靠词表正则猜测,新增 Agent 语义工具
rectification-stop-and-review,由服务端持久化paused;「没有 / 不记得 / 这方面没有」由 Agent 针对当前 active focus 调用rectification-resolve-focus,不会直接终止 Case,后续普通消息可恢复暂停 Case。选择题只在引擎返回完整、合法且恰好覆盖yes / weak_yes / no / unsure的动态style_options时显示;缺项、重复、空文案或非法事件家族直接不出卡,不再回退任何职业、考试、感情或学业静态模板。已确认感情证据抑制泛化 D9 复问,但不抑制真实候选区分 probe;structured/varga probe 只按 inference receipt 的已答 key 去重,普通事件 probe 按 live evidence 的domain.year去重。结构化答案改为累计软评分,累计 3 次唯一 strong conflict 才淘汰,并保证至少一个候选存活。UI、API、Mastra 和报告统一从 active inference 候选投影排名、代表时间和可信区间;候选映射、cluster range、代表时间或 receipt range 任一不一致时隐藏候选并禁止采用。统一 fingerprint 忽略展示地点与历史active_birth_time,纳入出生日期、原始时间、时间来源/时段、声明时间窗、不确定范围、经纬度和时区等真实计算输入。采用期间复用现有pending/busy锁定输入,采用后由既有 continuation effect 发起read_only,进入verify_adopted_time反向前事核验。删除 Agentic 入口的confirmationAllowed: false和 VedAstro 投影的canConfirmExactMinute: false,统一走buildConfirmationGate();真实证据门仍 fail closed,不把代表分钟伪装成唯一分钟。VedAstro 失败只持久化安全失败码;相同 evidence/range 缓存再次比较时可重试验证,并通过独立 service-role RPC 仅刷新当前 Result 的验证 receipt,不重算或改写候选、排名、inference 和 selection。删除「本轮校正已收口」「本会话以代表性时间收口」及采用后的机械式代表时间提示;只有真实唯一分钟确认后才显示确认时间。发布 immutable Skill10.0.13,10.0.12保留为 deprecated。 - 验证:新增/更新回归覆盖 Agent 语义暂停与恢复、局部否定 focus 关闭、动态选项 fail closed、感情泛化复问、D24 structured probe 不被普通教育 Evidence 屏蔽、累计冲突软淘汰、候选投影不变量、latest inference transition 权威采用、候选 schema 缺失时 fail closed、采用/普通消息互斥、Profile freshness、确认门真实输入、VedAstro 安全失败码及缓存重试、验证刷新 RPC 的归属/指纹/Profile freshness/最小更新权限;最终本地类型检查与聚焦套件结果见本次变更验收记录。
- 防复发:停止整个流程必须由显式语义工具落库,不得增加停止词正则;动态选项不完整时宁可不出卡,不得恢复任何静态题干或 A/B/C/D 文案;Evidence 语义覆盖不得替代 structured probe receipt;单个启发式选择不得一票淘汰候选;公开候选卡、代表时间、可信区间与采用入口必须共享 active inference 投影;外部验证临时故障必须可重试且不得改写候选状态;入口不得硬编码绕过真实确认门,也不得删除 holdout、相邻分钟、外部验证和用户同意等证据门;代表性采用和唯一分钟确认必须使用不同状态与文案。
- 相关记录:BUG-366、BUG-367、BUG-398、BUG-401、BUG-402
- 复发自:BUG-366(覆盖完成被当成收敛)、BUG-367(结构化选择旁路)、BUG-398(probe freshness)、BUG-401(伪 probe 与过早出卡)、BUG-402(点选后续跑)
- 修复版本:10.0.13(未提交)
BUG-404 | 区分题未落 active Focus 仍被说出,自由文本否定误入完整 Agent
- 状态:resolved(修复已验证,待 staging 发布)
- 首次发现:2026-08-27
- 最近更新:2026-08-27
- 影响面:
POST /api/rectification/agent、active Focus、动态选择题、自由文本回答、候选后验更新 - 用户现象:Agent 正文询问候选区分题,但页面没有选择卡;用户随后输入「那段时间没有变化」一类自然语言回答时,系统没有关闭当前问题,而是启动完整 Agent/候选比较长链,最终超时并显示校正暂时不可用。
- 触发条件:服务端没有成功持久化对应 active Focus,或 active Focus 已存在但用户没有点击卡片、改用自由文本回答。
- 根因:正文问题与 durable Focus 曾是两套真源;自由文本消息没有 active Focus 的轻量结构化意图入口;旧路径还能按 A/B/C/D 位置、focus status 或选项引文猜答案语义。结果是未落库的问题仍可见,而自然语言局部否定无法复用结构化点选 resolver。
- 修复:只有
created/already_open且 Focus UUID、questionId、semantic_key、candidate_split_hash和完整动态选项 schema 一致时才暴露区分题;持久化失败、重复冲突或 schema 非法时同时隐藏卡片和 Agent 可见 follow-up。active Focus 下的自由文本先交给轻量模型输出结构化 intent 与answer_class,模型只分类不改状态;当前问题答案通过动态选项自带的answer_class反查 option key,并与点击卡片统一调用applyRectificationChoice()。分类不清或模型异常时保留 Focus 并提示点选,不再落回完整 Agent。删除用户语义正则、选项引文解析及 status/位置到答案的推断;动态 schema 不完整时 fail closed,不恢复静态题干或选项 fallback。 - 验证:相关回归
144/144通过;tsc --noEmit通过;完整 ESLint 无 error(仅保留仓库既有 warning);完整前端套件遍历发现的旧静态选项/旧提示词断言已改为动态 schema 契约并逐项通过。回归覆盖乱序 option key、自由文本分类、Focus 持久化不变量、卡片身份、点选与文本共用 resolver、非法 schema fail closed、无语义正则及 inference fixture 动态 schema。 - 防复发:候选区分题必须以持久化 active Focus 为唯一真源;模型分类结果不能直接写状态;所有答案必须经过动态
answer_class和同一确定性 resolver;不得增加语义词表、正则或 A/B/C/D 位置 fallback;分类失败时保持当前 Focus,不得启动完整 Agent 长链。 - 相关记录:BUG-367、BUG-398、BUG-400、BUG-401、BUG-402、BUG-403
- 复发自:BUG-367(结构化点选旁路)、BUG-400(正文与卡片题干分离)、BUG-403(局部否定仍依赖 Agent 工具)
- 修复版本:10.0.13(待 staging 发布)
- 后续复发:BUG-405
BUG-405 | 区分探针问题合同未统一,低信息量事件题压过可渲染高信息量分盘题
- 状态:resolved
- 首次发现:2026-08-27
- 最近更新:2026-08-28
- 影响面:生时纠正候选区分探针、点选题四选项合同、方法追问排序、active Focus 持久化、Agent 可见投影、
POST /api/rectification/agent - 用户现象:候选比较阶段出现无法点选的自由文本问题,或先问信息量很低的事业存在题,而更高信息量的 D24 等分盘区分机会被跳过;点选「没有」或自然语言回答无法更新后验,流程退回泛问经历。
- 触发条件:Python 区分探针只给出 yes/no 结局、不带完整
style_options;TypeScript 曾用测试夹具补四选项,生产路径却 fail-closed 不出卡;method-followup在冲突事件探针与 contrast 探针之间按 if/else 分支优先,而不是按信息量全局排序;ask_candidate_discriminator且没有合法 active Focus 时仍可能进入完整 Agent。 - 根因:问题合同、可渲染性、探针排序和 Focus 持久化是四套规则。BUG-403/BUG-404 要求动态四选项,但 Python 从未发出完整
style_options,存在题无法服务端补全;冲突事件探针因分支顺序可以压过更高information_gain的 contrast 探针;Agent 仍能看见原始candidate_contrast_packet/current_probe,并在没有卡片时把区分题说出来。 - 修复:新增跨运行时
probe-question-v1合同。存在题/质量题可由服务端补全为yes / weak_yes / no / unsure;分盘风格标签保持动态,并补都不是这些特质与这段记不清楚。只有isRenderableProbe通过的探针进入排序;冲突事件探针与 contrast 探针按information_gain × novelty × coverage - penalty全局取最高。先持久化 Focus 再暴露问题;compare-candidates 对 Agent 隐藏原始探针包。缺少合法 Focus 时先修复或收口到可信区间,不进入完整 Agent;流结束后若仍是 discriminator 且没有合法 Focus,记state_invariant_failed。Skill 版本保持10.0.13。 - 验证:
frontend/tests/rectification-probe-question-contract.test.ts、choice-card / server-focus / contrast-packet / eight-method 排序回归、turn-decision 隐藏current_probe、route 在runV9AgentTurn前修复 Focus、Pythontests/test_probe_question_contract.py与tests/test_rectification_event_probes.py四选项合同。Gitea Independent Staging Quality Gate3ae22f576842310e6e43de497bf76714b614c7ed通过后,Deploy staging run 2121 成功;https://staging.jyotisha.chat/api/health报告同一 SHA,rectificationMigrations=ok。 - 防复发:区分题必须同时满足四选项合同、可渲染、全局信息量排序、Focus 先于提问。不得恢复冲突探针优先于 contrast 的分支;不得把原始探针包交给 Agent;不得在
current_question=null时把 discriminator 当成成功口语轮次。 - 相关记录:BUG-367、BUG-398、BUG-400、BUG-401、BUG-403、BUG-404
- 复发自:BUG-403(动态四选项合同未贯穿 Python/排序)、BUG-404(Focus 与口语问题仍可分裂)
- 修复版本:10.0.13
- 后续复发:BUG-407
BUG-406 | 点选题干只在无障碍 legend 里,卡片上看不到
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
rectification-choice-card、GETchoice_card.prompt、生时纠正 Agent 口语 - 用户现象:区分阶段出现 A/B/C/D,气泡只说“请看下面的选项”,卡片上方没有年份和事件家族;用户看不到要核对的是哪一类经历。
- 触发条件:服务器已持久化
choice_card.prompt(年份 · 事件家族),Agent 按 P0 只写承接句、不复述题干。 - 根因:BUG-401 把卡片 legend 设为
sr-only,当时题干由 Agent 正文展示。BUG-405 改为正文只做承接、题干以 Focus/卡片为真源,但没有取消sr-only,题干两边都不出现。 - 修复:卡片 legend 恢复为可见题干,与 GET
choice_card.prompt同一份文案。不改 Skill10.0.13。 - 验证:
frontend/tests/rectification-agentic-entry.test.ts锁定可见<legend>{props.card.prompt}</legend>,禁止sr-only。 - 防复发:有点选卡时题干必须在卡片上可见。Agent 承接句不得替代卡片题干。不得再把
choice_card.prompt藏进sr-only。 - 相关记录:BUG-400、BUG-401、BUG-405
- 复发自:BUG-401(卡片隐藏题干)叠加 BUG-405(正文不再复述题干)
- 修复版本:待发布
- 后续复发:BUG-471
BUG-407 | 区分题目录被快照投影饿死,低分职业题压过已评分的高分 D24
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:生时纠正 GET 点选卡、Mastra 工具读路径、
POST /api/rectification/agent、contrast packet、method-followup - 用户现象:训练事件已经够、推理层已有高信息量 D24 探针,界面仍出低信息量事业存在题;题干修复后选题内容仍不对。
- 触发条件:快照
latest_result.candidates为空,或可信区间与推理活跃时刻不一致导致authoritativeCandidateProjectionfail-closed;Pythondiscriminating_event_probes仍给出低分事业题;已打开的事业区分卡把后续排序锁在原题上。 - 根因:BUG-405 把排序公式改成按信息量取最高,但读路径组包不读
inference_state.probes,remaining D24 又依赖快照时刻。时刻被投影饿死后目录只剩职业题。评分落库用score.candidates能把 D24 写进推理层,GET/工具随后丢掉。已打开的低分 distinguish Focus 还在 method-followup 里优先于重新排序。 - 修复:GET、Agent、工具共用一份
rectificationFollowupCatalog。合并未回答的推理探针与 Python 事件探针,按semantic_key去重留更高信息量。拼 remaining splits 用推理未淘汰时刻,不把 adopt 投影的空scores当成出题目录。已有日期证据的领域不再出存在题(例如已记事业就不再问另一年入职);D9/D10/D24 等分盘区分题仍进池,按信息量全局取最高,不绑定某个领域。varga.d24/d5按质量题、d9/d10按风格题补全。已打开但 semantic_key 不是当前目录赢家的区分卡让位。评分落库仍用当次score.candidates。Skill 版本保持10.0.13。 - 验证:
frontend/tests/rectification-decision-authority.test.ts空快照仍选出目录最高分;frontend/tests/rectification-eight-method.test.ts已覆盖领域的存在题让位,D10 与 D24 谁分高问谁。 - 防复发:出题目录必须来自推理探针加引擎探针,不得只吃 Python 事件探针或快照投影时刻。Adopt/展示投影 fail-closed 不得饿死出题。已打开的低分区分卡不得挡住更高分目录赢家。
- 相关记录:BUG-405、BUG-406
- 复发自:BUG-405(排序公式对,目录被投影饿死,已打开低分卡锁题)
- 修复版本:待发布
BUG-411 | 已持久化的 D24 区分卡在 GET 时被 active_focus 重建丢掉
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
buildMethodFollowupPlan已打开区分焦点、GETchoice_card、生时纠正点选卡 - 用户现象:训练事件已齐,助手记下感情经历后只说“对候选的区分有帮助”,不再追问,界面也没有点选卡。服务端其实已经创建了 D24 区分焦点。
- 触发条件:目录赢家是推理层
varga_contrast(不在 Pythondiscriminating_event_probes里),该焦点已持久化,随后 GET 刷新卡片。公开分数字段可因credible_range与全窗候选不一致而被清空。 - 根因:已打开的区分焦点只要还没被更高分探针替换,计划就改写成
source=active_focus。重建时只在 Python 事件探针里按targetDomain找 live probe。D24 只存在于对比包,education 域又没有 dasha 事件探针,于是semantic_key丢失、choice_frame对不上已持久化的questionId,GETchoice_card变成 null。Agent 被禁止在没有公开卡片时口述区分题,正文只剩确认句。 - 修复:打开的区分卡若已经是当前目录赢家,继续用该赢家的 ranked discriminator 出题,不要改写成 active_focus。
- 验证:
rectification-choice-card锁定「Python 事件探针没有教育行、D24 只在推理探针、焦点已持久化」时 GET 仍返回点选卡。 - 防复发:不得把已打开且仍是目录赢家的分盘区分题改写成没有
semantic_key的active_focus。GET 有合法持久化区分焦点时不得把choice_card置空。 - 相关记录:BUG-407、BUG-410
- 复发自:BUG-410(决策已进入区分并持久化焦点,GET 仍把卡片藏掉)
- 修复版本:待发布
BUG-412 | 反推点选卡丢掉已记下的年份月份,后两个选项收成一排小按钮
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
choice-cardperiod/prompt、GETchoice_card、rectification-choice-card点选样式 - 用户现象:区分卡出现后题干写成「当前这几个候选 · 学业…」,已记下的入学年份和月份精度出不来。A/B/C 是整行主按钮,D「这段记不清楚」和「先这样」挤在下一排、更小、居中、颜色变淡。
- 触发条件:目录赢家是没有
year的 D24varga_contrast,账本里已有同学业带日期经历;点选卡把unsure和停止键标成secondary。 - 根因:
periodFor只要找到探针就采用year_label,对比包探针年份为 0 时标签是占位「当前这几个候选」,不再回落到同学业已确认经历。GET 又优先用已持久化的占位题干。选项按unsure=secondary拆到special-choices,和停止键共用 flex 小按钮。 - 修复:探针没有具体年份时,题干锁定同学业已确认经历的「YYYY 年前后」或月份精度的「YYYY 年 M 月前后」。GET 用更具体的服务端题干覆盖占位题干。A/B/C/D 和停止键同一列主按钮。
- 验证:
rectification-choice-card锁定 D24 占位探针 + 2016 年学业经历 → 题干带「2016 年前后」;月份精度经历带出「M 月前后」;GET 仍把占位题干升级成年份锁。rectification-eight-method锁定高分 D24 的 period 是已记学业年。rectification-agentic-entry锁定点选卡不再拆special-choices。 - 防复发:不得在对比包年份为 0 时把「当前这几个候选」压过账本已确认年份。点选卡四个答案不得因为
unsure改成另一套小按钮。 - 相关记录:BUG-406、BUG-408、BUG-411
- 复发自:BUG-411(卡片能投影后,占位年份和 secondary 拆行才被看见)
- 修复版本:待发布
BUG-413 | 点选后卡片消失,只留下一条自动发出的选项气泡
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:生时纠正点选卡停留与选中态、
choice_click用户气泡、D24/发挥质量题干 - 用户现象:点 A/B/C/D 或「先这样」后卡片立刻从对话里消失,底下多出一条用户自己点出来的
A. 发挥明显失常…。同一张卡不能再看见选中项。题干写成「2016 年前后 · 学业、考试发挥或学习压力出现明显变化」,像目录锁标签。 - 触发条件:GET 已出示点选卡;用户点击任一选项。
showChoiceCards在busy时卸载卡片,并把choiceCardUserMessage追加成用户消息。 - 根因:点选被做成「发一条用户消息 + 等下一轮」,卡片只挂在最新未 busy 的助手气泡上。题干走
period · family锁标签;对比包发挥质量又不分领域,一律套学业考试目录句。 - 修复:点选后把卡片钉在出题的那条助手消息上,保留
data-selected,禁用再点;不再插入自动用户气泡,刷新时也藏掉A. …/ 「先这样」这类合成行。发挥质量题干改成年份锁定的口语问句,并按领域区分家族,不再用「当前这几个候选 · 学业…」目录锁。 - 验证:
rectification-choice-card锁定 D24 题干是「2016 年前后,有没有学业或考试发挥失常、压力特别大的时候?」;GET 把已持久化的目录锁升级成这句。rectification-agentic-entry锁定choiceAttachment停留、不再创建v9-choice-user-气泡。rectification-answer-choice锁定合成点选行不是聊天用户句。 - 防复发:点选卡不得因为
busy从已回答的助手气泡上卸掉。点选不得再追加A. 标签用户气泡。题干不得再把period · family目录锁直接展示给用户。 - 相关记录:BUG-405、BUG-406、BUG-412
- 复发自:BUG-412(卡片能看清后,点选消失和目录题干才被看见)
- 修复版本:待发布
BUG-414 | 无年份家人反推仍出点选卡
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:生时纠正点选卡
eventQuestionPrompt、buildChoiceFrame、rankRenderableDiscriminators、家人存在性家族、GET 题干覆盖 - 用户现象:D24 点选之后出现「那段时间,有没有家人相关的明显变化?」。没有具体年份,事件也不是一件能点是/否的家事。改成「有没有家人结婚、添丁或住院?」后仍缺时间,无法反推。
- 触发条件:家人域没有已记带日期经历;区分探针年份为 0(D12 分盘对比);或目录家族仍是「家人相关的明显变化」。
- 根因:口语题干用
period + 有没有 + event_family拼接。D12 对比探针没有年份,同域也没有带日期证据,period落成占位「那段时间」。第一轮误修成去掉年份、只问「有没有发生过」。反推点选必须锁在具体年/月上,没有时间就不能计分。 - 修复:存在性 / 发挥质量点选在锁不住具体年份时失败关闭,不出卡。目录跳过无年份 D12,改问下一条有年份的区分题;若没有,用自然语言采集一件记得住时间的家事。已记家人年份时题干为「2018 年前后,有没有家人结婚、添丁或住院?」。各域存在性家族改成可核对的口语事件。GET 把已持久化的「那段时间 / 目录锁」升级成带年份的新句。
- 验证:
rectification-choice-card锁定无年份家人帧为null;有引擎年份的家事探针才带年。rectification-eight-method锁定无年份 D12 让位给有年份的事业探针,或退回家人自然语言采集;已开的无年份家人卡不再保持评分帧。无年份 D24 不得借用账本学业年。方法层齐后剩下无年份 D4 时,先用自然语言问记得住时间的搬家,不出无年份点选卡。 - 防复发:评分用 A/B/C/D 反推不得用「那段时间」或无年份的「有没有发生过」顶时间。不得用年龄带中点发明年份。家人存在性家族不得再展示「家人相关的明显变化」。
- 相关记录:BUG-412、BUG-413
- 复发自:BUG-413(锁标签改成「有没有 + 家族」后,空年份仍被当成可问的点选卡)
- 修复版本:待发布
BUG-415 | 反推点选借用账本年份,点选后又多一条确认气泡
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:生时纠正点选卡
periodFor、冲突探针去重、answer_choice续跑、bindSpokenToOpenQuestion - 用户现象:点选卡问「2016 年前后有没有学业或考试发挥失常」。该年不是候选分钟反推出来的,而是账本里已记的学业年。点选 A/B/C/D 后先出现「已记录你的选择,并更新了候选比较。」,再出现工具「读取校正记录」和第二条确认/下一问。
- 触发条件:D24
event_quality探针年份为 0,同领域已有带日期证据;同时 Python 已给出另一领域带月份的 dasha 冲突探针。点选成功且nextAction仍要继续提问。 - 根因:评分反推把账本同领域年份当成锁年。冲突探针按整个领域去重,已有事业经历后不再问 2012 年 11 月事业边界。点选成功会持久化确认 turn,客户端再把它落成助手气泡,随后
read_only续跑又写第二条。 - 修复:存在性 / 发挥质量点选只认引擎探针年或月,不再回落到账本同领域年份。冲突探针按年去重,同年跳过、不同年继续问。无年份 D24 让位给带年份的 dasha 题。点选后续跑时不写确认 turn,客户端丢掉点选占位气泡,只保留续跑那一条;续跑正文去掉「已记录选择 / 请看下方选项」。
- 验证:
rectification-choice-card锁定无年份 D24 不得借用 2016 学业年,GET 展示 2012 年 11 月事业题。rectification-eight-method锁定同年事业探针跳过、不同年仍问,无年份 D24 让位给 2023 事业 dasha。rectification-answer-choice锁定续跑不写append_agentic_rectification_turn,「先这样」仍写。rectification-v9-stream锁定点选确认句被剥掉。 - 防复发:评分 A/B/C/D 反推不得用账本同领域已记年份顶引擎时间。冲突探针不得按领域整段跳过。点选后续跑不得再展示独立确认气泡。
- 相关记录:BUG-402、BUG-411、BUG-412、BUG-414
- 复发自:BUG-414(无年份家人卡修好后,无年份 D24 仍借用学业年);BUG-402(续跑补上后多写了一条确认)
- 修复版本:待发布
BUG-416 | 生时纠正口语被攒到整轮结束,点选后续跑不再逐字出现
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
runV9AgentTurn、step-answer、公开answer.delta、生时纠正聊天气泡 - 用户现象:工具活动仍会更新,但助手正文不再边生成边出现;点选卡回合尤其明显,进度走完后整段或占位句一次性弹出。
- 触发条件:生时纠正对话里模型在工具之后写口语;当前有
open_question/ 点选卡时更重。 - 根因:BUG-373 把含工具调用的 step 的
text-delta整段丢掉,只在step-finish发布终端步,口语无法按 token 直播。随后open_question把已写出口语放进heldSpoken,等到流结束再bindSpokenToOpenQuestion一次性发出。点选续跑还会把确认句剥掉,用户先看到长时间空白。 - 修复:终端中文在比对/结果类工具之后按
text-delta直播;未解锁时等到中文句末再开始直播。规划英文和工具步正文仍丢弃。有点选锁时边生成边绑定,缩短则发replace覆盖,不再等到 flush。客户端按replace重写气泡。 - 验证:
rectification-step-answer锁定比对后分片直播、工具步英文不进answer.delta、直播后若再调工具则 retract。rectification-v9-stream锁定比对后两段answer.delta按序到达,点选锁下确认句直播且不问句、timeout 仍用占位句。rectification-activity-receipt/rectification-agentic-entry锁定客户端replace。 - 防复发:有
open_question时不得把口语攒到flushPersistedPrompt。禁止把含工具调用的 step 的规划句留给浏览器。answer.delta若会缩短必须带replace,客户端不得只做raw +=。 - 相关记录:BUG-373、BUG-377、BUG-415
- 复发自:BUG-373(隔离终端步后不再按 token 发布);BUG-415(点选绑定把已推迟的口语又剥成空白)
- 修复版本:待发布
BUG-417 | 未成年事业/搬家点选,点选后续跑复述旧经历并因截断报未完成
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
event_probes.pydasha boundary、method-followup年龄下限、bindSpokenToOpenQuestion、点选后续跑answer_truncated - 用户现象:1997 年出生后被问「2012 年 11 月有没有入职升职」「2005 年 5 月有没有搬家离乡」。点选后续跑正文反复说「实习和离职都记下了」。选 C 后出现「回答未完成,已保留现有内容;本次不会扣点。」
- 触发条件:候选在童年/少年有高区分度大运边界;账本已有事业或感情经历;点选后续跑模型复述旧确认并写满长度。
- 根因:BUG-399 只给感情加了 catalog
age_lo,事业/搬家/学业仍从出生后 5 年起算。邻近年去重只覆盖学业。点选锁只剥问句,不剥跨领域确认。finish_reason=length即使已有open_question也走失败,超时则不会。 - 修复:成人语义领域(学业/搬家/感情/事业/财务/健康)的 dasha 边界都从
birth_year + age_lo起算,仍不用age_hi作硬上限。家人事件仍从出生后 5 年起算。感情/事业/搬家存在性探针跳过邻近年。点选锁下跨领域确认句丢掉。有点选锁时长度截断与超时一样收成成功占位句,不报未完成。 - 验证:
tests/test_rectification_event_probes.py锁定 1997 年出生不得出 2012 事业、2005 搬家,已记 2024 感情不得再问 2023。rectification-eight-method锁定出生日期过滤童年事业探针、邻近年感情探针。rectification-v9-stream锁定搬家/感情锁下丢掉实习确认,以及open_question+length仍run.completed。 - 防复发:成人语义事件家族不得用出生后 5 年当地板;家人事件仍允许童年年。点选续跑不得把上一件经历的确认句带到另一领域卡片。有
open_question时answer_truncated不得再把整轮标失败。 - 相关记录:BUG-399、BUG-415、BUG-416
- 复发自:BUG-399(只修了感情未成年年)
- 修复版本:待发布
BUG-418 | 点选后刷新,助手说请点选但 GET 没有卡片
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
applyRectificationChoice、GETchoice_card、点选后续跑 - 用户现象:点完区分题后助手仍写「接下来有一个问题需要您点选一下」,刷新后这条还在,下方没有点选卡。
GET的choice_card为null。账本里该问已经答过。 - 触发条件:训练门已开、当前区分焦点被点选关闭;同一请求没有写下一条焦点。客户端按
nextAction再开一轮只读续跑。刷新发生在续跑写完新焦点之前。 - 根因:GET 投影卡片必须有已持久化的焦点 UUID。点选只关闭当前焦点,下一条区分题或带年份的采集问要等后续 Agent 轮。续跑失败、截断或用户刷新时,界面只剩「请点选」占位,没有可点的卡。无年份 D24 也不能顶成评分卡。
- 修复:点选成功后在同一请求里按更新后的推断状态排出下一问。下一条是带年份的区分题就写入新焦点,正文改成「接下来请点选下面这一问。」;下一条是家人等采集就写入带年份的口语问,不出无年份 D24 卡。这两种都不再自动续跑。
- 验证:
rectification-answer-choice锁定答完搬家区分题后持久化 2014 学业卡,刷新 GET 仍有focus_id。只剩已答区分题时写入 2021 年家人采集口语,不调用 set-focus。rectification-eight-method锁定家人采集带上 collection-probe 年份。shouldContinueAfterStructuredChoice在nextInterviewPersisted时为 false。 - 防复发:点选关闭焦点后,同一请求必须留下下一问(新焦点或口语采集)。不得在 GET
choice_card为空时继续展示「请点选」。无年份 D24 不得在点选后变成评分卡。 - 相关记录:BUG-411、BUG-410、BUG-413、BUG-414、BUG-415、BUG-417
- 复发自:BUG-411(已持久化的卡会被丢掉;这里是下一问根本没持久化)
- 修复版本:待发布
BUG-419 | 生时纠正 holdout 落到年份事件,无星座 D9 被丢掉,题干把写作说明给用户
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
split-holdout、candidate-contrast-packet、method-followup出题目录 - 用户现象:训练/盘外划分时可能把只有年份的经历留作 holdout,把日/月精度事件拿去打分。D9/D10 窗口扫描没有星座名时整题消失。区分题题干出现「不得发明年份」「Opportunity」。账本里写过「程序员」一类职责说明后,剩余 D10 区分题被当成已经问过。
- 触发条件:至少两个领域都有两条带日期经历、其中只有事业带月/日精度;或 D9 只有换盘时刻没有 from/to 星座;或剩余分盘题进入点选;或职业备注含职责关键词而 D10 尚未真正答过。
- 根因:holdout 回退把月日事件与全部事件拼在一起再取最后一个,年份事件排在后面。D9/D10 缺星座时仍坚持
varga_style,选项凑不齐就被丢掉。remainingQuestion把给模型的写作说明写进question。账本关键词生成的varga.d10被当成已答 asked key,前缀匹配跳过整层剩余题。 - 修复:没有单领域孤立事件时,holdout 取最后一个月/日精度事件。D9/D10 凑不齐风格选项时降为存在题,不删题。
question改为对人的短问,写作说明放进authoringHint。账本关键词只作 mentioned/新颖度惩罚,不跳过剩余分盘题;只有已答semantic_key或显式varga.d10才跳过。存在题weak_yes仍与yes同向半权、一次作答不淘汰。 - 验证:
rectification-split-holdout锁定无孤立领域时 holdout 是日精度事业而不是年份感情。rectification-candidate-contrast-packet锁定无星座 D9 降为存在题、题干不含 Opportunity、程序员提及仍保留 D10 但选未提及的 D4。rectification-distinguish-contract锁定存在题weak_yes与yes同映射半权。rectification-decide-next-action/rectification-eight-method改为 mentioned 与 answered 分集。 - 防复发:不得把账本关键词生成的
varga.d*写进 packetaskedKeys。不得在 D9/D10 缺星座时继续要求varga_style四个风格选项。不得把不得发明年份写进对人的question。holdout 回退不得再把年份事件排在月日事件后面取最后一个。 - 相关记录:BUG-410、#39
- 复发自:无
- 修复版本:待发布
BUG-420 | 候选快照缺指纹被当成 current,stale 时把区分打回采集
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
decideFromDossier、scoreableSnapshotIsCurrent、decideRectification、GET/工具candidate_snapshot - 用户现象:已进入区分的 Case 在快照过期或缺证据指纹时,下一问变回采集,点选卡消失。工具侧和 dossier 侧对同一份账本可能一个判 stale、一个判 current。
- 触发条件:已有区分探针和训练门;
latest_result缺少evidence_ledger_fingerprint,或账本指纹已变但决策器仍走snapshotCurrent === false → collect。 - 根因:dossier 用
!storedFingerprint || stored === currentfail-open;工具用!storedSnapshot || !fingerprint || scoreableSnapshotIsCurrent同样 fail-open。decideRectification把snapshotCurrent === false与方法覆盖缺失绑在一起打回event_collection。 - 修复:两处都走
candidateSnapshotSource+storedSnapshotIsCurrent。指纹缺失判fingerprint_missing,不得当 current。没有已存快照仍不算 stale。快照 stale 且仍有区分探针时保持ask_candidate_discriminator,receipt 写stale_reason,要求重算;不把区分打回采集。不改 Skill。 - 验证:
rectification-decide-next-action锁定缺指纹为 stale;训练已开+探针时snapshotCurrent: false仍是ask_candidate_discriminator;训练未开仍采集。rectification-decision-authority无指纹的事业/感情训练账本仍区分。rectification-eight-method锁定缺指纹拒绝offer-candidates;暂停逃生口和覆盖后的 34/33/33 平局只在指纹匹配时出牌。 - 防复发:不得把空指纹写成 current。不得在已有区分探针时用 stale 快照把
nextAction改成ask_fact_collection。不得为了暂停出牌或平局出牌把空指纹 fail-open。occupation_note仍不得单独让 scoreable 快照失效。 - 相关记录:BUG-410、#40
- 复发自:无
- 修复版本:待发布
BUG-421 | 工具投影与 turn_decision 各判一次,单候选被写成并列区间
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
safeCaseProjection、latestResultToolProjection、evaluateCandidateSeparation、GET/工具session_outcome - 用户现象:同一 Case 的 turn_decision 与 full_diagnostics 可能给出不同的
session_outcome/selection_allowed。只剩一个候选时仍被说成基本并列。 - 触发条件:read-case 同时被问 turn_decision 与 full_diagnostics;或候选集收敛到 1 个分钟后继续出牌/写报告。
- 根因:
safeCaseProjection自己跑decideConversationalSession并可能第二次buildMethodFollowupPlan,latestResultToolProjection可在没有决策时把会话猜成collect_evidence。单候选把 lead 伪造为 8,状态仍是not_separated,报告按并列区间写。 - 修复:
safeCaseProjection开头decideFromDossier,方法计划只编一次。latestResultToolProjection必收RectificationDecision并用overlayPublicDecision。单候选状态为sole_candidate,不再伪造 lead。canConfirmExactMinute仍只由确认门打开。不改 Skill。 - 验证:
rectification-decision-authority锁定projectTurnDecision与safeCaseProjection的session_outcome/selection_allowed相同,工具源不再decideConversationalSession。rectification-decide-next-action锁定单候选是sole_candidate、可收口为代表时间、报告不含「基本并列」、确认门仍关。 - 防复发:不得在投影层再猜
sessionOutcome ?? "collect_evidence"。不得为单候选伪造MIN_SEPARATION_LEAD。不得把单候选写成并列区间。 - 相关记录:BUG-410、#41
- 复发自:无
- 修复版本:待发布
BUG-422 | 丢题不可见、契约无 golden、工具表与提示词重复
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
completeStyleOptions/isRenderableProbe、GETcurrent_question、Mastra Agent 工具表、contracts/probe-question-v1.json - 用户现象:区分探针因外貌词、标签不足或无法渲染被静默丢掉,界面和工具投影都看不到原因。选择题 schema 坏了时
current_question变成null,模型继续自拟题。采集阶段的提示词重复 Skill 里已有的外貌/财务禁令和工具调用表。 - 触发条件:varga 风格标签含禁词或不足两项;已持久化 Focus 的 choice schema 缺四选项;
proposeAllowed为 false 时模型仍看到offer-candidates。 - 根因:选项补全和可渲染检查只返回成功数组或
null,没有reason。读路径把坏 schema 当成「没有题」。Agent 工具表不看decideFromDossier。TS/Python 合同没有 byte-equal golden。uniquify曾用answer_class当可见后缀。 - 修复:补全/可渲染返回
{ ok, options|reason }。inspectDiscriminatorProbes把丢题写入decision.droppedProbes和 receiptdropped_probes。GET/turn_decision对坏选择题返回{ unrenderable: true, reason },采集类 Focus 仍为null。createRectificationV9AgentTools(ctx, decision)按 phase /proposeAllowed/selectionAllowed/canConfirmExactMinute过滤工具。合同 golden 在contracts/probe-question-v1.json,引擎 client 拒不匹配的question_contract_version。重复标签用·2而不是weak_yes。Mastra 提示词删掉已由代码执行的外貌/财务/工具禁令,Skill 保持 10.0.13。lib/birth-time-*仍被app/与components/的 guided/journey/intake 与/api/birth-time-journey、/api/birth-time-guide引用,本批不删。 - 验证:
rectification-probe-question-contract锁定 result type、index uniquify、golden bytes、forbidden_copy 丢题。Pythontest_probe_question_contract同样对齐 golden。rectification-answer-choice锁定坏 schema 为 unrenderable、采集 Focus 仍 null。rectification-server-focus锁定不完整 choice schema 为 unrenderable 而不是可点选 prompt。rectification-v10-tool-contract锁定proposeAllowed为 false 时没有offer-candidates。rectification-v9-engine-contract锁定错误合同版本 fail-closed。rectification-v9-agent/rectification-agentic-entry/rectification-ingest-p0/rectification-confirmation-gate锁定瘦身后的提示词(confirmation_allowed、不得宣称唯一出生分钟、新事件走 rectification-record-evidence-batch),不再要求 Mastra 提示词复述confirmation_gate。Independent Staging Quality Gate 必须通过后才算部署。 - 防复发:不得把
completeStyleOptions/isRenderableProbe改回只返回数组或 boolean。不得把坏选择题投影成current_question: null。不得在无决策时按 phase 过滤工具后,再把offer-candidates写进提示词禁令。不得用answer_class当可见标签后缀。不得在本路径删除仍被 journey/guide UI 引用的birth-time-*。不得把 Skill 升出版本。 - 相关记录:BUG-403、BUG-421、#42
- 复发自:BUG-403(动态四选项合同未贯穿丢题原因)
- 修复版本:待发布
BUG-423 | 写盘 still-valid 区间与读盘全体跨度不一致,区分永远收不了口
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
authoritativeCandidateProjection、GET/工具候选投影、decideFromDossier的candidateScores - 用户现象:区分环节答再多题也走不到
ask_holdout_validation或ready_to_adopt,最后落到offer_provisional_range且representative_time为 null。第一次打分、两候选 58/42 时投影就已经是空列表。 - 触发条件:receipt 由
buildInferenceState写入unionStillValidRange(落后峰值 ≥ 8 的候选不进区间),读盘却要求credible_range等于全体 active 跨度。lead ≥ 8 时两者必然分叉。 - 根因:写盘口径是 still-valid 并集,读盘校验口径是全体 active 跨度,阈值同为
MIN_SEPARATION_LEAD = 8,构成闭环死锁。consistent = false清空scores,evaluateCandidateSeparation判不出已拉开。既有 fixture 把分差写成 < 8 或手写宽 range 冒充生产者输出,所以测试红不了。 - 修复:
receiptRangeMatches改与unionStillValidRange(inference.candidates)比对,投影对外的credibleRange返回该 still-valid 并集。每个 activecluster_range落在全体跨度内的包含性校验保留。没有inference_state、坏 candidate set、或窗口外/颠倒的 range 仍 fail-closed。不改阈值、不改确认门、不改 Skill。 - 验证:
rectification-credible-range-projection用buildInferenceState/buildCaseInferenceState锁定 58/42、50/30/20、单候选、无 inference;58/42 决策层separation.sufficient且ask_holdout_validation或ready_to_adopt,canConfirmExactMinute === false。rectification-eight-method把「窄 range 当损坏」改为窗口外["04:00","04:10"]。rectification-decision-authority的损坏 range 同样改为窗口外或首尾颠倒。 - 防复发:不得让写盘与读盘对
credible_range使用不同口径。不得用手写credible_range字面量的 fixture 覆盖这条生产者路径。不得把consistent = false改成 fail-open。不得用改MIN_SEPARATION_LEAD绕过死锁。 - 相关记录:BUG-403、BUG-421
- 复发自:BUG-403(候选卡、代表时间、可信区间必须共享 inference 投影,但读写口径未钉成同一函数)
- 修复版本:待发布
BUG-424 | quarter/range 精度训练门计入、推断账本却标 unused
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
datedPrecision、buildCaseInferenceState的asPrecision - 用户现象:账本允许
quarter/range精度。训练门把它们当成带日期事件开门,推断状态却把同一批事件标成unknown/unused,既不打分也不进 holdout。 - 触发条件:录证据工具写入
datePrecision: "quarter"或"range"后重建inference_state。 - 根因:
evidence-model.datedPrecision把quarter/range映射成year,inference-adapter.asPrecision映射成unknown。 - 修复:导出同一个
datedPrecision,推断适配器也走它。空值仍是unknown。 - 验证:
rectification-v9-contracts锁定quarter/range→year。rectification-credible-range-projection锁定quarter事件usage !== "unused"。 - 防复发:训练门与推断账本不得对同一
datePrecision使用两套映射。 - 相关记录:BUG-423
- 复发自:无
- 修复版本:待发布
BUG-425 | varga 风格题按 answer_class 半权,时间靠前的组被系统性抬高
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-29
- 影响面:
ConflictProbe.choice_kind、conflictProbesFromContrast、applyProbeOutcome/directionFor - 用户现象:D9/D10 风格点选里,选项是并列的盘面风格,不是“有/弱有”。选后一组只得一半分,更早分钟被系统性抬高。
- 触发条件:剩余候选按 varga 风格分组出题;组 0 映射
yes(±2),组 1 映射weak_yes(±1)。 - 根因:
directionFor只看answer_class。风格题复用weak_yes当第二组标签,却走了存在题的半权。引擎若发来无style_options的varga.d9/varga.d10题,打分仍用原始choiceKind,渲染已降为existence。 - 修复:探针带显式
choice_kind。varga_style各组满权 ±2,unsure仍为 0。存在题 /event_quality的weak_yes仍半权且一次作答不淘汰。缺字段的旧收据保持旧分。不改SCORE_DELTA数值。conflictProbesFromContrast改用effectiveContrastChoiceKind,与渲染降级一致。 - 验证:
rectification-varga-style-weight锁定两组/三组风格等权、旧收据无choice_kind仍半权、replay 时candidate_set_id不变且revision单调;varga_style的weak_yes计入strong_conflict_count且连答 3 次可淘汰对面分组;引擎varga.d9/varga.d10无style_options时打分choice_kind与渲染 effective kind 一致。rectification-distinguish-contract锁定存在题weak_yes半权。rectification-coverage-collect锁定分数已拉开且家人/职业未覆盖时仍停在采集,且采集下一问是未覆盖的 blocking method;补齐或拒绝家人+职业后离开采集;canConfirmExactMinute === false。 - 防复发:不得从
semantic_key前缀猜测风格题权重。不得把存在题weak_yes改成淘汰或反向。varga_style的 B 选项weak_yes必须与 A 一样计入强冲突。打分用的choice_kind必须等于渲染用的 effective kind。旧收据缺choice_kind必须仍能加载。不得打开 unique-minute 门。 - 相关记录:BUG-419、BUG-410
- 复发自:无
- 修复版本:待发布
BUG-426 | 无年份 sticky holdout 仍进入核对,点选卡却渲染不出来
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-29
- 影响面:
holdoutStatusFromInference/holdoutStatusFromState、decideFromDossier、holdout 点选卡 - 用户现象:保留的 holdout 后来变成未知精度、没有年份后,决策仍要
ask_holdout_validation,出题计划却给不出可渲染卡片。 - 触发条件:
stickyHoldoutEvents把已保留 holdout 钉住;该事件year === null或precision: "unknown";或validate_holdout时既没有oosBlindPrompts也没有带年份的 holdout。 - 根因:holdout 状态只看
usage === "holdout"。能否出题看的是带年份的事件或 oos 提示。两套口径不一致。 - 修复:抽出共用 helper:能问(oos 提示或 dated holdout)才是
not_started;否则unavailable。unavailable且用户未停走既有adopt_representative,不当成passed。带年份 holdout 追问补上存在题选项和probe_year,好渲染 choice frame。不改applyHoldoutAnswer,不改 stickiness。 - 验证:
rectification-holdout-renderable锁定未知精度 sticky holdout 不进入ask_holdout_validation且可ready_to_adopt;带日期 holdout 有可渲染choice_frame;通过为validated_range;失败路径不变;nextAction === ask_holdout_validation时 followup 与choice_frame均非空。canConfirmExactMinute === false。 - 防复发:不得只凭
usage === "holdout"打开核对。不得把unavailable当成已通过。ask_holdout_validation必须能渲染出卡(next_followup与choice_frame均非空)。不得打开 unique-minute 门。 - 相关记录:BUG-424、BUG-410
- 复发自:无
- 修复版本:待发布
BUG-427 | 密封 holdout 合同把冻结 scorer 的成绩读成当前引擎成绩
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
references/rectification_sealed_holdout.v1.json、确认门 holdout 合同 - 用户现象:合同已回写 v3 的
top_1_rate=0.15,但当前树 sidecar 诊断是 0.45。读者会把冻结 scorer 的成绩当成当前引擎发布指标。 - 触发条件:读密封 holdout 合同、对照当前树
implementation_sha256,或把 sidecar 非盲诊断数字当成发布门槛。 - 根因:合同只写了指标数字,没有绑定产生这些数字的
implementation_sha256,也没有标明当前树是否仍匹配、更新的非盲诊断在哪。 - 修复:补充
metrics_produced_by/current_tree_scorer/current_tree_unfrozen_diagnostic。六个既有键的键名、类型、数值不动。sidecar 0.45 只记在带限定条件的诊断对象里。删除未引用的 v2PILOT_REPORT_PATH。确认门保持关闭。 - 验证:
holdout_passed()为 False;test_rectification_confirmation_and与rectification-confirmation-gate锁定身份哈希来自报告文件、sidecar 不得写入六个键、门仍关闭。 - 防复发:评测指标必须与产生它的
implementation_sha256绑定记录。当前树哈希漂移时不得把合同数字读成当前引擎成绩。不得把非盲、结果已被看过、scorer 未冻结、source audit 未过的 sidecar 数字写入发布六键。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-428 | v4 intake 把已看过、迁移审阅的案例标成密封盲测材料
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
minute_rectification_holdout_v4_intake.json、intake 脚本、holdout validator 人审安全条款 - 用户现象:4 例带着
independent_human_reviewed=true和holdout_partition=sealed_evaluation,但adjudicator是自动化迁移,评分结果已在 v3 盲测回放和 post-audit sidecar 里被看过。 - 触发条件:把 v3 密封集迁入 v4 intake 后,用
case_errors(..., require_review_safeguards=True)当盲测资格。 - 根因:人审安全条款只看布尔量;intake 接受该布尔量即可写成已审。曝光记录和人审来源没有一等字段。
- 修复:4 例留在队列并标
prior_score_exposures、human_review_kind=migrated_v3_record、frozen_before_scoring=false、holdout_partition=exposed_awaiting_human_rereview。带曝光记录的案例不得过盲测校验;只有fresh_biography_audit满足人审条款。intake 不再在缺少该字段时把案例写成已审。盲测可用 0 例、已曝光待复审 4 例。生产调参与精确分钟声称仍为 false。 - 验证:
tests/test_minute_rectification_holdout_intake.py、tests/test_minute_rectification_holdout_validator.py。 - 防复发:结果已被看过的案例不得再计入盲测。人审安全条款不得由自动化脚本自行置真。迁移来的审阅记录不满足新的传记级人工核查。
- 相关记录:BUG-427
- 复发自:无
- 修复版本:待发布
BUG-410 | 训练已齐仍因家人/职业方法层停在采集,Agent 只确认后截断
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
decideRectification、GETinterview、Mastra 出题计划、生时纠正 Agent 口语 - 用户现象:事业和感情各记下两件带日期经历后,助手只说“记下了”,不再追问,也没有点选卡。引擎已经算出高信息量 D24 区分题。
- 触发条件:训练事件达到 3 条/2 个领域(holdout 不计),家人与职业方法层仍未覆盖,候选尚未拉开,目录里已有可渲染区分探针。
- 根因:
methodCoverageAll把relatives和occupation当成进入区分的硬门槛。决策器因此一直返回collect_evidence。出题计划却按信息量选出 D24。Agent 被禁止在没有open_question时口述区分题,采集下一问又不是家人/职业,正文只剩确认句,choice_card为空。 - 修复:训练门已开、候选未拉开、且存在区分探针时,不再被未覆盖的家人/职业挡回采集。分数已经拉开时仍不得因缺方法层而直接采用。提示禁止因家人或职业未覆盖改回收集。
- 验证:
rectification-decide-next-action锁定未拉开或公开分数字段被清空时,训练已齐+探针 →ask_candidate_discriminator;已拉开+方法层未齐仍采集。rectification-decision-authority用事业+感情训练账本锁定 D24 区分题和 choice frame。rectification-eight-method锁定训练已齐后 dasha 冲突探针的会话结果是discriminate_candidates,不再停在collect_evidence。 - 防复发:不得把家人/职业方法覆盖当成区分阶段的前置条件。不得在
session_outcome=collect_evidence且下一问是区分题时把追问整段丢掉。 - 相关记录:BUG-400、BUG-405、BUG-407
- 复发自:BUG-407(目录能选出 D24,决策器仍被方法覆盖挡在采集)
- 修复版本:待发布
BUG-409 | staging publish 的 next build 因 answered_probes.id 与 optional receipt 失败
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:Gitea
backend-quality-gate.ymlpublish、deploy/railway-web.Dockerfile的RUN npm run build、contrastPacketFromLatestResult、Mastrarectification-v9-tools - 用户现象:向
staging推送月份精度修复后,run2128validate 通过,publish 在 web 镜像next build失败。没有 dispatchDeploy staging。公网仍停在上一成功 SHA。 - 触发条件:staging push 的 validate 跳过
npm run build;publish 才在 Docker 里跑 TypeScript。 - 根因:
answered_probes的字段是probe_id,目录过滤写成了不存在的item.id,tsc报 TS2339。工具侧latestResult.decisionReceipt可为undefined,目录函数要求| null,报 TS2345。BuildKit 日志末尾仍是skill-package-registry.ts的 Import traces,真正失败是Failed to type check。 - 修复:已答探针按
probe_id过滤。目录函数接受undefinedlatest,并把缺省 receipt 收成null再往下传。 - 验证:
npx tsc --noEmit;rectification-decision-authority锁定已答探针按probe_id退出目录。 - 防复发:改生时纠正目录或 Mastra 工具类型后,推 staging 前必须跑
npx tsc --noEmit或等价next build,不能只跑tsx --test。 - 相关记录:BUG-309、BUG-315、BUG-321、BUG-371、BUG-408
- 复发自:BUG-309(validate 跳过
next build,publish 才暴露类型错误) - 修复版本:待发布
BUG-408 | 反推题丢掉大运起点的月份,同年 3 月对 9 月问不出来
- 状态:resolved
- 首次发现:2026-08-28
- 最近更新:2026-08-28
- 影响面:
scripts/rectification/event_probes.py、点选卡choice_frame.period、Skill10.0.13区分阶段时间范围 - 用户现象:反推点选卡只写「YYYY 年前后」。候选分钟只差十几三十分钟时,大运/副运起点其实已经错开几个月,但题目仍按年问;同一年里 3 月对 9 月这种差被丢掉。
- 触发条件:训练事件已够、剩余候选的 Vimshottari/Narayana 大运或副运起点相差不足一年,但月份已分开。
- 根因:探针只取起点
.year,边界还要求两边年份至少差 1 年,评分固定落在当年 7 月 1 日。引擎算到的月日被扔掉。 - 修复:边界改用大运/副运实际起点。年份不同,或同年且相差至少 45 天,按该月出题并按该月计分。标签写成「YYYY 年 M 月前后」。年龄带兜底仍只锁年。点选卡直接用探针
year_label,不得再从年份拼回「年前后」。开场仍不诱导用户猜月份。Skill 版本保持10.0.13。 - 验证:Python 事件探针回归锁定同年 3 月/9 月边界、微小同年漂移仍不出边界题、年龄带仍是年精度;前端点选卡与 method-followup 锁定「2018 年 3 月前后」。
- 防复发:
dasha_boundary必须带引擎月份;不得把起点截成年后再比。年龄带/无起点时不得伪造月份。 - 相关记录:BUG-405、BUG-407
- 复发自:无
- 修复版本:待发布
BUG-429 | 同一账号资料变化时星盘库被两个 effect 各写一遍 localStorage
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
/主对话页星盘库 hydration 与本人记录 upsert - 用户现象:进入对话或改一次出生资料时,主线程同步
JSON.stringify并写localStorage两次;账号切换时两条路径靠 ref 提前 return 分工,没有显式分支。 - 触发条件:
accountId或profile引用变化。 - 根因:两个
useEffect依赖数组都是[accountId, profile],一个负责首次本地+云端,一个无条件 upsert 本人。行为能跑通,但每次资料变化都重复序列化。 - 修复:合并为一个 effect,按
clear/hydrate-then-persist/persist-self显式分支。云端失败仍保留本地库;本人记录仍 upsert;切账号仍清空。 - 验证:
frontend/tests/chart-library-session.test.ts覆盖清空、切账号 hydration、同账号 persist-self、云端只保留 other、云端失败保留本地。既有chart-library-other-profile源码锁未改。 - 防复发:同一份资料不得用两个同依赖 effect 靠 ref 提前 return 分工;分支名必须写在代码里。
- 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-430 | 字体栈声明了 Inter 却从未加载,Windows/Linux 静默落到系统黑体
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-09-05
- 影响面:全站
--font-body、后台 antdfontFamily - 用户现象:设计稿写了 Inter,实际请求里没有 woff2;非苹果系统落到 Segoe UI / 微软雅黑。
- 触发条件:打开任意带根布局的页面。
- 根因:仓库 0 个字体文件、0 处
@font-face、0 处next/font,CSS 与 admin token 却把 Inter 写在 StyreneB 之后。 - 修复:根布局用
next/font/local加载仓内InterVariable-latin.woff2(display: "swap"、--font-inter)。早期曾用next/font/google,构建期会访问 Google Fonts,见 BUG-543。--font-body与 adminfontFamily改为var(--font-inter, Inter)。Tiempos / 宋体栈未改。构建产物与/HTML 可见 woff2 preload。 - 验证:
frontend/tests/site-style-isolation-contract.test.ts;next build的/HTML 含 Inter woff2 preload。 - 防复发:字体栈里出现的西文家族必须有
next/font/local(仓内字体文件)或@font-face;不得用next/font/google。缺授权的展示字体(Tiempos)不得用同一套办法偷偷补上。 - 相关记录:BUG-543
- 复发自:无
- 修复版本:待发布
BUG-431 | 自托管 PostgreSQL 缺库时仍对用户写“Supabase 尚未配置”
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
GET /api/account、会员余额、登录失败提示、会话/报告/星盘库等 503 - 用户现象:运行时已是 Better Auth + 本地 PostgreSQL,缺业务库 URL 或未设
AUTH_PROVIDER=self-hosted时,会员页“当前余额”和首页仍显示“Supabase 尚未配置”。 - 触发条件:未注入
APP_DATABASE_URL/SERVICE_DATABASE_URL,或AUTH_PROVIDER未设为self-hosted因而落到旧适配器路径。 - 根因:BUG-010 已禁止浏览器直连 Supabase,但 API 与
friendlyError仍把配置失败翻译成 Supabase 文案和SUPABASE_NOT_CONFIGURED。产品已不用 Supabase。 - 修复:对外统一为“数据库尚未配置” /
DATABASE_NOT_CONFIGURED,以及“请先配置数据库环境变量。”。内部类名仍叫SupabaseConfigurationError,避免本轮改适配器标识。 - 验证:
frontend/tests/self-hosted-error-copy.test.ts扫描frontend/src禁止上述旧文案,并锁定错误码与内部消息。 - 防复发:用户可见中文不得再出现“Supabase 尚未配置”;缺库 503 必须说数据库,不得说已退役的托管服务。
- 相关记录:BUG-010
- 复发自:BUG-010
- 修复版本:待发布
BUG-432 | 生时校正区分题出题层静默丢弃探针后无卡无记账死锁
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
POST /api/rectification/agent消息分支、decideFromDossier、buildMethodFollowupPlan、口述采集 focus - 用户现象:点选两道区分题后,助手口述家人采集问但没有选项卡。用户用自由文本回答后,服务端回没有出路的终结话术;
current_question与choice_card均为 null,case 停在collecting_evidence;该回合 receipt 的 phases / tools / tool_activities 全空,用户这句话里的新证据没有入账本。 - 触发条件:训练证据已齐、已答 dasha 边界探针、剩余探针含无年份 varga 与缺
style_options的varga_style;方法覆盖未齐时出题层回落到口述采集;随后用户在无 active choice focus 时发自由文本。 - 根因:
inspectDiscriminatorProbes用withCompletedContrastOptions把缺style_options的varga_style降级后仍视为可渲染,并选出无年份 varga 探针进入ask_candidate_discriminator。renderableContrastProbe仍走未降级的completeStyleOptions,把同一探针静默丢弃。无年份桶只在覆盖齐时使用,于是回落到source=method_coverage的家人采集且不带choice_frame。路由只信决策层:persistServerOwnedFocusskipped、openQuestionFromPersistedFocus为 null 后直接返回终结话术,既不落证据也不跑 Agent。口述采集题不落 focus,刷新后问题丢失;卡片持久化失败时返回无正文下一步。 - 修复:出题层与决策层共用
withCompletedContrastOptions;丢弃探针写入dropped_probes。仅当出题层给出intent=distinguish_candidates且带choice_frame的 followup 时才进入ask_candidate_discriminator,否则落到采集。消息分支拿不到可渲染卡且仍有 followup 时落入runV9AgentTurn;仅当next_followup为 null 时才收口,话术给出继续采集或按区间看盘。口述采集持久化不带 choice 的 collect focus;卡片持久化失败时落到口述采集下一步。冲突探针写回时保留style_options。收尾加固:口述采集投影带kind=collect_spoken,current_probe/inference.next_probe只对可渲染选择题放行;降级口述采集剥掉区分探针身份(semantic_key/ 年份 /probe_id),正文与 schema prompt 同一次生成;卡片失败后的假分支删除,focus 持久化失败按safeErrorCode/ caseId 打脱敏日志;decideFromDossier把可选birthDate传给门控 plan,门控通过时decision.probe取 plan 真正选中的探针。点选路径persistApplied先加载一次 compute,把同一birthDate喂给decideAfterInferenceChange与后续出题 plan。Skill 版本保持10.0.13。未放宽 confirmation gate,未增加「已确认唯一分钟」路径。 - 验证:
frontend/tests/rectification-discriminator-followup-consistency.test.ts锁定同一探针池上决策层与出题层一致、decision.probe与plan.next_followup.semantic_key一致、缺style_options的 D9/D10 风格题在方法覆盖未齐时仍出区分卡、无星座时钟键 fail-closed 到采集、低龄年份探针传入birthDate时门控与真实 plan 结论一致、消息路径不再返回死路话术。rectification-answer-choice.test.ts锁定口述采集后仍有 collect focus 与current_question,collect 时current_probe为空、选择题时非空,降级采集不含区分题身份,点选路径persistApplied在decideAfterInferenceChange之前加载 compute 并传入同一birthDate。rectification-server-focus.test.ts锁定 collect schema 不是未渲染区分卡,降级采集的 questionId / schema 不含区分题键且同一探针随后可出卡。rectification-turn-intent-classifier.test.ts按新收口话术改断言。npx tsc --noEmit、npx tsx --test tests/rectification-*.test.ts tests/birth-time-rectification-contract.test.ts tests/agentic-rectification-*.test.ts与npx tsx --test tests/*.test.ts。 - 防复发:可渲染判定必须共用选项补全,不得一边降级接受一边静默丢弃。无卡时不得把用户消息当终结话术丢掉。有合法 choice schema 的 active focus 仍走轻量分类 +
applyRectificationChoice。不得引入语义正则、A/B/C/D 位置推断或静态选项 fallback。口述采集题不得占用区分探针身份。无选择卡的问题必须由 Agent 正文问出来,current_probe只对可渲染选择题放行。 - 相关记录:BUG-404、BUG-407、BUG-410、BUG-411
- 复发自:BUG-404
- 修复版本:待发布
BUG-433 | 根错误页和 404 在摘掉 globals 后变成裸 HTML
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
src/app/error.tsx、src/app/not-found.tsx;任意未匹配路由与根渲染崩溃 - 用户现象:访问不存在的路径或触发根错误边界时,页面不居中、无配色、无间距,按钮塌成无样式文字。
- 触发条件:根布局不再 import
globals.css之后,这两页仍用 Tailwind 工具类和Button。 - 根因:它们与 admin 共享根布局段,不能 import
site-styles;工具类定义只在站点 CSS chunk 里。上一轮把“必须另开布局链”写成唯一出路,漏了仓库里global-error.tsx已有的内联样式模式。 - 修复:照
global-error.tsx改为模块常量 + 内联style,fallback 色值与globals.csstoken 一致。错误页“重试”用原生 button,“返回对话”用next/link。404 仍保留既有源码锁render={<Link href="/" />},用cloneElement套内联样式,不引入Button。 - 验证:
frontend/tests/site-style-isolation-contract.test.ts新增断言(既有断言未改);frontend/tests/root-error-boundaries-contract.test.ts保持绿灯;next build的_not-found.htmlbody 无min-h-svh等工具类;admin 路由 CSS 仍不含.message-list。 - 防复发:根布局段页面(
error.tsx/not-found.tsx/forbidden.tsx/global-error.tsx)只能用内联样式,不得依赖工具类或globals.css/site-styles。 - 相关记录:BUG-219、BUG-430
- 复发自:无
- 修复版本:待发布
BUG-434 | 入门付费墙的关闭按钮掉到标题下面,弹窗头部从未有过样式
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
/入门问题完成后弹出的「解锁完整咨询」弹窗,移动端最明显 - 用户现象:标题「解锁完整咨询」独占一行,关闭「×」以灰色圆角块的形式落在标题下方靠左,和礼物图标那段挤在一起;头部与正文之间没有间距。
- 触发条件:打开该弹窗即出现,与账号状态无关。
- 根因:
onboarding-redeem-paywall.tsx写的是<div className="dialog-header">,而dialog-header这个类在全仓 CSS 里没有任何定义。同类弹窗(account-dialog-overlay.tsx、membership/page.tsx)用的都是<header className="account-modal-header">(display:flex; justify-content:space-between; padding-bottom)。付费墙自a17ff258引入起就拼错,未被任何测试或样式覆盖,因此一直是无样式块级 div,标题与按钮竖排。 - 修复:改为
<header className="account-modal-header">,与另外两处弹窗完全一致。未新增任何 CSS,未改文案、aria-label="关闭"、aria-modal、焦点陷阱与 44px 触达。 - 验证:
frontend/tests/membership-page.test.ts新增两条 —— 一条锁付费墙必须复用account-modal-header且不得再出现dialog-header;一条通用守卫,抽出该组件所有静态className逐个断言在globals.css里有对应选择器。反向验证:把dialog-header放回去,两条同时变红;还原后 38 pass / 0 fail。 - 防复发:弹窗头部一律用
account-modal-header,不得为单个弹窗另造头部类名。新增组件若引入新类名,必须同时在globals.css落规则 —— 上面那条通用守卫会挡住这类拼写漂移。 - 相关记录:BUG-433
- 复发自:无
- 修复版本:待发布
BUG-435 | 删除会话确认弹窗的两个按钮没有任何样式,"确认删除"也不是红色
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
/侧边栏删除会话时弹出的「删除聊天记录?」确认框 - 用户现象:「取消」和「确认删除」渲染成浏览器原生灰色按钮 —— 没有圆角、没有 44px 高度、没有内边距,破坏性操作也没有任何红色提示。
- 触发条件:打开该确认框即出现。
- 根因:
page.tsx:4000的「取消」完全没写 class;page.tsx:4001的「确认删除」写的是danger-button,而这个类在全仓 CSS 里没有定义。全局只给button设了font: inherit; color: inherit,没有任何兜底规则,所以两个按钮都是裸原生按钮。同一文件 60 行之前的退出登录确认框用的是正确写法button-secondary+button-primary danger-primary。 - 修复:两个按钮改成与退出登录框完全一致的
button-secondary/button-primary danger-primary。未新增 CSS,未改文案与role="alertdialog"。星盘库「删除」上同样无定义的danger-button修饰符一并去掉(它本就没生效,渲染不变)。 - 验证:
./node_modules/.bin/next build后用类名扫描脚本核对,danger-button已从"无规则类名"清单消失;tsc --noEmitexit 0、eslint0 error、全量测试无新增失败。 - 防复发:破坏性确认框一律复用
button-secondary+button-primary danger-primary,不得临时造修饰符。新类名必须同时在globals.css落规则 —— 参见 BUG-434 在membership-page.test.ts里加的通用守卫。 - 相关记录:BUG-434
- 复发自:无
- 修复版本:待发布
BUG-436 | 主对话消息间距规则从 8-19 起失效,消息比设计稿挤
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
/主对话(非空、非生时校正会话)里相邻消息之间的竖向间距 - 用户现象:消息之间只剩气泡自身的内边距,比设计稿定的
--space-8(移动端--space-6)窄。 - 触发条件:任何有历史消息的普通咨询会话。
- 根因:
globals.css的.conversation:not(.is-empty):not(.is-rectification) .message + .message { margin-top: var(--space-8) }写于200c39d2(2026-07-22),当时消息是.message-list的直接兄弟。7d667fec(2026-08-19)给每条消息套了一层<div className="message-entry">,而这个类在 CSS 里没有任何定义,两条.message从此不再相邻,选择器永久不匹配。同一规则组里.message又被覆盖成padding: 0,于是间距归零。3198fb6b把同一层包装搬进ChatTranscript,未改变这一点。 - 修复:选择器改为
.message-entry + .message-entry,桌面--space-8、移动--space-6数值不变。.message-list里除.message-entry外只有可选的.error-message,不会误伤。引导语消息在.welcome内且只在.is-empty时渲染,本就被选择器排除。 - 验证:
frontend/tests/class-name-definition-contract.test.ts(本次新增的通用守卫)反向验证 —— 把选择器改回.message + .message后message-entry立即被判为无规则类名,测试变红;改回则绿。 - 防复发:给消息列表加包装层时必须同步迁移依赖相邻兄弟的选择器。新类名漏定义由上述守卫兜底;该守卫会剥掉 CSS 注释再判定,避免"只在注释里出现过"被当成已定义。
- 相关记录:BUG-434、BUG-435
- 复发自:无
- 修复版本:待发布
BUG-437 | 区分卡因过期候选集前缀被二次加前缀,界面无卡且 Agent 只承接不提问
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
probeFromEngine的candidate_split_hash归一化、projectRectificationChoiceCard身份校验、scoreAndPersistCurrentEvidence组包的candidateSetVersion、GETchoice_card - 用户现象:
current_question是合法选择题(kind=choice、focus_id与probe_id齐全、题干已生成),choice_card却是 null。Agent 按提示第 4 条只承接不复述,正文只剩「请你凭印象选一个」,界面没有卡片,用户无法继续。dropped_probes为空,看不出被抑制的原因。 - 触发条件:某一轮打分改变了候选分钟列表(例如 05:05 换成 05:00),此前铸出的 varga 对比探针仍带旧候选集前缀,随后任意一次读路径重建对比包。
- 根因:
scoreAndPersistCurrentEvidence组对比包时candidateSetVersion取上一轮inference_state.candidate_set_id,candidateTimes取本轮score.candidates,于是新写入的探针 hash 带着旧集合前缀,而同一份 state 的candidate_set_id按新候选生成。之后probeFromEngine用hash.includes(candidateSetVersion)判断是否已有前缀,旧前缀不含新集合 id,于是再加一层前缀得到双前缀 hash。落 focus 时stampChoiceSchemaWithProbe写的是 state 里探针的单前缀原值,projectRectificationChoiceCard的candidate_split_hash全等校验因此永远不成立,直接 return null,且不记入任何丢弃账本。 - 修复:新增
splitHashForCandidateSet,hash 为<候选集 id>:<语义键>形态时按当前候选集重铸,已带当前前缀时保持不变,不透明引擎 hash 只加一次前缀,杜绝前缀叠加。新增isSameCandidateSplit,两侧同为<任意候选集 id>:<同一语义键>时视为同一 split(语义键本身已编码候选分组),projectRectificationChoiceCard改用它做身份校验,语义键不同或不透明 hash 不同仍然 fail closed。组包candidateSetVersion改为按本轮candidateRange与score.candidates计算的candidateSetId,与写入 state 的候选集同源。Skill 版本保持10.0.13,未放宽 confirmation gate。 - 验证:
frontend/tests/rectification-choice-card.test.ts锁定「探针 hash 带旧候选集前缀时 GET 仍返回卡片」(去掉修复即失败)与「持久化 split 属于另一探针时仍隐藏」;rectification-candidate-contrast-packet.test.ts锁定前缀重铸幂等、不透明 hash 只加一次、split 身份忽略前缀但不忽略探针、组包按本轮候选集作用域。npx tsc --noEmit;npx tsx --test tests/rectification-*.test.ts tests/birth-time-rectification-contract.test.ts tests/agentic-rectification-*.test.ts658 项 0 失败;全量npx tsx --test tests/*.test.ts失败集合与改动前一致(23 项数据库/部署环境类)。 - 防复发:探针
candidate_split_hash只能有一层候选集前缀,任何读路径不得对已带前缀的 hash 再加前缀。打分写入的对比包必须与本轮候选集同源,不得沿用上一轮candidate_set_id。卡片身份校验不得因候选集前缀差异静默吞掉合法卡片。 - 相关记录:BUG-410、BUG-411、BUG-432
- 复发自:无
- 修复版本:待发布
BUG-438 | 移动端侧边抽屉里「新建对话」「我的报告」居中悬空,与其余左对齐项不齐
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
/移动端(<768px)侧边抽屉顶部两个导航项 - 用户现象:品牌行、星盘列表、收藏对话、历史对话全部左对齐,只有「新建对话」和「我的报告」的图标+文字组居中飘在抽屉正中,视觉上完全不成列。
- 触发条件:在移动端打开侧边抽屉即出现。
- 根因:
.new-chat/.report-nav-button的基础样式是justify-content: center(为桌面折叠图标栏准备),左对齐靠顶层的[data-state="expanded"] … { justify-content: flex-start }翻回来。但data-state取的是open ? "expanded" : "collapsed",open是桌面折叠状态;移动抽屉由独立的openMobile控制。组件侧isCollapsedDesktop = state === "collapsed" && !isMobile,移动端恒为 false,所以文字标签照常渲染;CSS 侧却因data-state="collapsed"匹配不上覆盖规则,按钮保持居中。JS 与 CSS 对"展开"的定义不一致。品牌行没被带歪,是因为[data-state="collapsed"] .brand-row那整套图标栏规则写在@media (min-width: 768px)里,移动端本就不生效。 - 修复:把默认翻过来 —— 基础样式改为
justify-content: flex-start(移动抽屉与桌面展开态都正确),删掉依赖data-state的顶层覆盖,居中改为@media (min-width: 768px)内的[data-state="collapsed"]规则,与既有的.brand-row、[data-sidebar="menu-button"]、.profile-trigger等图标栏规则并列。全仓仅此一处顶层[data-state=…]选择器,其余均已按视口收口。 - 验证:
frontend/tests/sidebar-contract.test.ts新增一条 —— 锁基础层左对齐、禁止再出现[data-state="expanded"]版覆盖、并要求居中规则位于 768px media 内。反向验证三次(基础样式改回 center / 覆盖加回来 / 居中规则移出 media),每次都变红。 - 既有断言改动(唯一一处):
frontend/tests/personal-report-entry.test.ts原先断言.report-nav-button必须justify-content: center,那正是本条缺陷本身,改为flex-start并注明原因。该断言其余部分(display: flex、align-items: center)未动。 - 防复发:
data-state只描述桌面折叠态,不得用它决定移动抽屉的布局;折叠图标栏的样式一律写进@media (min-width: 768px),不得作为基础层默认值再由覆盖翻回。 - 相关记录:BUG-434、BUG-436
- 复发自:无
- 修复版本:待发布
BUG-439 | 回答里的围栏代码块没有任何样式,长代码行撑破消息栏
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
/主对话中 Agent 回答里的 ``` 围栏代码块 - 用户现象:代码块没有容器,行内代码的小胶囊底色套在整块代码上;一行长代码不换行也不滚动,直接溢出消息栏,移动端会把版面顶横。
- 触发条件:回答里出现围栏代码块。
- 根因:
globals.css只有.message-markdown code(行内胶囊),没有任何pre规则。react-markdown 输出的裸<pre>保留浏览器默认的white-space: pre且无overflow,内容宽度不受.message-content的max-width约束。.message-content上的min-width: 0只能让 flex 收缩,挡不住块内溢出。 - 修复:新增
MarkdownCodeBlock组件接管pre,输出「语言标签 + 复制按钮」的头条和可横向滚动的代码区;.markdown-code pre > code设overflow-x: auto并重置行内胶囊底色,容器overflow: hidden收住圆角。未引入语法高亮依赖。 - 验证:
frontend/tests/markdown-code-block-contract.test.ts锁overflow-x: auto与white-space: pre必须同时存在、胶囊底色必须重置、pre必须走该组件、复制按钮有 aria-label、剪贴板被拒时不得误显示「已复制」、卸载时清定时器。 - 防复发:给 markdown 新增块级元素时必须同时给出容器与溢出策略;行内与块级代码的样式不得共用一条规则。
- 相关记录:BUG-434
- 复发自:无
- 修复版本:待发布
BUG-440 | 生时校正采集阶段否定后无下一问,对话空转
- 状态:resolved
- 首次发现:2026-08-29
- 最近更新:2026-08-29
- 影响面:
buildMethodFollowupPlan方法覆盖口述题、classifyRectificationTurnIntent采集焦点、applyCollectFocusDenial/persistNextInterviewIfIdle、POST/api/rectification/agent自由文本路径、GETcurrent_question - 用户现象:点完若干区分卡后,助手口述问家里有没有结婚、添丁或住院这类记得住时间的事。用户回「没有」。助手只确认「记下了」,随后
current_question与choice_card均为 null,interview.type = ask_fact_collection,对话停住,无法继续。 - 触发条件:方法覆盖未齐(尤其 relatives),剩余可渲染探针都是无年份分盘题;用户对家人口述采集题给出明确否定,且该轮走自由文本而不是点选。
- 根因:三段同时成立才会空转。(1) 「没有」既不是证据也不是计分答案,
answered_probes与 revision 不变;若焦点以resolved关闭,declined_skipped_topics只聚合status in ('declined','skipped'),buildMethodFollowupPlan又只认target_domain,覆盖度不前进。(2) 无年份分盘题被coverageComplete挡在 dated 排序之后,计划回落到同领域口述采集题;家人覆盖要求「有家人证据或有 declined 记录」,家里确实没发生过事的用户两条都不满足,而varga.d12本就是这道家人题、no分支可以计分淘汰候选,却被当成不计分采集题问出去并丢失。(3) 点选路径有persistNextInterviewAfterChoice保证下一问落库;自由文本路径跑完runV9AgentTurn后没有同等兜底。Agent 不重复刚被否掉的问题(这点是对的),于是只确认不提问,current_question变成 null。 - 修复:P0:即将返回某领域
intent=collect_method_evidence+source=method_coverage口述题时,若rankRenderableDiscriminators的 yearless 桶里有同领域可渲染探针,改出该 distinguish 卡(followupFromRanked,按既有rankDiscriminatorScore取最高)。未放宽 coverageComplete 对 yearless 的总排序,避免回归 BUG-405/407。无年份 varga 探针允许铸出 choice_frame,不得向题干借用账本年份。P1:采集焦点自由文本接入同一套轻量分类器;明确否定时服务端把焦点 resolve 成declined(不是resolved),并按点选路径持久化下一问。同步修正rectification-resolve-focus工具描述。未引入语义正则、关键词表或 A/B/C/D 位置推断。focusStatusForAnswer的no → declined语义未改。P2:runV9AgentTurn成功后若无 open focus 且 plan 仍有 followup,按点选路径补落下一问。收尾:(a) 分类器新增可选has_new_dated_event,缺失按 false;选择题与采集题快路径在该字段为 true 时先确定性地应用答案(计分 / declined),不落确定性正文、不 return,继续runV9AgentTurn记同一句里的新带时间事件;为 false 时行为与原先逐字一致。应用答案时deferFollowup,让 Agent 看到没有 active focus,不得再 resolve。(b)persistNextInterviewIfIdle预检改为先decideFromDossier,用同一个decision.sessionOutcome建 plan,并复用该 decision 做publicNextAction,不再写死collect_evidence。Skill 版本保持 10.0.13,未放宽 confirmation gate。 - 验证:
frontend/tests/rectification-collect-stall.test.ts锁 D12 卡、答 C declined 计分、occupation 否定推进 horary、自由文本后current_question非 null、有年份区分题仍优先。新增:缺失has_new_dated_event按 false;采集/选择题快路径在该字段为 true 时应用答案后进入 Agent 且不落确定性正文;采集否定 + deferFollowup 不抢先落下一问;选择题 deferFollowup 仍计分且不落下一问/正文;persistNextInterviewIfIdle同一 dossier 只调用一次decideFromDossier,预检 sessionOutcome 与决策层相同。npx tsc --noEmit通过;eslint 改动文件 0 error。指定套件tests/rectification-*.test.ts tests/birth-time-rectification-contract.test.ts tests/agentic-rectification-*.test.ts672 项、0 失败。全量tests/*.test.ts2266 项、0 失败。失败集合未扩大到产品逻辑。 - 防复发:同一领域的问题若存在可渲染计分卡,不得改用不计分口述题。采集题的明确否定必须产生 declined 记录,不得写成 resolved。出题层还有 followup 时
current_question不得为 null。不得为了救这条空转而取消 coverageComplete 对 yearless 的总限制。确定性快路径不得吞掉同一句话里的新带时间事件。同一次调用里出题层与决策层必须用同一个 sessionOutcome。 - 相关记录:BUG-404、BUG-405、BUG-407、BUG-411、BUG-432、BUG-437
- 复发自:无
- 修复版本:待发布
BUG-441 | 口述采集题已落库但界面看不到,助手只说看下面这一问
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:GET
current_question、openQuestionFromPersistedFocus、agentVisibleLatestProjection、stableFollowupQuestionId、POST/api/rectification/agent出卡判定与空正文兜底、Agent 系统提示第 4 条。可见性策略已被 BUG-449 取代。 - 用户现象:助手正文只写「接下来看下面这一问。」,界面没有任何问题。GET 里
choice_card为 null,current_question已是合法口述采集题(kind=collect_spoken、prompt 非空),问题只存在于数据库和 API。 - 触发条件:方法覆盖回落到不带 choice_frame 的口述采集焦点;Agent 按选择卡承接措辞说话;前端只解析
choice_card。 - 根因:三段同时成立。(1) 前端从不渲染
current_question:applyCaseSnapshot只解析latest_result/choice_card/case,kind=collect_spoken在界面上没有承载位置。(2) 面向 Agent 的两套投影自相矛盾:projectTurnDecision给出current_question.kind=collect_spoken与 prompt;记完证据后的agentVisibleLatestProjection取自openQuestionFromPersistedFocus,后者对 collect focus 一律返回 null。Agent 收到打架信号,退回「卡片会显示」的措辞。(3)stableFollowupQuestionId在 followup 既无 semantic_key 也无 probe_year 时退到${method_id}:${ask_theme};persistPlanFocus传入的 active_focus 计划正好两者都没有,questionId 退化成active_focus:active_focus。 - 修复:P1/P2 仍在:
openQuestionFromPersistedFocus对 collect focus 返回带kind=collect_spoken的同形结果;choiceReady/ 出卡判定只对kind=choice为真;current_probe与inference.next_probe仍只对可渲染选择题放行;collect 在无 semantic_key / probe_year 时用collect:<domain>:<intent>,active_focus:active_focus不得与新 id 互相 already_open。P0 独立提示条已撤回:前端不再解析或渲染current_question。口述采集题改由 Agent 用自己的话在正文里问出来(该可见性策略已被 BUG-449 取代:题干改由服务器按焦点生命周期确定性接在正文之后,模型不得自行写题干)。「请点选」「看下面这一问」仍只允许在确实有选择卡时使用。服务端 collect focus 继续持久化,GET 仍返回current_question。Skill 保持 10.0.13,未放宽 confirmation gate。 - 验证:
frontend/tests/rectification-spoken-collect.test.ts锁:GETcollect_spoken不再渲染独立块、.rectification-spoken-collect-prompt不存在、选择卡分支仍在。collect openQuestion 不为卡片、probe 在 collect 下为空、active_focus 派生 questionId 带 domain。可见性回归见 BUG-449。BUG-440 collect-stall 全绿,D12 同领域计分卡仍优先于家人口述题。 - 防复发:服务端问题的可见性由正文与确定性兜底保证,不得为无选项的问题新造视觉容器,更不得借用选择卡的样式类。同一焦点在所有面向 Agent 的投影里形态必须一致。「请点选」类措辞只允许在有选择卡时使用。不得用正文文本判断助手有没有问出来。collect questionId 不得退化为
active_focus:active_focus。口述采集题的可见性策略见 BUG-449,不得再改回「指望模型问」。 - 相关记录:BUG-404、BUG-411、BUG-432、BUG-437、BUG-440、BUG-449
- 复发自:BUG-432(无选择卡的问题曾要求由 Agent 正文问出,前端仍不渲染
current_question) - 修复版本:待发布
BUG-442 | 职业采集关不上、无年份卡出不来、覆盖门压掉区间出口
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:
applyOccupationCollectLedgerNorm/occupationCovered、buildMethodFollowupPlan无年份分盘卡、decideRectification覆盖门、POST/api/rectification/agent区间出口、houseTableAlignedToMinute/publicChartForMinute - 用户现象:账本已有足够可计分事件和多个领域,助手仍反复追问职业口述题;候选几乎均匀,
margin_percent停在门槛以下,entropy 不降;can_offer_range/can_adopt/propose_allowed/selection_allowed全为 false,用户从头到尾没被告知可以按当前区间收口。对外代表分钟与宫位表也可能不是同一时刻。 - 触发条件:职业采集焦点下用户给出职业性质描述,模型把证据记成
domain=career的入职/换工作;训练事件已达标,剩余高信息量探针全是无年份分盘题;引擎回执已accept_allowed/propose_allowed,决策层仍因 occupation 覆盖 flag 落collect()。 - 根因:四段同时成立。(1) 覆盖判定
isOccupationNote要求domain=occupation,职业口述回答被记成 career 域,occupation 方法层永不关闭,next_followup恒为职业口述采集题。(2) 无年份分盘卡的出题条件绑在coverageComplete上,覆盖不齐则 d24/d7/d4/d5 一道都出不来,margin 无法被这些探针拉开。(3)decideRectification撞上coverageBlocks直接collect(),把canOfferRange压成 false;同一份引擎回执已经放行 accept/propose,决策层因覆盖 flag 单方面压掉区间出口。(4) 对外投影的representative_time取推理层代表分钟,house_table/natal_recast却可能是另一分钟算出的盘面。 - 修复:P0:occupation 采集焦点下,服务端把该轮职业性质描述归一成
domain=occupation/eventKind=occupation_note(无日期允许,occupation_note 不参与计分)。覆盖端:已有确认 career 证据且 occupation 采集焦点已按 questionId / target_domain 关闭时也算覆盖,不读正文、不用关键词。P1:训练门开时允许出无年份分盘卡,不再要求覆盖齐;训练门未开仍不出,避免回到 BUG-405/407。同领域可渲染计分卡仍优先于不计分口述题(BUG-440)。P2:训练门已开且出题层给不出带 choice_frame 的可渲染区分卡时,允许canOfferRange并播报「可以先按当前区间看盘,也可以再补一件记得时间的经历」;引擎 accept/propose 已放行而覆盖 flag 要压掉时至少放行canOfferRange。canAdopt与 confirmation gate 维持 fail-closed。P3:对外representative_time、house_table、natal_recast必须是同一分钟;对不上则取house_tables_by_time对应表,否则标成不可用。Skill 保持 10.0.13。未引入语义正则、关键词表、A/B/C/D 位置推断或轮次状态机。 - 验证:
frontend/tests/rectification-occupation-coverage-exit.test.ts用本例 15 件可计分、4 域账本锁:occupation 采集回答落 occupation_note;career 证据 + 已关闭 occupation 焦点覆盖成立;career 单独仍不覆盖;训练门开且覆盖未齐能出无年份分盘卡,训练门关仍不出;训练门开且无可渲染区分卡时canOfferRange为真、canAdopt/canConfirmExactMinute为假;路由播报二选一;代表分钟与house_table/natal_recast同源,构造不一致回执不得直接发出。collect focus 仍不是选择卡。BUG-440 collect-stall、BUG-437 hash、D12 同领域卡优先保持绿。 - 防复发:覆盖判定不得依赖模型选对 domain。同一个覆盖 flag 不得同时锁死出题与出口。训练门已开且无可渲染区分卡时必须给用户出路。对外代表分钟与盘面必须同源。不得用正文文本判断覆盖或意图。
- 相关记录:BUG-404、BUG-407、BUG-410、BUG-432、BUG-437、BUG-440、BUG-441
- 复发自:BUG-440(覆盖未齐时的收口分支存在,但 occupation 记账域错误导致永远走不到;无年份卡仍被同一覆盖门锁死)
- 修复版本:待发布
BUG-443 | D9/D10 风格卡用了类型表原文、题干贴了年份、题干贴着选项
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:
varga-type-tables的d9StyleLabel/d10StyleLabel、completeStyleOptions风格题固定项、choice-card题干、birth-time-choice.csslegend、.rectification-choice-why - 用户现象:D9/D10 风格点选卡选项是「和谐、美感、合作」「照顾、情感」这类类型表词条,三个抽象名词并列、维度不统一,无法对号入座。题干出现「2024–2026 年这段里,做事风格更接近其中一种?」;风格题没有「这段」可指。题干与第一个选项几乎没有间隔。
- 触发条件:候选在 D9 或 D10 上升上分开,出
choice_kind=varga_style点选卡。 - 根因:三段同时成立。(1)
d9StyleLabel/d10StyleLabel直接输出D9_TYPE_TABLE[sign].trait/D10_TYPE_TABLE[sign].style,解读用的占星类型表字段被当成用户选项。(2)choice-card.ts的periodFor对varga_style走lifePeriodLabel(evidence, domain),eventQuestionPrompt再把时间段贴到长期风格题干上;风格描述的是平时怎么做 / 怎么相处,不随年份变化。(3).birth-time-choice-question用 gridgap分隔子项,但题干是<legend>,legend 不参与 fieldset 的 grid 布局,gap 对题干与选项不生效。 - 修复:类型表为 D9/D10 每行新增面向用户的
userChoice,不改trait/occupation/style/spouse/marriage。标签函数改输出userChoice;answer_class与 sign 的映射不变。风格题no改为「都不太像我」,unsure 单独为「一时说不好」;existence / event_quality 固定选项文案不变。varga_style不再返回时间段,题干写成长期表述。legend 与.rectification-choice-why用--space-3下外边距,不把 legend 改成p、不新增视觉容器。Skill 保持 10.0.13。未改计分、探针身份或 hash。 - 验证:
frontend/tests/rectification-varga-style-copy.test.ts锁:12×2userChoice非空且不等于trait/style;sign →answer_class仍按组序;风格卡题干无年份/「这段」;existence / event_quality 仍锁年份且固定选项不变;四选项isRenderableStyleOptions;legend / why 源码具有非零下边距。修复前:标签等于类型表原文、题干带lifePeriodLabel年份、「这段记不清楚」用在风格题、legend 仅margin-bottom: var(--space-1)。合同金标contracts/probe-question-v1.json同步。 - 防复发:面向用户的选项文案不得直接复用占星类型表字段。长期风格题不得锁定时间段。卡片内题干与选项必须有独立于 grid gap 的间距。existence / event_quality 的年份月份锁定(BUG-408、BUG-412)不得被风格题改动带回。
- 相关记录:BUG-408、BUG-412
- 复发自:无
- 修复版本:待发布
BUG-444 | 无年份分盘对比题重新出成无星座依据的点选卡
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:
isRenderableProbe/inspectDiscriminatorProbes/rankRenderableDiscriminators/buildChoiceFrame/shouldAttachChoiceFrame、无年份 remaining-layer 分盘对比题 - 用户现象:家里有没有结婚添丁住院、有没有搬家、学业压力、收入变化、生病受伤这类题可以在题干没有任何时间范围的情况下出成四选一点选卡;选项仍写「明确发生且时间吻合」「这段记不清楚」。答了以后按分组顺序灌进后验,候选被无依据地拉开或压掉。
- 触发条件:
REMAINING_LAYERS产出year: 0的分盘对比探针;训练门已开;出题层把无年份varga.*一律当成可渲染计分卡。 - 根因:三段同时成立。(1) 为满足 BUG-440「同领域可渲染计分卡优先于不计分口述采集题」,把
choice-card的无年份豁免从性格倾向题放宽到所有semantic_key以varga.开头的探针,重新打开了 BUG-414 关掉的门。(2) 存在类和质量类固定选项预设了题干里没有的时间范围(「时间吻合」「这段」)。(3) 非varga_style的分盘对比题remainingStyleOptions不绑定星座,remainingOutcomes按分组顺序映射 yes / weak_yes / no,属于无依据计分。 - 修复:没有具体年份或月份时,只有
choice_kind === varga_style且计分选项带sign才允许铸卡。同一条判定走共享的isRenderableProbe,同时作用于inspectDiscriminatorProbes与rankRenderableDiscriminators。被排除的探针进入dropped_probes,理由yearless_ungrounded_contrast。题干无时间范围时选项不得含「时间吻合」「这段」。oos_blind且无年份的 followup 显式不得挂choice_frame。覆盖门与区间出口保持 BUG-442:训练门已开且没有可渲染区分卡时仍可canOfferRange。计划回落到带年份的采集题。未改 confirmation gate、answer_class 映射、探针身份或 hash。Skill 保持 10.0.13。 - 验证:
frontend/tests/rectification-yearless-ungrounded.test.ts逐层锁无年份 d4/d7/d12/d2/d11/d30(existence)与 d24/d5(event_quality)不出卡;带 sign 的无年份 d9/d10 仍出卡,缺 sign 的 varga_style 不出卡;带年份的 dasha_boundary / age_band / known_event_quality / reserved_event 仍出卡;被排除探针出现在dropped_probes;决策层与出题层同一探针池结论一致;oos_blind 无年份无卡。修复前:上述无年份 existence/quality 层会铸卡,决策可能落ask_candidate_discriminator。BUG-440 collect-stall、BUG-437 hash、BUG-442 occupation 覆盖与区间出口、BUG-414 无年份家人卡、BUG-443 风格文案保持绿。 - 防复发:只有选项绑定星座的性格倾向题可以没有时间范围;其余分盘对比题必须有具体年份或月份;无时间范围的卡不得有预设时间的选项;被规则排除的探针必须记入
dropped_probes;不得为了「有题可问」重新放宽本条。判定必须看kind与sign字段,不得按层名白名单,不得对题干做语义分析。 - 相关记录:BUG-432、BUG-440
- 复发自:BUG-414
- 修复版本:待发布
BUG-445 | 深色主题次要字色与行动色在卡片底上对比度不足 4.5:1
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:深色主题 token、围栏代码块语言标签、根边界页行动色
- 用户现象:深色模式下卡片底上的次要说明文字和黏土橙行动色发灰发糊,代码块语言标签是其中一处已上线的实例。
- 触发条件:系统偏好或账户菜单选择深色;任何把
ink-tertiary或action画在canvas-muted上的界面。 - 根因:对比度合同只验了字色对阅读面
--color-canvas,漏掉了卡片底--color-canvas-muted。实测--color-ink-tertiary#928e84对#30302d为 4.05:1,--color-action#d4785a为 4.18:1,均低于 WCAG AA 4.5:1。 - 修复:两个深色块把
--color-ink-tertiary改为#9c988e、--color-action/--color-focus/--report-accent改为#d78064(色相 15°、饱和度不变,只提亮明度)。四个根边界页内联 token 与DESIGN.md对照表同步,清掉#d4785a。 - 验证:
frontend/tests/dark-theme-contract.test.ts把对比度断言扩成 8 种字色 × 4 种底色,失败信息打印具体配对和比值;反向把 tertiary 改回#928e84后该测试变红。 - 防复发:新增字色或底色 token 时必须进入这张 32 组矩阵,不得只验阅读面;不得靠放宽 4.5 阈值让测试通过。
- 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-446 | 回答操作等控件热区小于文档承诺的 44px
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:回答下方赞/踩/复制/重新生成、侧栏星盘 chip、登录页链接按钮、模型选择项、生时跳过、报告中心章节按钮
- 用户现象:回答下方四个图标按钮只有 26×26px 且没有 padding,很难点准;若干列表项和链接也低于 44px。
- 触发条件:任意一条助手回答下方的操作行;侧栏星盘 chip;登录页链接;onboarding 下拉;报告中心章节操作。
- 根因:
DESIGN.md写了 44px,样式却把高频图标做成 26px/padding: 0,chip 32px,若干文字按钮 40px。 - 修复:视觉尺寸不动。图标行与 chip、登录链接用透明伪元素扩大热区。选择项、跳过、报告章节按钮把
min-height提到 44px。回答操作四个按钮中心距只有 27px、下方 follow-up 只有 8px 边距,44×44 会互相吃点击,热区停在不重叠的 27×34;chip 与登录链接因 8px 间距停在 40px。见BLOCKED.md。 - 验证:
frontend/tests/touch-target-contract.test.ts锁 26px 视觉、伪元素尺寸、以及三处min-height >= 44。改前改后截图对比视觉尺寸。 - 防复发:新增可点元素必须满足 44px 命中区;视觉小于 44px 时用伪元素补,且扩大后的热区不得与相邻可点元素重叠。
- 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-447 | 字数上限只有 maxLength,到顶后界面毫无反应
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:主对话输入框、姓名输入、生时引导经历框、选择题补充说明
- 用户现象:打到字数上限后键盘像失灵,没有提示说明已经到顶。
- 触发条件:主输入写满 500 字(入门称呼为 80)、姓名 80、引导经历 500、补充说明 240。
- 根因:四处只有
maxLength,没有接近上限时的可见反馈,也没有aria-describedby。 - 修复:剩余字数进入阈值(500→50,短输入按 10% 且不低于 8)才挂载计数;平时不显示。计数用
ink-tertiary,到顶转danger。aria-live="polite"且aria-atomic="true",可见数字不进 live 文本,避免每字播报。主输入计数放进已有.composer-footer,不新造一层把输入框顶高。 - 验证:
frontend/tests/character-remaining-contract.test.ts锁阈值、两句 live 文案、四处aria-describedby接线。 - 防复发:带
maxLength的产品输入必须在阈值内给出计数,并只在阈值内挂载 live region。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-448 | 生时引导框用 ⌘/Ctrl+Enter 发送且没有任何说明
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:
birth-time-guide-turn经历输入框 - 用户现象:主对话和生时校正对话都是 Enter 发送,到引导框按 Enter 只会换行,界面没有任何键位说明。
- 触发条件:生时引导流程里在「说说这段经历」多行框按 Enter。
- 根因:该框绑定了修饰键发送,且未处理
isComposing;另外两处已经是 Enter 发送并忽略组字中的 Enter。 - 修复:统一为 Enter 发送、Shift+Enter 换行,并加上
!event.nativeEvent.isComposing。保留「整理为经历草稿」按钮。在既有#birth-time-guide-hint补一句键位说明。选择统一发送键而不是只补提示,是因为用户已在另外两处养成 Enter 习惯,漏掉isComposing会让中文选词误发送。 - 验证:
frontend/tests/composer-ime-contract.test.ts锁三处onKeyDown都判断isComposing,引导框不再看metaKey/ctrlKey。 - 防复发:产品内多行发送框必须同一套 Enter / Shift+Enter /
isComposing合同;新的输入框不得只改发送键不处理组字。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-449 | 口述采集题已落库但自由文本后助手只说记下了,用户看不到下一问
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:POST
/api/rectification/agent自由文本路径、persistNextInterviewIfIdle、persistCollectSpokenAssistantIfNew、runV9AgentTurn口述题可见性、Agent 系统提示第 4 条 - 用户现象:答完一件事后助手只说「记下了:…」,之后没有任何问题也没有卡片,但 GET 里
current_question已是合法collect_spoken题。 - 触发条件:自由文本记下带年份经历(本例唯一证据为 2022 搬家、
precision=year);轮内rectification-record-evidence-batch→autoRescoreAfterEvidenceChange→persistPlanFocus新建collect:relationship:collect_method_evidence;助手正文非空。 - 根因:五段同时成立。(1)
persistNextInterviewAfterChoice对 collect 焦点返回hostNarration = spokenFollowupForUser(followup);点选路径把它写进正文,所以点选没坏。(2)persistNextInterviewIfIdle调用同一个函数,却丢弃hostNarration,只返回{persisted, choiceReady}。自由文本路径因此没有题干输出。(3) 它还在开头if (dossier.conversationSummary.activeFocus) return提前返回;本 case 的焦点是轮内persistPlanFocus建的,所以这条兜底连跑都没跑。(4) route 唯一的兜底persistEmptyCollectSpokenAssistant只在!result.answerText.trim()时触发;正文非空一律不补。(5) 选择卡不受影响(卡片由current_question渲染);区分题有discriminatorInvariantfail-closed。collect 两样都没有。BUG-441 把可见性改成「指望模型问」后,非空正文不再由服务器保证。 - 修复:P0:在
runV9AgentTurn已有的首份 dossier 上快照activeFocus.id;轮后若projectCurrentQuestion为collect_spoken且 focus id 与轮前不同(含轮前为 null),服务器补题干。同一 id 不补。choice 永不补。P1:题干复用spokenFollowupForUser/ schemaprompt;非空正文作为独立一段接在同一条助手消息后并answer.delta;空正文整条消息就是题干。补出文本过INTERNAL_TOKEN_RE,不得含「请点选」。agent-run 与 route 共用 helper + 本轮collectSpokenEmitted,同一轮最多一次。P2:persistNextInterviewIfIdle返回hostNarration,交给 P1;activeFocus 提前返回保留,但不吞掉 P0。P3:撤回「口述采集题必须由你用自己的话问出来」;有持久化当前问题时题干一律不由模型写。Skill 保持 10.0.13。未放宽 confirmation gate / coverageComplete,未回退b43808b0无年份收窄。未用正文文本判断是否已问,未新造视觉容器。 - 验证:
frontend/tests/rectification-spoken-collect.test.ts锁:2022 relocation 自由文本后正文含感情采集题干;同一 focus id 下一轮不重复补;choice 正文无题干;空正文整条即题干;in-turn / idle 共用同一句题干且每轮一次;无内部 token、无「请点选」。改写「非空不补」与 routeif (!answerText.trim())旧锁。assert.doesNotMatch(/answerText\.(?:includes|match|search)\(/)仍绿。BUG-440 / BUG-441 / BUG-442 /b43808b0无年份收窄回归保持绿。修复前:persistEmptyCollectSpokenAssistant({ answerText: "记下了:2022年搬家。" })返回null。 - 防复发:口述采集题的可见性由服务器确定性输出保证,不得依赖模型是否照做,也不得用正文文本反推;点选路径与自由文本路径必须共用同一句题干来源(
spokenFollowupForUser/ schema prompt)。不得为口述题新造视觉容器或复用选择卡样式。 - 相关记录:BUG-404、BUG-432、BUG-440、BUG-441
- 复发自:BUG-441
- 修复版本:待发布
BUG-450 | 点选题答完后的非收敛区间提议卡死
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:
offerRangeWithoutAdopt、persistNextInterviewIfIdle、projectRectificationChoiceCard、buildCandidateContrastPacket、出题层rankRenderableDiscriminators、record-evidence-batch、POST/api/rectification/agent消息快路径 - 用户现象:点选题答完后,助手说「可以先按当前区间看盘,也可以再补一件记得时间的经历」;界面既没有可采用的时间卡,也没有下一道口述题。用户随后用真实带时间经历回答时,旧焦点仍指向已经回答的采集题,对话到此停止。
- 触发条件:训练门已开、occupation 等方法覆盖未齐、
datedMethodCollectOpen为假、引擎已放行 accept/propose、probe === null、canOfferRange=true但canAdopt/selectionAllowed/proposeAllowed全为假;随后collect_spoken焦点收到已成功入账的真实事件,且本轮工具调用可能先执行record-evidence-batch。 - 根因:五段同时成立。(A) 探针耗尽是假耗尽:带年份探针的分支只把已淘汰候选切出,当前活跃候选在所有
answer_class分支中待遇相同,按活跃集合重算信息量为零;旧出题层沿用全窗口信息量,不重算,也不把该探针写入dropped_probes,于是以probe=null静默失效。(B)probe=null且方法覆盖未齐时,rectification-decision.ts的offerRangeWithoutAdopt仍把canOfferRange设为真,但保持canAdopt/selectionAllowed/proposeAllowed为假并关闭 focus;这把本应继续补覆盖的状态送进没有承载的区间出口。(C)provisional_range的deferAdoption原本连intent=collect_method_evidence的覆盖追问一起吞掉,把它从next_followup挪到deferred_followup;消费端不读后者,因覆盖不足进入的状态反而结构性阻止补覆盖,形成自锁。(D) 用户用真实事件回答口述采集焦点并成功落库后,服务端没有按 active focus 确定性执行resolved;焦点继续存在,persistNextInterviewIfIdle因 active focus 提前返回,旧题仍出现在current_question。这与「确实没有」必须走declined的语义不同。(E) 本轮工具顺序可能是record-evidence-batch先于rectification-read-case,模型未拿到服务端 focusId 时仍能记证据;旧实现把工具顺序当成硬前置,既没有先补读 dossier,也没有保证后续焦点关闭和下一问落库。 - 修复:P0:
provisional_range只对intent=distinguish_candidates延后 followup;collect_method_evidence覆盖题继续从next_followup返回并正常落焦点。adopt_representative/validated_range/exact_minute_confirmed/provisional_range_user_stopped/completed_with_range的既有收口行为不变。P1:对比包和rankRenderableDiscriminators均按当前活跃候选重算探针信息量;零区分力探针显式写入dropped_probes.reason=no_split_among_active,不放宽MIN_BOUNDARY_DAYS、distinguish 合同或确认门。P2:record-evidence-batch成功且当前 active focus 是collect_spoken时,服务端按 dossier 中的 focus 自行resolve(status=resolved, evidenceId=acceptedEvidenceId),不依赖模型传focusId;否定回答仍走declined。关闭后继续走下一问持久化。P3:offerRangeWithoutAdopt使用统一的nonConvergingRangeNarration输出可信区间、代表分钟及「代表分钟不是已确认的唯一出生分钟」口径,并在同一轮持久化至少一个可执行的collect_spokenfocus;不为无选项问题新造视觉容器,也不为看盘按钮放宽canAdopt/confirmationAllowed。P4:若本轮尚未成功执行rectification-read-case,record-evidence-batch先自行读取一次 dossier 再落库,不因模型工具顺序错误打断用户。 - 验证:修复前基线(
79f65ac8)实际回归输出:record-before-read 时第一条 RPC 为insert_agentic_rectification_tool_receipt而非 dossier read;provisional_range.next_followup为undefined而不是collect_method_evidence。修复后tests/rectification-eight-method.test.ts为 62/62;该套件同时锁定首条 RPC、accepted evidence 使用其 evidence ID 关闭 spoken collect focus、resolve 发生在 record 之后且不再返回旧题;tests/rectification-range-offer-deadend.test.ts覆盖活跃候选零信息量 drop、仍有真实 split 时继续出区分卡、数字区间与代表分钟叙述、口述 focus 落库、userStopped 行为;tests/rectification-spoken-collect.test.ts与 BUG-440 / BUG-442 /b43808b0无年份收窄回归保持绿。npx tsc --noEmit通过;改动文件 ESLint 0 error(6 条既有 unused-vars warning);focused rectification/contract suite 727/727;全量tests/*.test.ts2339/2339;/opt/anaconda3/bin/python3.12 -m pytest tests/test_rectification_event_probes.py tests/test_candidate_discriminator_contract.py35/35。 - 防复发:任何因缺某项覆盖而进入的状态,不得阻止获取该项覆盖;探针信息量必须针对当前活跃候选计算,失去区分力的探针必须显式 drop;口述采集焦点被真实证据回答后必须由服务端确定性关闭,不依赖模型传
focusId;resolved与declined语义不得互换;助手正文承诺的每个出口,同一轮必须有对应承载,区间提议必须带数值与代表分钟口径;record-evidence-batch不得因未先 read-case 而丢弃用户证据。不得用answerText文本判断问题是否已经问出,不得引入语义正则、关键词表或 A/B/C/D 位置推断,不得回退无年份varga_style收窄或放宽 confirmation gate。 - 相关记录:BUG-405、BUG-407、BUG-432、BUG-440、BUG-441、BUG-442、BUG-449
- 复发自:BUG-432(出题层/决策层/可见承载再次分裂)、BUG-440(覆盖不足状态再次空转)、BUG-441/BUG-449(口述焦点可见性与服务端兜底边界未覆盖本路径)
- 修复版本:
fix(rectification): prevent collect focus dead-end after choice answers(Skill 保持10.0.13)
BUG-451 | 个人报告整份调用失败掩盖单章失败与截断边界
- 状态:mitigated
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:个人报告 worker、
createPersonalReportAgent、/reports/[reportId]、personal_report_jobs - 用户现象:一次模型输出承载整份报告;任一主题写坏或最终 JSON 解析失败,整份报告显示
report_schema_invalid,用户看不到已成功生成的主题。生成日志也无法区分模型停止、输出截断和校验失败。 - 触发条件:完整报告包含多个 write 主题,单次 writer 输出超过 provider 默认预算或任一主题未通过绑定/最终 schema 校验。
- 根因:报告 writer 只做整份报告的一次结构化调用加一次修复重试,没有章节级持久化、重试预算或
finishReason/token telemetry;report_document也没有可安全表达半成品的中间语义。 - 修复:先上线并采集
finishReason、inputTokens、outputTokens;随后改为串行 plan → section → summary → assemble。每章按evidenceRefs过滤 bundle 并独立落库/重试,耗尽后生成明确 blocked disclosure;摘要最后只接收章节标题与claimStatus;仅最终 assemble 写report_document,并把 job progress phase/percent 返给等待页。 - 验证:任务 0 真实 staging 观测为 1 次报告失败、
finishReason=length占比 0%、outputTokensp50/p95 为 3069/3069,失败归因为 report-level parse 而非截断;分章节单元测试、续做/blocked 测试、真实 Docker 数据库 RLS/权限测试均通过;TypeScript、ESLint(0 error)通过。 - 防复发:模型调用必须记录 allowlist 指标字段且不得记录 prompt、模型原文或用户资料;章节中间结果只能写
personal_report_sections;章节生成保持串行;摘要不得接收正文全文;blocked 章节必须显式渲染,不能静默丢失;最终成品仍须通过服务端 canonical parse。 - 相关记录:BUG-352
- 复发自:无
- 修复版本:待 staging 部署;本地提交待生成
BUG-452 | 采集焦点落库失败后静默结束
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:
persistNextInterviewAfterChoice、persistFocusAfterChoice、persistExhaustionCollect、POST/api/rectification/agent非终态轮末出口 - 用户现象:答完家人采集题后助手只说「记下了,这方面先跳过。」,没有下一问也没有时间卡。
- 触发条件:上一采集焦点刚被关闭,下一条
collect_method_evidence焦点落库返回duplicate_focus或skipped;该轮同时没有可采用的候选承载。 - 根因:采集焦点落库失败时
persistNextInterviewAfterChoice返回hostNarration=null,随后被 denial 兜底文案「记下了,这方面先跳过。」吞掉;轮末也没有结构化不变量检查current_question或真实可采用承载,最终形成非终态但无可继续交互入口的静默死路。 - 修复:采集焦点首次落库失败后重读 dossier 并重试一次;若已存在同
questionId的 active focus,则按already_open复用。重试仍失败时复用persistExhaustionCollect口径,正文写出可信区间、代表分钟和口述下一问,并尝试落一个可回答的采集焦点。自由文本轮末新增结构化非终态出口检查:若未 accepted、未 confirmed、未 user-stopped,且既无current_question也无真实可采用承载,则确定性补口述采集焦点、把题干写入正文,并记录不含用户内容的rectification_nonterminal_exit_repaired日志。删除会成为终局的 denial 单句兜底。 - 验证:修复前新增 fixture 证明
duplicate_focus与skipped均会得到hostNarration=null、current_question=null;修复后两条路径均有问题正文并持久化或复用非空焦点。新增非终态出口不变量回归,锁定服务端补出collect_spoken焦点和题干。复刻五证据、五条已答探针、活跃候选05:00/05:07/04:53、family declined 的 case,锁定career.2023.dasha_activation以no_split_among_activedrop、nextAction=offer_provisional_range、canAdopt=false,轮末current_question.question_id=collect:occupation:collect_method_evidence且正文含「你长期做什么工作?」。assert.doesNotMatch(afterRun, /answerText\.(?:includes|match|search)\(/)保持通过 本地npx tsc --noEmit、改动文件 ESLint、目标四组回归122/122通过;focused 全套730/731,唯一 Docker/PostgreSQL migration fixture 失败单独重跑1/1通过;全量基线2342/2347,五项失败均为并行数据库容器/migration 环境波动或本机缺少 PyYAML,未扩大到产品逻辑。 - 防复发:非终态轮次结束时必须有
current_question或真实可采用承载;任何采集焦点落库失败都不得静默降级为一句确认话。该不变量只看焦点与 decision/承载字段,不得用正文文本判断助手是否问出问题。不得为无选项题新造视觉容器或复用选择卡样式;不得引入语义正则、关键词表或 A/B/C/D 位置推断;resolved与declined语义不得互换。 - 相关记录:BUG-441、BUG-449、BUG-450
- 复发自:BUG-450
- 修复版本:
fix(rectification): prevent silent collect focus stalls(Skill 保持10.0.13)
BUG-453 | 可采用区间已由服务端放行但前端不渲染时间卡
- 状态:resolved
- 首次发现:2026-08-30
- 最近更新:2026-08-30
- 影响面:
rectification-agentic-chat.tsx时间卡渲染门控、choice-action点选快路径、Case GET 默认投影 - 用户现象:答完最后一道选择题后助手只说「已记录你的选择,并更新了候选比较。」;界面没有时间卡也没有下一问,流程停住。
- 触发条件:服务端已给出
can_adopt=true、selection_allowed=true、precision_stage=ready_to_adopt,但当前轮是确定性点选路径,没有产生rectification-offer-candidates模型工具 receipt;前端仍以历史offeredSelectionOnce作为时间卡前置条件。 - 根因:服务端允许采用,前端却把承载绑定到模型是否调用某个工具;确定性点选路径不会产生该 receipt step,因此
selectionAllowed=true仍无法渲染时间卡。点选快路径原正文也只保留确认句,没有区间、代表分钟和采用提示。 - 修复:P0:时间卡改为只依赖服务端
selectionAllowed=true与canAdopt=true,保留最新已结算助手消息、忙碌/只读/重生成状态和选择卡互斥门;不再依赖offeredSelectionOnce。P1:点选进入offer_provisional_range + can_adopt=true时由服务端确定性写出可信区间、代表分钟、代表分钟免责声明和「可以从下面的时间里选一个采用」;该正文同时落入确定性 turn。P2:非终态轮末只看current_question或决策canAdopt,缺失时确定性补口述采集焦点并记录结构化日志。P3:Case GET 默认只保留活跃候选/代表分钟对应宫位表与当前焦点 probe;detail=full保留完整 receipt;decision、gates、inference_state不被瘦身修改。模型侧继续使用既有的精简 projection。 - 验证:新增/更新前端门控与
canAdoptcamel/snake 解析回归;保留 duplicate focus、skipped focus、非终态出口和五证据 case 回归;P1 断言确定性落库正文包含区间、代表分钟、免责声明和采用提示;P3 断言默认投影保留决策字段与完整inference_state,并移除非当前 probe 与非活跃宫位表。提交前运行 TypeScript、改动文件 ESLint、rectification/agentic 回归及全量测试;真实线上模型 74 秒路径耗时未在本轮复测。 - 防复发:UI 承载只看服务端状态,不得绑定模型工具 receipt;ready-to-adopt 的确定性正文必须写区间、代表分钟和采用提示。非终态轮次必须有
current_question或真实可采用承载;所有承载判断只看焦点与 decision 字段,不得用正文文本判断。不得为无选项问题新造视觉容器或复用选择卡样式,不得引入语义正则、关键词表或 A/B/C/D 位置推断;不得改变 confirmation gate、resolved/declined语义或 Skill10.0.13。 - 相关记录:BUG-441、BUG-449、BUG-450、BUG-452
- 复发自:BUG-450
- 修复版本:待发布
BUG-454 | 新增后台页面未同步 capability audit 路由契约
- 状态:resolved
- 首次发现:2026-08-31
- 最近更新:2026-08-31
- 影响面:
backend-quality-gate.yml的 Python quality gate、_capability_audit()应用路由扫描 - 用户现象:推送
53450f5bf362c85d8b3bfbae7f2ab724e30f3208到staging后,quality gate run2217的validate失败,部署未继续。 - 触发条件:新增
frontend/src/app/admin/feature-pricing/page.tsx与frontend/src/app/admin/pricing-simulator/page.tsx后,运行tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources。 - 根因:
_scan_app_routes()按文件系统扫描真实page.*路由,而测试在tests/test_api_server_security.py中维护旧的硬编码完整列表,未加入两个新路由。 - 修复:在路由快照断言中加入
admin/feature-pricing与admin/pricing-simulator,保持测试与真实页面树的排序结果一致。未修改 workflow 配置或路由扫描逻辑。 - 验证:针对性 capability audit 测试通过;随后执行 quality gate 关注的 Python 测试集合,确认原失败断言已消除。
- 防复发:新增或删除前端
page.*路由时,必须同步 capability audit 的路由契约测试;quality gate 失败时先查看失败测试,不把下游publish的附带状态当成根因。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-455 | 定价配置异常被通用运行失败吞掉
- 状态:resolved
- 首次发现:2026-08-31
- 最近更新:2026-08-31
- 影响面:POST
/api/rectification/agent、POST/api/consult、POST/api/reports、FeaturePricingError错误映射 - 用户现象:环境没有发布对应 feature/model tier 定价时,生时校正显示通用“暂时不可用,请稍后再试”,咨询和报告也只落入通用 503;文案暗示重试,但实际需要运维发布定价配置。
- 触发条件:
resolve_feature_pricing返回feature_pricing_missing、feature_pricing_model_unavailable或其他 fail-closed 定价错误。 - 根因:三个付费入口没有保留
FeaturePricingError.code;生时校正把 reserve 异常统一改成billing_unavailable,随后又未在外层显式映射而落到run_failed,consult/reports 则由通用 catch 吞掉。 - 修复:三个入口均只把
FeaturePricingError.code写入脱敏服务端日志;生时校正保留内部feature_pricing_*reason,并统一映射为公开billing_unavailable;咨询和报告统一返回pricing_configuration_unavailable。用户文案明确“计费配置不可用/尚未完成,请联系支持”,不返回内部错误原文。 - 验证:新增三入口路由契约,断言内部 code 被保留、公开错误不落入
run_failed、响应不包含error.message;TypeScript、ESLint、billing/consult/report 聚焦回归通过。rectification 聚焦套件705/705;全量2368/2369,唯一失败为默认python3缺少 PyYAML,指定已有 PyYAML 的解释器后原失败文件39/39通过。真实 staging 定价行与日志因本地无环境凭据未验证。 - 防复发:付费入口必须把配置缺失与瞬时运行错误分开;内部日志记录稳定 code,公开响应只使用安全错误码和可执行文案。部署完成不等于定价可用,仍须通过 admin flow 发布当前环境实际 model tier 的定价。
- 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-456 | opening 成功后未持久化当前问题槽
- 状态:resolved
- 首次发现:2026-08-31
- 最近更新:2026-08-31
- 影响面:POST
/api/rectification/agentopening 成功路径、persistNextInterviewIfIdle、ensureNonTerminalTurnExit - 用户现象:开场回复正文看起来提出了问题,但服务端
current_question仍为空,页面显示“当前没有可回答的问题,正在等待服务端更新”。 - 触发条件:新 Case 执行
action=opening且 Agent 没有自行调用可持久化 focus 的工具。 - 根因:成功轮后的问题槽持久化与非终态出口检查都被限制在
action === "message";opening 只保存散文回复,没有建立服务端拥有的可回答问题槽。 - 修复:成功的
opening与message一起执行persistNextInterviewIfIdle;随后执行ensureNonTerminalTurnExit,在仍无问题且没有已确认、用户停止或真实可采用承载时确定性补出问题。read_only保持无副作用,不纳入该分支。 - 验证:回归锁定 opening/message 共用问题槽持久化与非终态出口路径,并以真实
persistNextInterviewIfIdlefixture 断言投影后的current_question非空;TypeScript、ESLint 与 rectification 聚焦套件705/705通过。未进行真实 staging opening smoke。 - 防复发:任何会启动或推进 Case 的成功非终态轮都必须以服务端状态结束:存在
current_question或真实可采用承载;不得以模型正文是否包含问句代替结构化状态。 - 相关记录:BUG-452、BUG-453
- 复发自:BUG-452(非终态出口只覆盖 message,未覆盖 opening)
- 修复版本:待发布(Skill 保持
10.0.13)
BUG-457 | 功能定价草稿保存成功但列表为空且菜单入口被隐藏
- 状态:resolved
- 首次发现:2026-08-31
- 最近更新:2026-08-31
- 影响面:
/admin/feature-pricing、/admin/pricing-simulator、GET /api/admin/feature-pricing、后台功能定价可读性 - 用户现象:管理员保存定价草稿后接口没有报错,但列表仍为空;左侧菜单找不到“功能定价”;功能和模型档位只显示内部英文键,非研发人员无法判断含义。
- 触发条件:以
admin_runtime读取启用 RLS 的public.feature_pricing;或由 Refine 根据resourcePermissions生成后台菜单;或打开功能定价、定价测算列表。 - 根因:建表迁移只向
admin_runtime授予了SELECT,没有为启用 RLS 的表创建读取策略,因此安全定义者函数可以保存草稿,但后台直查被 RLS 静默过滤为 0 行;同时feature-pricing和pricing-simulator未加入菜单权限映射;界面直接渲染稳定内部键值。 - 修复:新增向前迁移,为
admin_runtime添加feature_pricing只读 RLS 策略;补齐两个 Refine 资源的权限映射;统一提供中文标签并保留括号内原始键值,供功能定价和定价测算共同使用,同时把草稿、已发布、已停用状态本地化。 - 验证:契约测试锁定菜单映射、中文标签/原始值以及只读 RLS 策略;本地 PostgreSQL fixture 插入
rectification/standard草稿后,以admin_runtime成功读回该行;TypeScript、ESLint 与聚焦测试通过。 - 防复发:任何后台运行角色直查启用 RLS 的新表时,表权限和对应只读 policy 必须成对交付并用该真实角色查询验证;新增 Refine resource 必须同步
resourcePermissions;后台稳定键值必须显示可读标签且不得改变提交值。 - 相关记录:BUG-454、BUG-455
- 复发自:无
- 修复版本:待发布
BUG-458 | 订阅续费测试依赖固定时钟导致 staging quality gate 过期失败
- 状态:resolved
- 首次发现:2026-08-31
- 最近更新:2026-08-31
- 影响面:
frontend/tests/database-billing-admin.test.ts、Giteabackend-quality-gatestaging validate job - 用户现象:功能定价修复推送后 workflow run
2232的validate失败,后续publish、迁移和部署未执行;前端全量测试2370/2371通过,仅数据库计费契约失败。 - 触发条件:CI 在
2026-08-31 10:00 UTC之后执行订阅续费测试;测试把上一份月订阅结束时间和下一份订阅期望开始时间固定为该时刻。 - 根因:测试期望没有遵守
settle_order的真实契约greatest(v_now, max(existing_subscription.ends_at))。当数据库当前时间晚于写死的旧订阅结束时间时,新订阅会正确地从当前时间开始,固定字符串断言却仍期待过去时间。 - 修复:将已有月订阅结束时间设为相对当前时间的一天后,并直接断言续费订阅的
starts_at等于上一份订阅的ends_at、ends_at等于starts_at + interval '1 month';不修改正确的计费函数。 - 验证:
database-billing-admin.test.tsPostgreSQL 聚焦测试1/1通过;使用已安装 PyYAML 的 Python 解释器运行前端完整测试2371/2371通过;远端 workflow 结果另行记录。 - 防复发:涉及
clock_timestamp()、now()或续费基准的契约测试不得把当前日期附近的绝对时间作为期望;应断言记录间关系和周期不变量。 - 相关记录:BUG-455、BUG-457
- 复发自:无
- 修复版本:
test(billing): make renewal timing deterministic
BUG-459 | Skill 升版身份同步不完整:SHA 目标目录易错且身份分散在五处
- 状态:resolved
- 首次发现:2026-08-31
- 最近更新:2026-08-31
- 影响面:
skills/skill-package-registry.json、skills/jyotish-birth-time-rectification/**、RECTIFICATION_SKILL_VERSION、frontend/tests/skill-registry.test.ts、openRectificationCase - 用户现象:升版后新建 Case 直接失败(
skill_registry_version_mismatch),或skill-registry.test.ts在发布门禁处失败。 - 触发条件:升级 rectification Skill 版本时只改了部分身份位点;或对 Skill 根目录而非
versions/<version>/计算 SHA256。 - 根因:Skill 身份分散在五处——版本目录
SKILL.mdfrontmatter、根目录SKILL.md副本、registry 条目的version/sha256/packagePath、RECTIFICATION_SKILL_VERSION常量、测试内硬编码 SHA。registry 的packagePath指向versions/<version>/,但根目录存在同名SKILL.md副本,容易让人误以为 SHA 应对根目录计算;根目录含versions/整棵子树,对它计算会把全部历史版本吃进哈希,且能正常返回一个值、不会立刻报错。openRectificationCase(v9/case-service.ts)校验 registry active 版本必须等于RECTIFICATION_SKILL_VERSION,任一处不同步即导致新建 Case 整体失败。 - 修复:升版按固定顺序执行——先建
versions/<new>/并改完全部字节(含 frontmatter 与根目录副本),最后统一对versions/<new>/调用computeSkillPackageSha256(),再回填 registry 与测试。10.0.13 → 10.0.14 按此流程完成。 - 验证:对
versions/10.0.14/实算 SHA 与 registry、skill-registry.test.ts三处一致;registry active 条目唯一;根目录与版本目录SKILL.md字节一致;rectification-*与skill-registry套件 730 项 0 失败;tsc --noEmit通过。未进行真实 staging smoke。 - 防复发:SHA256 只能对 registry
packagePath指向的目录计算,绝不对 Skill 根目录计算;改字节与算 SHA 不得交错,必须先定稿再统一重算;升版必须在同一变更内同步全部五处身份位点;skill-registry.test.ts是发布门禁,不得跳过。存量 Case 绑定各自的skill_version,resolveExact对deprecated放行,因此升版不需要数据迁移,但旧 Case 不会获得新版 prompt 约束。 - 相关记录:BUG-456
- 复发自:无(提交
797a423a曾出现同类的"改了 Skill 字节但未刷新 registry hash",当时未单独立项) - 修复版本:Skill
10.0.14
BUG-460 | 非终态轮缺少统一出口闸导致 answer_choice 与消息早退进入无问题死路
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:POST
/api/rectification/agent的opening、message、answer_choice、stop_and_review推进轮,decideRectification的 probe 耗尽转向,以及服务端问题在 turn 历史中的可见性 - 用户现象:真实校正会话已有多条证据、多个领域并答完多道区分题后,仍保持正确的
can_adopt=false,但current_question=null,界面永久显示等待服务端更新。 - 触发条件:最后一步走
answer_choice,或message命中任一 HTTP 200 确定性早退;同时普通 discriminator 已耗尽、候选分离不足,原决策链直接返回区间而未继续尝试 holdout、带日期收集或 Nakshatra 边界题。 - 根因:BUG-456 只在当时命中的 opening/message 分支附近补了兜底调用,没有建立所有推进轮共享的后置条件;后来新增或既有的 structured choice 与多个消息早退仍可绕过。选择题历史另以「接下来请点选下面这一问。」代替服务端 focus prompt,导致回看时看不到真实问题。
- 修复:所有成功的推进轮统一经过 awaited 公共出口闸;immediate 路径必须等 focus 持久化完成后才返回 HTTP 200,stream 路径必须在
done前完成。read_only显式无副作用。闸门最终重读 Case 并验证current_question || canAdopt || 已终态,无法建立后置条件时 fail-closed。probe 耗尽按普通 discriminator → holdout → dated collect → Nakshatra 边界题 → 明确区间出口转向,所有路径继续使用既有 delivery capability,事故 Case 的canAdopt=false不变。服务端 focus prompt 直接写入 turn 历史,不让模型复述。 - 验证:任务 0 三条不变量覆盖四种推进 action 与 answer_choice 响应可见时序;上一轮五条收紧不变量继续通过;rectification、skill registry 与 TypeScript 最终数字见本次交付报告。未进行真实 staging smoke。
- 防复发:新增 action 必须先加入穷举的 execution-kind 映射,否则 TypeScript/结构契约失败;所有 HTTP 200 推进出口只能在公共闸门之后可见,所有流式成功出口只能在公共闸门之后发送
done。不得用正文、问号或 A/B/C/D 文本推断当前问题,也不得通过放宽采用门槛消除无下一步状态。 - 相关记录:BUG-459
- 复发自:BUG-456
- 修复版本:待发布
BUG-461 | 口述采集卡重复助手正文,选择题卡题干与气泡不对齐
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
rectification-agentic-chat当前问题槽、RectificationChoiceCardlegend、.rectification-question-slot对齐 - 用户现象:口述采集时卡片把助手已经说过的题干再显示一遍,并多出灰色提示「请在下方输入框回答…」。选择题卡在未点选时与助手气泡左缘错位,点选后回看才对齐。
- 触发条件:当前问题是
collect_spoken;或当前问题是选择题且卡片仍在问题槽、尚未变成消息附件。 - 根因:BUG-449/460 已把服务端题干写入助手正文,BUG-441/452 也禁止为无选项问题新造视觉容器,但后续测试把口述采集卡和可见 legend 锁成了必有。问题槽是消息列表的兄弟节点,没有
--assistant-content-inset;已回答卡片在.rectification-message-wrap内,所以只有回看才对齐。 - 修复:口述采集不再渲染第二块视觉卡,题干只留在正文,
collectSpokenPrompt只驱动输入框占位。选择题 legend 改为sr-only。问题槽与助手内容使用同一 inset,且不给槽内选择题再套一层 inset。 - 验证:
rectification-spoken-collect、rectification-agentic-entry、rectification-varga-style-copy改为锁新行为;口述采集不得再出现__spoken/__prompt/aria-describedby。未做登录后的真实页面点选。 - 防复发:不得为
collect_spoken新造视觉容器或复用选择卡样式;选择题可见题干不得与助手正文重复;问题槽与已回答卡片必须共用--assistant-content-inset。不得用正文文本判断助手有没有问出来。 - 相关记录:BUG-441、BUG-449、BUG-452、BUG-460
- 复发自:BUG-441
- 修复版本:待发布
- 后续复发:BUG-471
BUG-462 | 区间出口采集撞上开场题号后,成功计费轮被误报 run_failed
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
exhaustionSpokenCollectFollowup、persistCollectFocus、POST/api/rectification/agent流式成功出口 - 用户现象:助手已经记下带年份事件并完成计费后,界面同时出现「回答未完成,已保留现有内容;本次不会扣点。」和「生时校正暂时不可用,请稍后再试。」
- 触发条件:非收敛区间出口后需要再问口述采集,且开场通用采集题号
collect:unknown:collect_method_evidence已作为历史行存在;或职业/健康压力等域不在 SQLtarget_domain白名单里。 - 根因:耗尽采集只轮转家庭/升学/财务后就退回
domain: null,题号与开场题相同。unique (case_id, question_id)含已结束行,落库报focus_idempotency_conflict。出口闸因此认为没有下一步并抛agentic_rectification_nonterminal_exit_missing。该异常发生在billing.settled与run.completed之后,被映射成run_failed;前端只要看到error就把本轮标成失败并提示不扣点。 - 修复:耗尽采集在家庭/升学/财务之后问职业,再问健康压力/搬家/事业/感情,最后用
domain: "other"。职业落库映射为other,健康压力映射为health。同一题号冲突时改写questionId:next。流式result.ok后出口闸失败只记日志,仍发送done,不再发run_failed。 - 验证:
rectification-range-offer-deadend锁职业与other回退;rectification-server-focus锁域映射与:next重试;rectification-spoken-collect锁成功出口不send error;rectification-collect-stall冲突路径改为两次落库。未做登录后的真实页面复现。 - 防复发:耗尽回退不得再用
collect:unknown:*。SQL 不允许的域必须在落库前映射。流式成功轮在计费完成后不得把出口闸失败发成用户可见的run_failed。出口闸仍须在send done之前 awaited。不得放宽canAdopt。 - 相关记录:BUG-450、BUG-452、BUG-460
- 复发自:BUG-460
- 修复版本:待发布
BUG-463 | 采用门把分离与 holdout 绑死导致引擎可出牌时用户无法采用
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
deliveryCapability/decideRectification、GET case overlay、persistNextInterviewIfIdle、候选卡采用 RPC 投影 - 用户现象:多条可评分证据、多领域、区分题已答完,引擎 receipt 已允许 acceptance/selection/propose,界面仍是
can_adopt=false、没有时间卡、不能保存,并继续轮询财务/职业等采集题。 - 触发条件:方法覆盖(含职业确认或拒答关闭)已满足,候选领先不足
MIN_SEPARATION_LEAD,或 holdout 未通过/不可用;引擎四门自洽且confirmation_allowed=false。 - 根因:策略死结,不是实现回归。引擎 receipt 与 Skill
10.0.14都把分离/holdout 留在唯一分钟确认门,交付物是代表分钟加可信区间。TypeScriptdeliveryCapability却把这两项写进canAdopt,overlay 再与引擎取交集,于是引擎的“可以”永远被 TS 盖成“不行”。上一轮TASK-rectification-nonterminal-exit-20260901.md与 BUG-460/462 的防复发写了“不得放宽canAdopt”;该红线已由产品负责人于 2026-09-01 显式推翻,本条记录授权变更。 - 修复:
locallySelectable只保留候选存在、方法覆盖完成、以及证据下限keep_collecting。分离充分、holdout passed 与引擎confirmation_allowed只进入canConfirmExactMinute。覆盖完成后 idle 持久化不再追问,GET overlay 放出候选卡;采用 RPC 仍读引擎 snapshot 的selection_allowed,写入accepted不写confirmed。 - 验证:事故形状 1a–4 与交付 B1–B3;授权组断言(分离不足/holdout/并列/
offer_provisional_range不采用)改为 provisional 采用且确认门仍关;证据下限、引擎 ceiling、确认门、非终态出口闸与read_only保持全绿。rectification-*.test.ts与skill-registry.test.ts最终数字见本次交付;./node_modules/.bin/tsc --noEmit必须通过。未进行真实 staging smoke。 - 防复发:
canAdopt/selectionAllowed/proposeAllowed/canConfirmExactMinute只在deliveryCapability计算并经...capability展开,出口不得覆写。不得把分离或 holdout 重新绑回采用门。不得放宽确认门、表达边界、3/2 证据下限或引擎 ceiling fail-closed。不得 bump skill 版本或改 Python 引擎来绕过本条。 - 相关记录:BUG-456、BUG-460、BUG-462
- 复发自:无(授权策略变更,不是同一实现回归)
- 修复版本:待发布
- 后续复发:BUG-472
BUG-464 | 客户端全量覆盖 chat_sessions.messages 导致列表膨胀、写满失败与双标签页丢消息
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
GET /api/sessions、GET/PATCH /api/sessions/[id]、POST /api/consult、append_consultation_question、首页会话列表与对话区 - 用户现象:会话一多首页变慢;对话变长后发问提示“问题保存失败”;两个标签页各问一句,刷新后只剩后写的那一轮;中止或失败的半截回答会留下可刷新的记录。
- 触发条件:打开带多段对话的账户首页;长会话继续发问;同一会话在两个标签页交替发送;停止生成或 run.failed 后刷新。
- 根因:
chat_sessions.messages由浏览器整包 PATCH 覆盖,列表 GET 又把全部消息一次性带回。服务端结算已经 append assistant,客户端再 last-write-wins 覆盖。列表无分页、无上限。 - 修复:列表 GET 去掉
messages;详情 GET 按会话加载。咨询在 reserve 成功后由append_consultation_question写入用户提问,满 200 条或 200,000 字符返回session_full并退回预扣。consult 忽略请求体history,改读库内最近 12 条。客户端persistSession只写元数据;旧 bundle 的messagesPATCH 接受并忽略。中止与失败的部分回答只留在当前页。 - 验证:合同测试锁列表/详情拆分、伪造 history 不进模型上下文、PATCH 忽略 messages、SQL 幂等与
session_full、双 request_id 可叠加。./node_modules/.bin/tsc --noEmit、npm test与npm run test:db必须通过。 - 防复发:不得把
messages加回列表 GET 或校正 agent select。不得恢复客户端 transcript PATCH。consult 不得再读取parsed.data.history。append_consultation_question必须保持 advisory lock、request_id 幂等与满员拒绝。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-465 | 会话选择只活在内存里,刷新、后退和登录回跳都会丢掉当前对话
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:首页
activeSessionId、浏览器历史、redirectToLogin后的登录回跳 - 用户现象:无论当前在哪个对话,刷新后回到最近一条;会话间切换后按后退会直接离开站点;登录过期后再登回来落在空白首页,对不上刚才那条对话。
- 触发条件:在侧边栏点开非默认会话后刷新;连续切换几个会话后按浏览器后退;带着
?c=会话地址遇到 401 被送去登录。 - 根因:
activeSessionId只存在 React state。地址栏没有会话痕迹,登录页的successPath也只允许回到/或/admin。 - 修复:合法会话 id 写入
?c=。用户主动切换、新建、打开校正时pushState;删除/归档当前会话或伪造 id 时replaceState。默认选中和生成恢复不写 URL。popstate复用selectSession且不再二次 push。401 把当前?c=暂存 sessionStorage,登录落回/后启动逻辑读回并清暂存。登录页零改动。 - 验证:合同测试锁启动读参、默认不写 URL、popstate 不二次 push、删除清死链、401 暂存、首页不用
useSearchParams。./node_modules/.bin/tsc --noEmit、前端测试与next build的/○ Static必须通过。 - 防复发:首页不得为读 query 引入
useSearchParams或服务端searchParams,以免/从 Static 退化。不得给登录页加开放重定向next参数。默认选中不得pushState。 - 相关记录:BUG-464
- 复发自:无
- 修复版本:待发布
BUG-466 | 星盘库、合盘历史和会话置顶/归档仍有一份会骗人的本地真相
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:星盘库保存/删除、合盘历史、会话置顶与归档、
chat_sessions列 - 用户现象:云端保存失败仍提示已保存,刷新后记录消失;云端删除失败后刷新会复活;合盘历史按 id 并集,服务端删掉的记录会从本机回来;置顶和归档只活在这台浏览器里,换设备全部归零。
- 触发条件:断网或接口失败时保存/删除其他人的星盘;云端删除合盘历史后刷新;在一台设备置顶或归档后再换设备打开。
- 根因:星盘库和合盘历史同时写 localStorage 与云端,启动时又用云端替换或并集覆盖;置顶/归档只有
jyotisha-session-controls本地键,没有账户数据。仓内曾用20260718100000_repair_missing_chart_profiles.sql与20260718101000_repair_missing_synastry_reports.sql修过同类双份真相。 - 修复:星盘库与合盘历史只信云端列表;写失败保留表单或当页报告并明确报错,不再写本地副本。启动清除旧库/历史键。
chat_sessions增加pinned、archived_at,置顶/归档走既有 PATCH;本地控制键一次性导入后删除。每日星语缓存与activeChartId不动。 - 验证:合同测试锁保存/删除失败不再写本地、合盘不再并集、列表/详情带上两列、PATCH 元数据接受
pinned/archived_at、旧messagesPATCH 仍忽略。npm run test:db重放加列迁移并验证 RLS。./node_modules/.bin/tsc --noEmit、前端测试与next build的/○ Static必须通过。 - 防复发:不得把
jyotisha_chart_library/jyotisha_synastry_history/jyotisha-session-controls写回作为权威存储。云端写失败不得再提示已保存到本地。不得把业务迁移复制进frontend/db/migrations。列表 GET 仍不得带回messages。 - 相关记录:BUG-464、BUG-465
- 复发自:无
- 修复版本:待发布
BUG-467 | 引擎算出的解读事实被报告提取层全部丢弃,claim card 只剩执行回执
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
buildReportEvidencePacket、buildReportEvidenceBundleV2的 claim card 构造、filterReportEvidenceBundleForSection、ReportEvidenceBundleV2schema、报告 writer 指令与jyotish-personal-reportSkill - 用户现象:个人报告读起来空洞、各主题高度雷同,说不出这张盘特有的任何东西;报告正文只能围绕"服务器已闭合本主题所需的最低证据组"这类回执文案展开。
- 触发条件:任意一次个人报告生成。
/api/consultation_workflow响应里已经带有解读层,但报告路径不读。 - 根因:结构性矛盾。引擎响应的
chart.ai_prompt_pack.evidence_snapshot.functional_benefic_malefic、.strength.shadbala_ranking、chart.modules.ashtakavarga.sav、chart.modules.dasha_sub_periods.current、chart.yogas、chart.modules.guided_topics都存在,但提取层只保留行星/宫位/上升、两条 Dasha 时间线、varga 宫位占据和技法执行回执。于是 claim card 的conclusion是模板句、supportingFacts是"XX 已执行并纳入本主题证据计划"。写作端合同又规定 claimCards 是唯一结论来源——结论来源里没有结论,模型只能输出空洞合规文本或悄悄违规自行解读原始盘面(schema 查不出来)。同时写作端拿不到任何解读方法论。 - 修复:
ReportEvidenceBundleV2新增interpretiveFacts(yogas / functionalRoles / shadbalaRanking / savScores+savTotal / currentDasha / convergenceDomains)与themeNarrativeSeeds,均必填、允许为空、保持.strict()、进入 canonical 排序与bundleHash,并对悬空 ref、重复 rank/house/theme、越界文本 fail closed。提取走 allowlist:yoga 分类与功能吉凶角色是闭合枚举,行星过safeCelestialName,SAV 按上升整宫把星座分投影成宫位分,叙事种子逐行过违禁词清洗(含外部供应商名与内部路由词一律丢行)。claim card 的conclusion/supportingFacts改为由这些事实确定性拼装,risks 进counterFacts;assertionLevel推导规则一字未改,只在完全没有种子时把 consensus 压到single_system_inference。filterReportEvidenceBundleForSection按主题裁剪种子与 SAV 宫位,同时让盘面通用层(功能吉凶、yoga、八分力、当前大运)的 receipt 随每章下发。新增frontend/src/lib/report-interpretation-packs/:通用包进cachedSystemMessage缓存前缀,本章主题包放缓存边界之后;包内只写措辞与解释方式,不含路径、内部名词、供应商名与任何用户数据。Skill 升到1.1.0(1.0.0转 deprecated)并写明知识包不是事实来源。telemetry 只补记interpretiveFactCount与knowledgePackCharacters两个数值。 - 验证:先用虚构 smoke 出生数据真实调用本地
/api/consultation_workflow九个主题各一次,把字段存在性写进PROGRESS-report-skill-parity-20260901.md(timing.vimshottari、timing.convergence_top_domains、strength.sav_scores、三个*_narrative在该响应里确实不存在,按缺失处理,未伪造)。frontend/tests/report-interpretive-facts.test.ts用合成夹具锁住每层提取条数、hit:false的候选 yoga 不算成盘、非法行星名被丢弃、超长种子截断、外部供应商行被丢、hash 在字段顺序扰动下不变、越界输入 fail closed、有种子/无种子两种 claim card 行为、consensus 防升级回归、按主题裁剪。frontend/tests/report-interpretation-packs.test.ts锁每主题可解析、单包 ≤3000 字符、违禁词/路径扫描、缓存前缀逐字节稳定。./node_modules/.bin/tsc --noEmit、eslint0 error、next build通过,前端全量tsx --test与基线同一失败集(均为本机缺 Docker/PostgreSQL 的数据库与部署夹具)。 - 防复发:bundle 内自由文本只允许来自服务器自产生成器,用户 question 与模型历史输出一律不得写入。新增字段必须有长度上限、数量上限与字符净化,schema 保持
.strict(),hash 必须覆盖。不得因内容变丰富而提升assertionLevel,不得删改 consensus ≥2 verified 校验。modules.yogas.yogas里hit=false的规则行不是成盘 yoga,不得当证据。知识包只约束措辞,不得成为 bundle 之外新占星断言的来源;包内不得出现路径、内部模块名、引擎/供应商名或 prompt 模板。写作 agent 仍然 no skills / no tools / no memory,章节仍串行。 - 相关记录:BUG-457
- 复发自:无
- 修复版本:待发布
BUG-468 | 填报精确出生时间后咨询仍按未校正范围交互
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
prepareConsultationRoute/serverChartFromProfile、runConsultationWorkflow请求体、toAgentConsultationContext、run_rectification_gate/_build_consumer_context、咨询模型指令 - 用户现象:初始化已填医院记录级精确出生时间,或已付费采用校正时间的用户,咨询时模型仍说没做过生时校正、要按时间范围交互。
- 触发条件:
hospital_record未校正填报分钟进入咨询;或birth_time_status为accepted/confirmed且存在active_birth_time的verified_chart咨询。 - 根因:前端星盘
toolInput不传declared_accuracy/time_source,引擎默认minute+family_clear得到5min且矩阵缺档;mastra 又无条件把rectification.boundary写成not_auto_rectified。 - 修复:按档案真值映射精度字段;
rectified只在accepted/confirmed且有 active time 时声明。workflow 透传引擎 gate 边界。补5min分盘档与分级文案。declared_birth_window/general_no_birth_time与输出守卫不改。 - 验证:任务 0 不变量先红后绿;
consultation-birth-time-mode守卫 9 条保持全绿;聚焦consultation-*/consult-*/chat-*/rectification-*与tests/test_rectification_*.py、tests/test_consultation_birth_accuracy.py。 - 防复发:不得对未采用校正的档案声明
rectified;不得把not_auto_rectified写回每次咨询;不得改输出守卫或 window/general 两档指令来“修好”精度传递。 - 相关记录:BUG-009、BUG-073
- 复发自:无
- 复发自:无
- 修复版本:待发布
BUG-469 | opening 轮把采集题干再写成第二条助手消息
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:POST
/api/rectification/agentopening 成功出口、finalizeSuccessfulTurnExit、口述采集当前问题槽 - 用户现象:开场模型正文已经自然引导用户说一件事,紧接着又出现一条服务端采集题干,两条语义重复、语气断裂。
- 触发条件:新 Case
action=opening,本轮已有非空 assistant 正文,且 focus 为 collect 类。 - 根因:BUG-460 为了让题干进历史,对所有成功推进轮都把
current_question.prompt追加成独立 assistant 消息。opening 的模型正文本身已承担引导,再追加就变成双重提问。 - 修复:focus 仍照常持久化。仅当
action === opening、focus 为 collect、且本轮已有非空 assistant 正文时,不再把题干写成第二条 turn 消息。判定只看 action / kind / intent / 正文是否非空,不读正文内容。 - 验证:
shouldPersistFocusPromptTurn矩阵;既有 server-focus / question-ownership / 非终态出口闸测试保持;agent-voice-copy-contract.test.ts。 - 防复发:不得用正文字符串判断“是否已问过”。非 opening 轮仍把服务端题干写入历史。
- 相关记录:BUG-456、BUG-460、BUG-461
- 复发自:BUG-460
- 修复版本:待发布
BUG-470 | Home 拆分后 eslint 开始分析 page.tsx,staging quality gate 因 16 条 react-hooks 失败
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:Gitea
backend-quality-gatevalidate、frontend/src/app/page.tsx、账户 overlay、首页 lint - 用户现象:向
staging推送124d3990后 quality gate run2280失败。validatejob5496前端测试 2460/2460 通过,随后npm run lint --prefix frontend报✖ 86 problems (16 errors, 70 warnings)。publish因needs: validate跳过。 - 触发条件:含第三批 Home 拆分的提交进入完整 runner 的 frontend lint。
eslint-plugin-react-hooksv7 的 compiler 规则开始分析已缩到约 2000 行的Home。 - 根因:拆分前
Home过大,同一套规则不分析该组件,render 里写 ref、effect 同步 setState、模块级{ current }赋值和Date.now()都不会报错。拆分后这些写法变成 16 个 error:react-hooks/refs、react-hooks/immutability、react-hooks/set-state-in-effect、react-hooks/purity。模块级 holder 是第三批为打通 hook 循环新加的;其余模式在拆分前已存在,只是当时扫不到。 - 修复:循环回调改为
useRef,在产出它们的自定义 hook 体里写入(与已有resumeRectificationSession.current相同)。sessionsRef改在 session hook 内同步。preview改为 state,不再在 render 读uiPreview.current。账户 overlay 改为传入model对象,去掉 render 里累加 epoch / 写modelRef。聊天 actions 改到copyAssistantMessage之后的 effect。effect 里同步 setState 改queueMicrotask。合盘卡片时间改走timestamp()。 - 验证:
npm run lint --prefix frontend0 error;./node_modules/.bin/tsc --noEmit;frontend/tests/account-dialog-overlay.test.ts;相关首页/账户合同测试。 - 防复发:
Home不得在 render 里写ref.current或改模块级{ current };账户 overlay 不得再靠openEpoch+modelRef在 render 里刷 memo;源码合同锁定 overlay 走modelprop、chat actions 只在 effect 里写入。 - 相关记录:BUG-326、BUG-339、BUG-343、BUG-383
- 复发自:BUG-326(render 里写 ref);BUG-339(effect 同步 setState)
- 修复版本:待发布
BUG-471 | 选择题干与口述采集题在直播界面消失
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
rectification-choice-cardlegend、.rectification-question-slot口述题干、shouldPersistFocusPromptTurn、生时纠正直播对话 - 用户现象:助手说「界面上继续有下一问 / 有下一步」,下方没有可见问句;D9/D10 风格卡只有 A/B/C/D,看不到题干。用户无法判断要回答什么。
- 触发条件:Agent 正文只写承接句;
current_question已是choice或collect_spoken;直播send()不重拉 turns。 - 根因:BUG-461 为去掉重复采集卡,把选择题 legend 重新设为
sr-only,并拆掉口述题槽。题干只指望助手正文或第二条 persist turn。Agent 被禁止复述题干,直播又不合并额外 turn,于是两边都空。 - 修复:选择题 legend 恢复可见 prompt。
collect_spoken在问题槽渲染 prompt 段落(不加灰色「请在下方输入框回答…」、不套选择卡样式)。collect 且本轮已有非空助手正文时,不再追加第二条题干消息。不改 Skill 版本,不打开确认门。 - 验证:
rectification-agentic-entry、rectification-varga-style-copy、rectification-spoken-collect、agent-voice-copy-contract。未做登录后的真实页面点选。 - 防复发:有点选卡时题干必须在卡片上可见,不得再把
choice_card.prompt藏进sr-only。口述采集的真源是 GETcurrent_question.prompt,必须在问题槽可见。不得用助手正文字符串判断「有没有问过」。不得恢复灰色 hint。 - 相关记录:BUG-406、BUG-441、BUG-461、BUG-469
- 复发自:BUG-406(可见 legend)被 BUG-461 回退;BUG-461 拆掉口述槽后直播无题干
- 修复版本:待发布
BUG-472 | 职业与财务采集挡住已放行的临时采用卡
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
BLOCKING_COVERAGE_IDS、persistNextInterviewIfIdle、persistNextInterviewAfterChoice、GET overlaycan_adopt - 用户现象:区分题已答完、引擎
accept_allowed=true、区间已收到约 8 分钟,界面没有时间卡。用户说家里「没有」后只看到一句范围旁白,流程像停住。 - 触发条件:带日期的 dasha/D9/D10/relatives 已覆盖或拒答,
occupation仍 uncovered;耗尽采集按 family→education→finance 继续追问。拒答家人走applyCollectFocusDenial→persistNextInterviewAfterChoice,不走 idle 早退。 - 根因:BUG-463 已把分离/holdout 移出采用门,但仍把
occupation留在 blocking 集合。idle 覆盖完成后不再追问,拒答采集路径仍会落财务题。公开层can_adopt=false,前端不渲染时间卡;口述槽当时也是空的。 - 修复:blocking 采用门只保留
dasha_events、d9_relationship、d10_career、relatives。occupation仍可采集,不再挡住canAdopt。persistNextInterviewAfterChoice与 idle 共用同一早退:已可采用且下一步不是采集/区分/holdout 时,不再落下一问。确认门、3/2 证据下限、引擎 ceiling 不变。不 bump Skill,不改 Python 引擎。 - 验证:
rectification-provisional-adopt事故形状 1c 与 after-choice 早退;rectification-eight-method、rectification-coverage-collect、rectification-occupation-coverage-exit、rectification-range-offer-deadend、rectification-collect-stall。./node_modules/.bin/tsc --noEmit通过。未做登录后的真实页面点选。 - 防复发:不得把
occupation、财务或健康重新写进BLOCKING_COVERAGE_IDS。覆盖完成后persistNextInterviewAfterChoice与 idle 都不得再追问 OOS 采集。不得放宽确认门或 3/2 下限。 - 相关记录:BUG-453、BUG-463
- 复发自:BUG-463(idle 不再追问,拒答路径与 occupation blocking 仍挡住采用卡)
- 修复版本:待发布
BUG-473 | 流式回答每个网络事件都全量重渲染,长回答越写越卡
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
use-consultation-run.tsNDJSON 回调、rectification-agentic-chat.tsxsend循环、chat-message-content.tsx - 用户现象:回答越长越卡,文字一坨一坨蹦出来而不是流出来;思考步骤更新时整条消息跟着抖。
- 触发条件:任何一次流式咨询或校正回合,回答超过一两千字后明显。
- 根因:服务端每个模型 text-delta 立即发一条
answer.delta,客户端每条事件各自setStreamingReply/setMessages;每次提交都对整段部分回答跑parseAgentReply、applyThinkingSectionProgress、splitSpokenAnswerAndTechniqueAudit和react-markdown全量 parse,随回答长度呈平方级增长。 - 修复:新增
lib/stream-frame-buffer.ts,所有流式事件先落到累加器,requestAnimationFrame合并成每帧最多一次提交;正文与思考文本按max(2, ceil(积压/12))每帧匀速释放,突发积压约十二帧追平,页面隐藏时退化为 250ms 定时并一次放完,run.completed/失败/中止时同步冲干净。chat-message-content.tsx在流式时把已完成段落(最后一个空行之前,不切进代码围栏、列表项之间或表格)交给按内容记忆的前缀组件,只有尾块每帧重 parse;结算后仍整篇一次 parse。 - 验证:
tests/stream-frame-buffer.test.ts(释放公式、200 token/50 帧只提交 50 次、突发 12 帧追平、隐藏退化、reset/dispose)、tests/chat-markdown-split.test.ts(切分规则、前缀稳定、前缀与尾块分开 parse)、tests/home-streaming-render-split.test.ts新增按帧驱动的渲染次数上限。 - 防复发:流式状态必须经
stream-frame-buffer提交,不得在事件回调里直接setState;流式期间的 Markdown 渲染必须走前缀/尾块切分。 - 相关记录:BUG-474
- 复发自:无
- 修复版本:待发布
BUG-474 | 回答结算瞬间整条消息闪一下,步骤时间线从展开直接跳成折叠
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
chat-transcript.tsx、chat-message-row.tsx、consultation-run-timeline.tsx、globals.css.message - 用户现象:流式回答写完的那一刻,正在读的整条回答淡出再淡入一次;上方「正在分析」步骤块瞬间收成「已完成 N 步」,下方正文向上跳一段。
- 触发条件:任何一次咨询流式结束。
- 根因:流式中的最后一条由
StreamingMessageEntry渲染,结算后由SettledMessageEntry渲染,两者是不同组件,renderKey相同也会卸载重挂,ChatMessageRow的 GSAP 入场在新挂载的<article>上重放;ConsultationRunTimeline用key={live ? "live" : "settled"}强制重挂,原生<details>的open没有过渡;.message上另有一份 160ms CSS 入场关键帧与 GSAP 的 180ms 叠加。 - 修复:
latestAssistantView派生最后一条 assistant 视图(流式或刚结算),LatestAssistantEntry一个组件、一个 key 负责两个状态;历史列表按excludeLatestAssistant排除尾条。删除.message的 CSS 入场与message-enter关键帧,GSAP 时长改为 0.16s 与 DESIGN.md §6 一致。时间线去掉key,改成button[aria-expanded]+grid-template-rows 0fr→1fr180ms 过渡,折叠后内容inert;summary 文案换行时 120ms 淡入。 - 验证:
tests/chat-stream-settle-contract.test.ts(视图 key 跨结算一致、只有一处渲染尾条、.message无 animation、时间线过渡与 reduced-motion、live 空行「正在处理…」);session-conversation-layout与chat-bundle-splitting-contract的 0.18 锁改为 0.16 并注释原值。 - 防复发:尾条 assistant 必须由
LatestAssistantEntry单点渲染;不得给时间线加随状态变化的key;入场动画只允许 GSAP 一份。 - 相关记录:BUG-473、BUG-475
- 复发自:无
- 修复版本:待发布
BUG-475 | 流式期间步骤时间线无法折叠,请求已发出但第一个事件到达前没有任何进行中反馈
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
consultation-run-timeline.tsx - 用户现象:回答生成中点「正在分析」想收起步骤,下一个 token 又把它撑开;发送后到服务端第一条事件之间只有头像和空白。
- 触发条件:流式期间点击时间线 summary;服务端首事件延迟超过一两秒时。
- 根因:
<details open={live ? true : undefined}>由live受控,用户的开合没有进入状态;rows为空时组件直接返回null。 - 修复:
open = userOpen ?? live,用户切换后记入 state,结算时若用户未动才程序折叠;rows为空且 live 时渲染一条QUEUED_TIMELINE_ROW(「正在处理…」+ 行内 spinner + shimmer 文案)。 - 验证:
tests/chat-stream-settle-contract.test.ts。 - 防复发:时间线开合必须以用户选择优先;live 状态下不得渲染空壳。
- 相关记录:BUG-474
- 复发自:无
- 修复版本:待发布
BUG-476 | 生时校正会话的 Agent 活动 UI 与普通会话是两套,live 标记被 14px 格子裁切
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-01
- 影响面:
rectification-agentic-chat.tsx、agent-activity-status.tsx、chat-message-row.tsx、globals.css、package.json(thinking-orbs) - 用户现象:校正会话里步骤标记是一个 20px 的 canvas 小球,被压在 14px 的格子里边缘裁切;结算后步骤列表永远全展开,下面还多挂一个「本轮完成 · N 个步骤」折叠块;失败时消息上方多一行红字;重新生成时先出现一个假的「正在组织回答…」;普通会话则是行内 spinner、结算收成「已完成 N 步」一行。两边看起来不像一个产品。
- 触发条件:任何一次校正回合与任何一次咨询回合并排对比。
- 根因:校正走
activityTrace+AgentActivityStatus的 trace 分支,普通会话走ConsultationRunTimeline;chat-message-row.tsx里并存三条思考渲染路径(timeline、ConsultationThinkingReport+ThinkingStepTree、trace panel),其中 sections report 已无任何调用方可达;.conversation.is-rectification .agent-thinking-marker { 14px }覆写与ThinkingOrb size={20}冲突。 - 修复:新增
lib/rectification-timeline-adapter.ts,把 trace/receipt/activity 映射成ConsultationTimelineRow(tool → calculate 行;失败 tool 标签加「未完成」;回执方法作为最后一条完成行的 sources chips,去重 ≤8;无 live 行时把「正在…」类活动文案作为 live 行,answer-composition 归 write)。校正消息由此走同一个ConsultationRunTimeline。删除consultation-thinking-report.tsx、thinking-step-tree.tsx、completed-activity-receipt.tsx及其 CSS;AgentActivityStatus只保留无 timeline 的兜底,live 标记统一InlineSpinner12px,移除thinking-orbs依赖;删除 14px 覆写;失败态改走既有 error 通知;重新生成显示 queued 行由真实事件填充。校正thinking.delta仍在服务端公开边界丢弃,本轮未动。 - 验证:
tests/rectification-timeline-adapter.test.ts;chat-stream-layout、chat-bundle-splitting-contract、rectification-agentic-entry、rectification-activity-receipt中锁死路径的断言按「注明原值与错因」改为锁新路径。 - 防复发:任何会话面的 Agent 活动都必须投影成
ConsultationTimelineRow交给ConsultationRunTimeline;live 标记只允许InlineSpinner;不得再引入第二种等待词汇。 - 相关记录:BUG-474、BUG-475
- 复发自:无
- 修复版本:待发布
BUG-477 | 生时校正会话的输入框没有字数上限,也没有接近上限的计数
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-02
- 影响面:
rectification-agentic-chat.tsx、chat-composer.tsx - 用户现象:校正会话里可以无限输入;主对话在剩余 50 字时会出现计数并在到顶时变红(BUG-447),校正会话什么都没有。停止按钮、发送按钮、textarea 也是另一份手写的。
- 触发条件:任何一次校正会话输入。
- 根因:校正面用裸
<Textarea>+ 两个<Button>自己拼输入框,没有maxLength,也没有接CharacterRemaining;ChatComposer只会读主会话的composer-draft外部 store,校正面有自己的draftstate,所以一直没复用。 - 修复:
ChatComposer新增可选受控value与remainingId:有value时以其为准(store 订阅仍在组件内,不改主会话行为),校正面传value={draft},不写主会话 store。校正面渲染ChatComposer(maxLength=500,与主对话同上限)并在composer-footer放CharacterRemaining;三段 placeholder、Enter/Shift+Enter/isComposing处理、停止按钮文案原样保留。 - 验证:
composer-isolation-contract(校正传value、不 importcomposer-draft)、character-remaining-contract、composer-ime-contract、rectification-agentic-entry。 - 防复发:不得再手写第二个聊天输入框;新增会话面一律渲染
ChatComposer,自有草稿走value。 - 相关记录:BUG-447、BUG-478
- 复发自:无
- 修复版本:待发布
BUG-478 | 滚动跟随与「跳到最新」在两个会话面各写一份,主会话每个 token 都触发一次 scrollTo
- 状态:resolved
- 首次发现:2026-09-01
- 最近更新:2026-09-02
- 影响面:
use-conversation-scroll-anchor.ts、page.tsx、rectification-agentic-chat.tsx、rectification-sticky-scroll.ts(删除)、globals.css - 用户现象:主会话流式期间每个 token 触发一次
scrollTo,回答结束瞬间再补一次 smooth 滚动,看起来"先跳到底再滑一下";校正面是另一套 rAF 跟随;「跳到最新」按钮两份不同样式(主会话内联 Tailwindshadow-md、校正面--shadow-soft,文案一个「跳到最新」一个「回到最新」)。 - 触发条件:任意流式回答;向上翻阅历史。
- 根因:
page.tsx的滚动 effect 以activeStreamingText为依赖;校正面自带followTailRef/followLatestContent/rectification-sticky-scroll.ts;按钮各写各的。 - 修复:跟随并入
useConversationScrollAnchor:ResizeObserver观察滚动容器的子元素、MutationObserver跟踪子元素增删,anchored 时下一帧scrollTop = scrollHeight;resetKey变化立即落底;不再在结算时补 smooth 滚动。page.tsx的滚动 effect 删除;校正面接入同一 hook(resetKey = caseId),删除rectification-sticky-scroll.ts及其全部胶水。新增JumpToLatestButton(.jump-to-latest,--shadow-elevated、44px、focus ring),两面共用,文案统一「跳到最新」带箭头;page.tsx内联 Tailwind 版与.rectification-jump-latest删除。校正.message-list720px 覆写删除,与主会话同 900px。 - 验证:
chat-notice-and-scroll-contract(follow 在 hook 内、anchored 守卫在赋值前、page.tsx无scrollTo)、chat-navigation-a11y-contract、chat-stream-layout、rectification-agentic-entry、rectification-answer-choice。 - 防复发:任何会话面不得自行监听 scroll 或按内容变化调
scrollTo;跟随只经useConversationScrollAnchor,跳到最新只经JumpToLatestButton。 - 相关记录:BUG-477
- 复发自:无
- 修复版本:待发布
BUG-479 | 首页揭幕后仍有组件级加载:外层加载屏只等三样,推荐问题、今日星语、校正摘要各自转圈
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
page.tsx启动 effect 与揭幕条件、starter-home.tsx、globals.css(.starter-loading)、lib/home-bootstrap.ts(新增)、DESIGN.md§9 - 用户现象:登录后先看一次整页加载屏,进入界面后推荐问题位置再转一次(
starter-loading)、今日星语卡aria-busy并显示「正在写下今天的星语。」、切会话消息区出现 spinner;用户感受是"进来了又在等"。 - 触发条件:任何一次进入首页;切换到消息尚未缓存的会话。
- 根因:
setHydrated(true)只等账户、模型目录、会话列表;推荐问题、今日星语、校正入口摘要三个 effect 都以hydrated为门槛,只能在揭幕之后才发请求,于是各自带一套等待表现。这些请求彼此无数据依赖,串行体验是历史堆出来的。 - 修复:启动分两阶段(
bootstrapPhase: "account" → "prepare")。账户阶段完成后不揭幕,改进入 prepare:三个 effect 改以bootstrapPhase为门槛在揭幕前并行发起,并预热校正分包import();揭幕 effect 在全部适用项就绪(bootstrapPrepareSettled)或 4 秒预算(BOOTSTRAP_PREPARE_TIMEOUT_MS,从进入 prepare 起算)到期时setHydrated(true)。加载屏文案按阶段切换(bootstrapLoadingCopy)。揭幕后删除starter-loading分支、今日星语aria-busy与「正在写下」文案(pending 显示静态「今天的星语还没写出来。」,到达后静默替换)、会话切换的InlineSpinner(保留sr-only文案)。揭幕后按侧栏顺序后台预取最近 5 条会话消息(sessionIdsToPrefetch)。账户阶段失败仍直接揭幕到错误屏;8 秒 bootstrap 超时不变。 - 验证:
home-bootstrap-reveal.test.ts(纯函数 + 源码合同:prepare 门槛 ×3、揭幕延时、预热、预取、揭幕后无 spinner、加载屏 DOM 合同);daily-starlanguage.test.ts/starter-questions.test.ts按例外条款改读新门槛;tsc、eslint0 error、next build/Static、首屏 gzip-9 513570 → 513889 B(+0.06%)。 - 防复发:任何首页数据拉取不得以
hydrated为门槛再在揭幕后展示等待态;新增的揭幕前依赖加入bootstrapPrepareSettled的输入,而不是各自转圈。main.app-loading[aria-busy="true"]/main.app-loading-error选择器被stale-client-recovery依赖,不得改。 - 相关记录:BUG-464(消息按需加载引入的首屏消息等待,流式轮已把当前会话消息前移到揭幕前)、BUG-470(effect 内 setState 规则)
- 复发自:无
- 修复版本:待发布
BUG-480 | 第一道 D9 选择卡只有 A-D、没有题干
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
persistServerOwnedFocus、persistPlanFocus、finalizeSuccessfulTurnExit、选择题choice_frame.prompt - 用户现象:走查第一道 D9 相处方式卡只有 A–D,题干不在卡片上,也不在 turn 历史里。后续点选题走 answer_choice 路径则正常。
- 触发条件:message/agent 路径首次为
distinguish_candidates建 focus;serverOwnedChoiceCopy因空 prompt/period 返回 null,或 turn-exit 只在 idle 新建 focus 时写题干 turn。 - 根因:copy 为 null 时仍可能留下不可渲染 choice;agent
persistPlanFocus没有 spoken collect 回退;turn-exit 在 agent 已建 focus 时跳过题干持久化。 - 修复:copy 为 null 的区分题 fail-closed,回退为
spokenCollectFallbackFollowup,不建空题干 choice。agent 路径同样回退。turn-exit 只要当前问题有 prompt 就按既有结构规则写题干 turn。D9varga_style优先接上探针问句。不改 Python 引擎,不 bump Skill。 - 验证:
rectification-walkthrough-polishD9 不变量与 unwired 回退;rectification-server-focus;agent-voice-copy-contract。未做登录后的真实页面点选。 - 防复发:任何可渲染
choice_card必须有非空 prompt;不得再把空 prompt 的 choice focus 当成功创建。agent 与 idle 创建链都要走同一 fail-closed。 - 相关记录:BUG-460、BUG-461、BUG-469、BUG-471
- 复发自:BUG-471(可见 legend 修好后,空 prompt 创建链仍会丢题干)
- 修复版本:待发布
BUG-481 | 正文说「界面上有下一问」时问题槽还是空的
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
agentic-rectification指令、VOICE.md、流结束后loadCaseSnapshot - 用户现象:助手说界面上已有下一问,用户当下看不到题干。题干 turn 在流结束后的 turn-exit 才写入。
- 触发条件:Agent 正文断言界面当前状态;前端在
done后才刷新 case 快照。 - 根因:指令允许「过渡到界面上的下一步」;模型把尚未刷新的槽位说成已经出现。
- 修复:指令改为中性过渡(「接下来我们继续」),明确禁止断言界面当前状态。时序沿用已有
await loadCaseSnapshot(),不新增question.ready事件。 - 验证:
agent-voice-copy-contract;rectification-walkthrough-polish指令合同。未做登录后的真实页面点选。 - 防复发:正文不得写「界面上有/出现了下一问」。题干真源仍是服务端问题槽。
- 相关记录:BUG-441、BUG-449、BUG-471
- 复发自:BUG-471(直播看不到题干时,模型仍被要求过渡到「界面上的下一步」)
- 修复版本:待发布
BUG-482 | 同域采集问句逐字复读
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
USER_COLLECT_QUESTION_RETRY、spokenFollowupForUser、buildMethodFollowupPlan - 用户现象:「感情这边,还记得哪年认真在一起、分开,或结婚吗?」在历史里逐字出现两次。第一次用工作事件回答后重问正确,但措辞完全一样。
- 触发条件:该域 collect focus 已关闭且未被拒答,覆盖仍未完成,下一问再次采集同一域。
- 根因:采集 copy 每域只有一句;没有用结构化「已问过」状态切换第二措辞。
- 修复:每域增加 retry 变体。
closedCollectFocuses里该域 collect 已建立且 status 不是 declined 时设collect_retry。不做正文字符串匹配。 - 验证:
rectification-walkthrough-polish同域 retry;agent-voice-copy-contract可见文案词表。未做登录后的真实页面点选。 - 防复发:同域重问必须换第二措辞;判定只用 focus 结构化状态,不读正文。
- 相关记录:BUG-028、BUG-087
- 修复版本:待发布
BUG-483 | 采用后仍挂着采用前的采集题,没有切入前事核对
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
persistNextInterviewIfIdle、accept 路由、.rectification-question-slot渲染位置 - 用户现象:采用 04:53 成功后,当前问题仍是采用前的财务采集,显示在候选卡下方、脱离对话流。预期是最多两件前事核对。
- 触发条件:accept RPC 成功,active focus 仍是 collect;idle 见任何 activeFocus 就直接返回。
- 根因:
buildMethodFollowupPlan在 accepted 时会出 reverse_verify,但 idle 被旧 collect 挡住;accept 成功后也不替换 focus。问题槽是消息列表的兄弟节点。 - 修复:
accepted_time存在且当前 focus 不是 reverse_verify/oos 时,先 skipped 关闭再 persist 下一问。accept 成功后调用同一 idle。问题槽改到最新一条消息内、候选卡之后。GET 不作为主变更路径。 - 验证:
rectification-walkthrough-polish采用后不变量;rectification-eight-methodreverse_verify;rectification-spoken-collect槽位合同。未做登录后的真实页面点选。 - 防复发:
accepted_time非空时current_question不得仍是采用前 collect。问题槽必须留在对话流内。 - 相关记录:BUG-117、BUG-120
- 修复版本:待发布
BUG-484 | overlay can_adopt 与引擎出牌/采用不一致
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
deliveryCapability、GET overlaycan_adopt、offer/accept 可执行性 - 用户现象:职业从未被问,overlay 判
can_adopt:false,但 offer 工具与引擎accept_allowed=true仍出牌且采用成功。 - 触发条件:blocking coverage 未齐(含 occupation 路由缺口),引擎 ceiling 已放行采用。
- 根因:
deliveryCapability把methodCoverageAll和训练门绑进locallySelectable。BUG-472 已把 occupation 移出 blocking 集合,其余 coverage 仍挡 overlay 采用。 - 修复:方案 1(对齐引擎)。coverage 从采用门移除,只保留为问询路由优先级。训练门 3/2、
keep_collecting证据下限、引擎 ceiling、确认门不变。canAdopt为真且 nextAction 已是出牌/采用时,仍持久化deferred_followup里的剩余区分题与未覆盖 blocking collect;点选回执不得用采用口播覆盖已写入的下一题题干。职业/财务/已覆盖层的耗尽采集在出牌态仍跳过。不 bump Skill 10.0.14;「职业挡出牌」的 skill 文本偏差记在本条,下次 skill 版本再改。 - 验证:
rectification-walkthrough-polishoverlay 与引擎对齐;rectification-occupation-coverage-exit;rectification-decide-next-action;rectification-range-offer-deadend;rectification-answer-choice点选后仍持久化下一张带年区分卡与家人采集;rectification-decision-authority非终态出口仍等待家人采集写入。./node_modules/.bin/tsc --noEmit与四组测试见当轮记录。未做登录后的真实页面点选。 - 防复发:overlay
can_adopt必须与 offer/accept 实际可执行性一致。不得把 coverage 重新写回采用门。不得放宽确认门。不得因为canAdopt就跳过剩余区分题或未覆盖的 dasha/D9/D10/家人采集。点选回执在下一题已写入时不得改写成采用口播。 - 相关记录:BUG-453、BUG-463、BUG-472
- 复发自:BUG-472(occupation 移出 blocking 后,deliveryCapability 仍用 coverageComplete 挡采用)
- 修复版本:待发布
BUG-485 | 生时校正开场正文与问题槽同时提问
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:POST
/api/rectification/agentopening、openingBrief、.rectification-question-slot__prompt、口述采集直播界面 - 用户现象:开场助手气泡已经邀请用户说一件经历(例如哪年上大学、哪年换工作),气泡下方问题槽再用一句几乎相同的采集题再问一遍。用户发出「2016 年 9 月上大学」后,分析中的时间线下面仍挂着同一句旧题。
- 触发条件:新 Case
action=opening且current_question.kind=collect_spoken;或在该采集题仍有效时提交下一条自由文本。 - 根因:两条规则并行。BUG-471 规定口述题干真源是 GET
current_question.prompt,必须在问题槽可见。开场模型却在current_question落库之前生成正文,opening brief 还写「当前可询问范围」,于是正文自己提问。轮末persistNextInterviewIfIdle再写入GENERIC_COLLECT_QUESTION。shouldPersistFocusPromptTurn只挡住第二条助手消息,挡不住问题槽。采集题槽也没有选择题那样的!busy门,分析中仍渲染旧题。 - 修复:开场流开始前先
persistNextInterviewIfIdle,让 read-case 能看到已持久化题干。opening brief 与系统指令改为:题干由问题槽承担,正文只打招呼、不得提问或举大学/工作/搬家的例子。问题槽口述题干与输入框 describedBy 在busy/ 重生成时隐藏,与选择卡一致。不 bump Skill,不改 Python 引擎,不放宽确认门。 - 验证:
rectification-spoken-collect锁开场先持久化、brief 不再写「可询问范围」、槽位showCollectSpokenPrompt含!busy;rectification-v9-agent锁 opening brief;agent-voice-copy-contract锁开场不得举例提问。 - 防复发:口述采集直播题干只由问题槽展示。开场正文不得并行提问。采集槽与选择卡一样在忙碌时隐藏。不得用正文字符串判断「有没有问过」。不得把题干再追加成第二条 opening 消息。
- 相关记录:BUG-441、BUG-449、BUG-461、BUG-469、BUG-471
- 复发自:BUG-469(opening 正文仍提问)与 BUG-471(槽位必须可见题干)同时生效
- 修复版本:待发布
BUG-486 | 个人报告财富章有 D2/D11 回执却无结构化分盘,终态 parse 整份失败
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
buildReportEvidenceBundleV2、generatePersonalReport、readAllVargaCharts/readVargaHouses、POST /api/reports后台生成、report_schema_invalid - 用户现象:计费配置可用后,完整个人报告创建成功但生成停在失败;列表
status=failed、failureCode=report_schema_invalid。日志内层是final_parse_rejected,进度可到约 85%,财富章可能已写成 ready,事业/婚恋/时机没有章节行。 - 触发条件:请求含财富主题;引擎
modules.varga_full使用D2_Hora/D11_Rudramsa这类带流派后缀的键;工作流把 D2/D11 标成已执行。财富章因此被标成 write,终态文档却没有charts[].id为 D2/D11。 - 根因:两层叠加。提取只把
^D\d{1,3}$当成分盘 id,丢掉D2_Hora/D11_Rudramsa,readVargaHouses也只认 D9/D10。主题计划仍可用 D2/D11 回执闭合财富章。组装后findThemeCoverageViolations要求财富主题必须带结构化 D2 与 D11,最终 parse 失败,公开码压成report_schema_invalid。 - 修复:把文档允许的分盘键归一成 D2/D9/D10/D11/D24(含
D2_Hora等别名),占据星体走 celestial allowlist。write 主题若缺少对应结构化分盘,降成 blocked disclosure,不再组装成会解析失败的文档。终态 parse 失败时把缺分盘与 hash 失配从笼统的final_parse_rejected分出来;日志仍只写 allowlist token,不含用户或模型原文。不放宽.strict()文档 schema,也不改 assertionLevel 规则。 - 验证:
frontend/tests/personal-report-generation.test.ts用D2_Hora/D11_Rudramsa抽出 D2/D11 且财富章成为 claim;frontend/tests/personal-report-generation-v2.test.ts财富 write 只有 D1 时 ready 且财富进 blocked;frontend/tests/personal-report-contract.test.ts财富主题缺 D2/D11 仍拒绝。Gitea run 2298 在report-interpretive-facts.test.ts6 fail:夹具只有 D2/D11/D24 回执、没有varga_full,降级后 wealth/education 卡被拿掉。补上结构化 D2/D11/D24 后该文件本地绿,并锁「仅回执则 demote」。未用登录会话在 staging 重生。 - 防复发:文档分盘 id 必须从引擎
varga_full别名归一,不得只认D2这种短键。财富/事业/婚恋/教育 write 主题缺对应结构化分盘时必须降级 blocked,不得把final_parse_rejected当正常完成。新增分盘别名必须同时覆盖提取与文档CHART_IDS。改REQUIRED_THEME_CHARTS或 demote 后必须跑frontend/tests/report-interpretive-facts.test.ts;期望 write 主题 claim card 的夹具必须带对应结构化分盘,不能只标 machine section used。 - 相关记录:BUG-352、BUG-451、BUG-467、BUG-489
- 复发自:无
- 修复版本:待发布
BUG-487 | 口述采集题干在动作图标下方,刷新后聊天历史丢失
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:POST
/api/rectification/agentopening/evidence、finalize_agentic_rectification_turn、重生成、.rectification-question-slot__prompt - 用户现象:开场助手气泡只打招呼,采集题干出现在点赞/复制/重生成图标下面的问题槽。刷新页面后气泡里没有那句题干,因为它从未写入
assistant_message。 - 触发条件:新 Case
action=opening且current_question.kind=collect_spoken;或同一采集题仍有效时刷新/重进会话。 - 根因:BUG-471 / BUG-485 把口述题干真源放在 GET
current_question.prompt的直播槽。槽位不是 turn 历史。shouldPersistFocusPromptTurn在已有非空正文时禁止第二条助手消息,所以刷新只能看到打招呼。 - 修复:
composeCollectSpokenAssistantText按题干字符串精确身份接到同一条正文末尾。opening/evidence 在finalizeTurn之前 idle-persist 当前题、用answer.delta replace替换直播正文,并把组合文本写入p_assistant_message。重生成同样回贴当前collect_spoken题干。前端仅在最新助手正文尚未带上该精确题干时才渲染采集槽;选择卡仍走问题槽。不 bump Skill 10.0.14,不放宽确认门,不用正文字符串判断「有没有问过」。 - 验证:
rectification-collect-prompt;rectification-spoken-collect锁槽位兜底与composeCollectSpokenAssistantText;rectification-v9-agent锁 finalize 前写入组合正文;rectification-v9-regenerate锁重生成回贴;agent-voice-copy-contract锁服务器接题干并写入聊天历史。 - 防复发:口述采集题干必须出现在同一条已完成助手消息里,刷新后仍在
turns[].text。不得只靠问题槽直播。不得再追加第二条 opening 题干消息。不得用includes/ 正则扫描正文决定是否已提问。 - 相关记录:BUG-449、BUG-461、BUG-469、BUG-471、BUG-485
- 复发自:BUG-485(槽位可见但不是聊天历史)
- 修复版本:待发布
BUG-488 | 口述采集题干仍画在动作图标下方,没有进气泡正文
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
rectification-agentic-chat最新助手气泡、.rectification-question-slot__prompt - 用户现象:开场气泡只有打招呼,采集题干仍出现在点赞/复制/重生成图标下面。staging 已是 BUG-487 的 SHA。
- 触发条件:新 Case opening,
current_question.kind=collect_spoken,GET 已有 prompt。 - 根因:BUG-487 把题干接到
assistant_message并允许槽位兜底。直播正文仍可能只有打招呼(replace 未进客户端 raw、或 GET 后才有 current_question),于是showCollectSpokenPrompt把同一句画在图标下。用户要的是气泡正文,不是槽。 - 修复:不再渲染采集题槽。最新助手气泡按题干精确身份
composeCollectSpokenAssistantText;定稿后的 Case snapshot、发送下一条、以及挂载后的 snapshot 回调把组合文本写进该条消息状态,复制走组合文本。选择卡仍走问题槽。模型仍不自己提问,避免气泡里两句相近问法。不 bump Skill,不放宽确认门。 - 验证:
rectification-spoken-collect锁气泡拼接、禁止rectification-question-slot__prompt;既有 collect-prompt / v9-agent / regenerate / voice 锁保持。staging 门禁 run 2304 因showActivity={bubbleMessage.state…}对不上rectification-agentic-entry源码锁失败;showActivity改回displayedMessage.state(气泡仍走bubbleMessage)。run 2305 前端 2509/2509 后 ESLint 在currentQuestionRef.current = currentQuestion(render)和 snapshotsetMessageseffect 上报react-hooks/refs/react-hooks/set-state-in-effect;ref 改到useLayoutEffect,题干写入改到 snapshot/send 回调。 - 防复发:口述采集题干不得作为动作图标下的兄弟节点。不得用正文字符串判断「有没有问过」。不得同时让模型提问又拼接同一句服务端题干。
- 相关记录:BUG-471、BUG-485、BUG-487、BUG-585
- 复发自:BUG-487(服务端拼接后槽位兜底仍可见)
- 修复版本:待发布
BUG-489 | 个人报告四主题全部 blocked:分盘值形状、Transit 回执与 Chara Karaka 层缺失
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
deriveVargaHousesFromEngine、buildReportEvidenceBundleV2、_attach_local_consultation_layers、personal_fullcareer/marriage/timing/wealth - 用户现象:staging 真实 personal_full 报告四个请求主题全部 blocked,摘要退化为「报告主题尚未生成」。blocked 理由分别为缺 AmK+Transit、缺 DK、缺 Transit、缺结构化 D2/D11。
- 触发条件:个人报告按主题调用
/api/consultation_workflow,提取层组ReportEvidenceBundleV2。与出生数据或线上环境无关,本地 smoke 出生可逐字复现。 - 根因:三层叠加。
modules.varga_full真值是小写ascendant/sign_index/planets字典,提取层只读旧形状Ascendant/sign_idx/ 顶层行星键,所有分盘图提取失败;modules.transits.status=executed存在但从不 upsert Transit 回执;consultation 响应的modules.jaimini只有arudha_padas,没有 Chara Karaka,AmK/DK 回执无法闭合。BUG-486 的45f190a1/643f6f7a只修了键名别名、demote 兜底和手造旧形状 fixture,测试绿、线上仍挂。 - 修复:提取层双形状兼容真实 varga;
transits.status==="executed"时 upsert Transit(partial+executed,缺或非 executed 不产回执);引擎把jaimini.calc_chara_karaka_7挂到chart.modules.jaimini.chara_karakas(不改arudha_padas),前端按 AmK/DK 存在性 upsert 回执。不放宽主题最低证据组,不改既有字段形状。 - 验证:golden fixture 形状契约;
frontend/tests/report-blocked-repairs.test.ts锁双形状、Transit 三态、AmK/DK、四主题 claimCards 4 / blocked 0、九主题全 claim;tests/test_consultation_consumer_context.py::test_local_consultation_layers_attach_chara_karakas_from_jaimini;既有 personal-report generation / interpretive / contract / v2 回归绿。本地tsc --noEmit/ eslint 0 error /next build --webpack/ pytest 28。全量tsx --test2514/2515,唯一失败是既有 Docker 争用 flake,同文件单独 7/7。Gitea gate 2308 与 Deploy staging 2309 成功;https://staging.jyotisha.chat/api/health的deployment.gitCommit=7faf8555a589a0bdf14a5b17323352470816a7ee。登录后的 staging 重生与 writer/token 抽查仍需委托方(本环境 401,无模型凭据)。 - 防复发:分盘 fixture 必须来自真实引擎响应,不得再手造
Ascendant/sign_idx形状当 live 契约。Transit / AmK / DK 回执必须来自模块数据存在性,不得用available_layers名目冒充已执行。新增chara_karakas不得改写既有arudha_padas。改提取形状后必须跑report-blocked-repairs与report-interpretive-facts。 - 相关记录:BUG-486、BUG-467、BUG-352
- 复发自:BUG-486(键名别名与 demote 治标,值形状与缺失层未修)
- 修复版本:
7faf8555(staging;未提升 main)
BUG-490 | 校正题目必须在同一条助手消息里,删除问题槽
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
agentic_rectification_conversation_focuses.asked_turn_id/answer_option、GET/api/rectification/cases/[caseId]turns[].question、rectification-set-focus.spokenPrompt、rectification-agentic-chat、.rectification-question-slot - 用户现象:采集题、区分题、前事核对出现在助手气泡下面的独立问题槽,或刷新后只剩正文。产品要求每一问都是同一条助手消息:正文 → 题干 → 选项;已答原地变灰;刷新从 turn 重建。
- 触发条件:任意校正 opening / message / answer_choice / accept 后仍有 active focus。
- 根因:focus 没有挂到 asked turn,也没有记录所选选项。前端只能从 GET 直播槽或正文拼接恢复题目,所以必须另画
.rectification-question-slot。Agent 被禁止调用 set-focus,题干只能用服务端模板。 - 修复:focus 增加
asked_turn_id与answer_option。GET 按asked_turn_id把question接到 assistant turn。Agent 用rectification-set-focus.spokenPrompt写题干(校验失败两次落模板)。题干和选项画在同一条<article>里。删除问题槽与把题干拼进assistant_message的路径。旧 turn 仅在精确后缀\n\n${prompt}且asked_turn_id命中时 detach。不 bump Skill 10.0.14,确认门仍 fail-closed。 - 验证:
rectification-question-in-message、rectification-spoken-prompt、rectification-spoken-collect/rectification-collect-prompt/rectification-walkthrough-polish/rectification-answer-choice/rectification-decision-authority/rectification-varga-style-copy迁到新合同;rectification-v10-tool-contract锁 Agent 可调用 set-focus,两次invalid_spoken_prompt后retryable=false落模板。 - 防复发:DOM 中不得再出现
.rectification-question-slot。运行时不得用正文字符串判断「有没有问过」。任意 active focus 必须能在某条 assistant turn 的question.focus_id上找到。缺题状态只出现在输入框上方。本题废止 BUG-441/461/471/485/487/488 中与「槽」或「正文不得含题干」绑定的条款;其余条款保留(有选项时题干可见、共用--assistant-content-inset、不得用正文判断状态、开场不得并行提问、不得追加第二条 opening 题干消息)。 - 相关记录:BUG-441、BUG-461、BUG-471、BUG-480、BUG-485、BUG-487、BUG-488、BUG-491
- 复发自:BUG-488(拼接进正文后选择卡仍走槽,刷新合同仍不完整)
- 修复版本:待发布
BUG-491 | message 路径预建 choice focus 不挂 asked_turn_id,刷新丢题干
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
persistNextInterviewIfIdle、finalizeSuccessfulTurnExit、agent-run message 路径预建 choice focus - 用户现象:用户回答采集题后进入区分题时,在线会话也许能看到下一问;刷新后对应助手消息没有题干和选项。
- 触发条件:message 路径上 agent-run 已为下一问建好 choice focus,随后
finalizeSuccessfulTurnExit因已有 focus 早退,不写题干 turn。引入提交约为39c30b55。 - 根因:题干曾依赖第二条 assistant turn 或正文拼接。已有 active focus 时 idle persist 直接返回,不把 focus 链到当前 turn。
- 修复:题干改为 turn 上的
question。idle persist 若已有 active focus,仍用当前turnId回写asked_turn_id。GET 按该列重建。不再需要第二条题干 turn,早退不再丢题。 - 验证:
rectification-question-in-message锁「预建 choice focus + askedTurnId → set_focus.p_asked_turn_id」;GETturns[].question按asked_turn_id连接,禁止按时间戳推断。 - 防复发:opening / message / answer_choice / accept 建立的每个 focus 都必须有
asked_turn_id,且 GETturns[]恰有一条 assistant turn 带该question。不得按asked_at与created_at对题。 - 相关记录:BUG-480、BUG-490
- 复发自:无
- 修复版本:待发布
BUG-492 | 消息路径 set-focus 用 Agent 选项覆盖服务端 choice schema,探针不计分
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
rectification-set-focus、expectedAnswerSchemaFor、projectCurrentQuestion、method-followup Agent hint - 用户现象:答完采集题后的区分题没有 A–D,或点选后候选比较不更新。刷新后同一条助手消息里仍可能只有题干没有选项。
- 触发条件:message 路径 Agent 只传
spokenPrompt(或只传choice.option_a..d、或自写options[].answer_class)调用rectification-set-focus,且next_followup带choice_frame。 - 根因:
d9404976把 set-focus 重新交给 Agent 写题干,但execute仍用parseAgentChoiceCopy(Agent expectedAnswerSchema)建 choice。只写spokenPrompt时 schema={prompt},投影成采集/空题,idle persist 见 active focus 早退,服务端 choice 永远建不出,该探针不计分。Agent 自写answer_class则模型拥有映射,违反选项与计分服务端所有。 - 修复:set-focus 按
next_followup.choice_frame强制expectedAnswerSchemaFor/ collect schema,忽略 Agentchoice。无下一问返回no_pending_question。hint 改为「你只写 spokenPrompt」。同探针再 set-focus 走 prompt 外 identity 幂等。 - 验证:
rectification-question-ownership锁只传 spokenPrompt → 四选项与serverOwnedChoiceCopy相等、probe_id非空、kind===choice;额外 Agent choice 被忽略;采集题collect:true;无下一问不写 focus;第二次 set-focus 幂等且选项不丢。rectification-question-in-message的 choice fixture 补options[].answer_class并断言kind与四选项。 - 防复发:任何路径不得再从 Agent 输入读
choice。choice fixture 必须能过parseAgentChoiceCopy。已知边界:用户不答上一题直接发新消息时,旧 focus 仍挂在旧消息、新 turn 无 question(不变量 1 短暂不成立),旧卡仍可点,属可接受。 - 相关记录:BUG-490、BUG-491
- 复发自:BUG-490(题目进消息后开放 Agent set-focus,未改 schema 所有权)
- 修复版本:待发布
BUG-493 | 题目列表 RPC 失败时 fail-open,聊天里全部题目消失
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
listV10ConversationFocuses、GET/api/rectification/cases/[caseId]question_source、rectification-agentic-chat - 用户现象:迁移未跑或 RPC 权限错误时,历史消息上的题干和选项全部消失,界面只剩「等待服务端更新」,没有失败提示。
- 触发条件:
list_agentic_rectification_conversation_focuses返回 error 或抛异常。 - 根因:列表函数失败时静默
return [],GET 把空列表当成「没有题」,前端无法区分加载失败与真的缺题。 - 修复:失败时
console.warn并返回{ focuses: [], available: false }。GET 带question_source: "unavailable"|"focus"。前端文案「题目加载失败,请刷新。」与「正在等待服务端更新」分开。 - 验证:
rectification-question-ownership锁 RPC error → 列表available===false、turns 上 question 全 null、question_source==="unavailable"、warn 含 case 与 reason。GET 仍走 200 组装路径,不把列表失败提升为 503。 - 防复发:题目加载失败不得再吞成空数组。
question_source为unavailable时不得显示「服务端更新中」。 - 相关记录:BUG-490、BUG-492
- 复发自:无
- 修复版本:待发布
BUG-494 | 题干可省略探针年份,嵌入卡又隐藏 why
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
validateSpokenPrompt、rectification-set-focus、VOICE.md、嵌入RectificationChoiceCard - 用户现象:区分题看起来像「你有没有一段认真开始或结束的关系?」,用户看不到是哪一年,答错年份会记到错误探针上。
- 触发条件:
followup.probe_year或choice_frame.period含年份,但 AgentspokenPrompt不写该年份。嵌入卡variant="embedded"隐藏why。 - 根因:
year_mismatch只在题干已经写出 19xx/20xx 时比对;缺年份直接放过。 - 修复:有
probe_year时题干必须包含该年,否则year_missing;无probe_year但 period 含年份时每个年份都要出现。采集题不加年份要求。VOICE 增加漏写年份坏例。 - 验证:
rectification-spoken-prompt锁year_missing/year_mismatch/ 通过;嵌入卡测试断言question.prompt含探针年份(结构断言,不扫正文)。 - 防复发:区分题题干必须能单独看出年份/期间,不得依赖已隐藏的
why。 - 相关记录:BUG-490、BUG-492
- 复发自:无
- 修复版本:待发布
BUG-495 | 选择题选中项外圈粗描边溢出气泡
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
.birth-time-choice-option:focus-visible、嵌入RectificationChoiceCard、.message-bubble - 用户现象:点选 A 后(或新题自动聚焦第一项时),选项外侧出现一圈粗的红棕色描边,圆角对不齐,看起来像边框溢出。已有选中底色和边框已经够用。
- 触发条件:校正聊天里嵌入选择题;选项在
.message-bubble { overflow: hidden }内。新题会focus()第一项,点击后按钮仍保持:focus-visible。 - 根因:
:focus-visible使用outline-offset: 2px(会话页还有3px),描边画在按钮外面,被气泡裁切。选中态border-color: var(--color-action)与焦点色相同,叠成双层粗边。 - 修复:焦点环改为
outline-offset: -2px画在按钮内。已选中项不再叠加 outline(高对比模式除外)。嵌入卡不再自动聚焦第一项。 - 验证:
rectification-varga-style-copy锁 inset offset、选中无 outline、卡片不.focus()。 - 防复发:选择题焦点环不得使用正
outline-offset。选中态只保留data-selected的边框和底色。 - 相关记录:BUG-490
- 复发自:无
- 修复版本:待发布
BUG-496 | 个人报告写作失败被压成 report_schema_invalid,首章失败后其余主题未开始
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
generateSectionedPersonalReport、createPersonalReportAgent、classifySectionErrorCode、GET /api/reports、GET /api/reports/[reportId]、报告中心失败卡片 - 用户现象:blocked-repairs 部署后,真实
personal_full仍显示生成失败,可见码只有report_schema_invalid。四主题里只有事业章留下失败记录,婚恋/财富/应期未开始。 - 触发条件:章节 writer 输出未通过
assertWriterOutput(常见为evidenceRefs与 plan 集合不等),或输出在 token 预算内被截断;随后整份报告失败。 - 根因:三层叠加。旧错误分类把任何含
evidence的消息打成section_evidence_insufficient,report_writer_evidence_refs_mismatch被误标;章节输出预算用英文「字符 ÷ 2」,standard 上限 1200 字中文只分到 1024 token;一章 blocked 或未捕获抛错可以中止整份,job 停在约 43%。失败详情只有聚合码,页面无法区分截断、引用不齐或未开始。staging 事故窗口的 writer telemetry 在容器 recreate 后丢失,本事故不能写成finishReason=length。 - 修复:中文口径重算每章/摘要
maxOutputTokens,去掉 ÷2;length 修复要求压到字数下限且预算不低于首次。Prompt 要求逐字复制plan.evidenceRefs;repair 只加失败类别。refs/identity 先于泛化evidence分类。单章失败后继续其余主题;all_sections_blocked走统一失败日志。详情与列表聚合已有 section 行,展示可读摘要与错误码,不改表、不放宽 writer schema。 - 验证:预算表锁定 concise 1218 / standard 2128 / deep 3072 / research 3072 / summary 3072。refs mismatch 分类为
section_refs_mismatch且其余主题仍交付。repair 提示含类别词、不含内容。失败详情从 section 行汇总;四章 ready 但整份失败时摘要为「已写成,未完成装配」。tsc --noEmit、改动文件 ESLint、个人报告聚焦测试。staging5c0bec0c真实 standard 四章均 ready(无finishReason=length),整份因onProgress抛错被标成report_schema_invalid;进度投影失败改为不中止装配。 - 防复发:输出预算不得再用字符 ÷ 2。
assertWriterOutput保持 id/theme 全等与 refs 集合相等。section 错误码不得把 refs mismatch 归进 evidence insufficient。一章失败不得中止其余 write 主题。用户可见失败必须有错误码级摘要,不得只展示report_schema_invalid。日志与 PROGRESS 不得写入 prompt、bundle、模型原文或用户资料。 - 相关记录:BUG-352、BUG-451、BUG-486、BUG-489
- 复发自:BUG-451(分章后仍把写作失败压成整份 schema 码,截断预算与失败分类未按中文口径收口)
- 修复版本:待发布
BUG-497 | 采集阶段同时出现采集题和采用卡
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-03
- 影响面:
publicDecisionFields、POST .../candidates/accept、parseRectificationCandidateResult、rectification-agentic-chat、OPENING_COLLECT_DOMAIN - 用户现象:采集题还在问时,同一条助手消息下面出现三张「采用此时间」卡。开场「随便一件带时间的经历」被跳过或答成搬家后,真正的教育采集题变成「再补一件」,或教育域被当成已拒。
- 触发条件:
session_outcome=collect_evidence,内部capability.canAdopt=true,当前题是collect_spoken。开场题曾以collect:education:collect_method_evidence持久化。 - 根因:
collect()把canAdopt原样投影成公开can_adopt。前端showSelectionCards只靠!showLiveChoiceCard,采集题没有 A–D 卡,采用卡就露出来。v9 删掉 offer 门后,BUG-313 / BUG-323 的防复发条款失效。开场题不是教育题,却以education持久化。 - 修复:公开
can_adopt只在adopt_representative/provisional_range_user_stopped/awaiting_confirmation/validated_range为真(后者是 holdout 已过、ready_to_adopt 的既有语义,不是新开口)。accept 在can_adopt=false时 409;前端再按session_outcome双保险。采集阶段只显示只读范围行,composer 旁提供「先这样」。开场域改为other(idcollect:other:collect_method_evidence);declinedDomains/domainCollectFocusAsked忽略collect:other:开场行。occupation 仍走collect:occupation:。 - 验证:
rectification-adopt-flow-20260902、rectification-decide-next-action、rectification-confirmation-gate、rectification-candidate-result;accept 源码锁 409 在 RPC 之前。rectification-adopt-flow-fix-20260903跳过开场后 exhaustion 仍给 education 且无collect_retry。 - 防复发:
can_adopt只能收紧,不得在collect_evidence/discriminate_candidates/validate_holdout放行。exact_minute_confirmed/completed_with_range不得新增放行。开场题不得占用 education/career/relationship/family,不得为 unknown。不改 DB check 约束。 - 相关记录:BUG-313、BUG-323、BUG-501
- 复发自:BUG-313、BUG-323
- 修复版本:待发布
BUG-498 | 采用后核对题 choice_card 为空导致不可答
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-03
- 影响面:
keepAcceptedFocus、projectRectificationChoiceCard、rectification-set-focus、expectedAnswerSchemaFor - 用户现象:点「采用此时间」后出现核对题,嵌入卡 disabled,点不了 A–D。题干有两个年份时沿用错年;schema 缺
probe_year时从文案反推。 - 触发条件:已采用,active focus 为
reverse_verify:education_style:score,随后 GET / set-focus 重算 followup。schema 无probe_year且题干含19xx/20xx。 - 根因:承接分支用
method_id: active_focus重建question_id,与已持久化 id 不一致;projectRectificationChoiceCard按设计拒绝串题,返回 null。核对题 schema 还被最高增益区分探针盖上 relocation identity。keepProbeYear用题干正则兜底,和「不得用字符串反推状态」同向。 - 修复:
keepAcceptedFocus/out_of_sample_check沿用已持久化questionId、ask_theme、domain和选项。不放宽 choice-card 校验。reverse_verify schema 不盖区分探针。spokenPrompt 含钟点时保留服务端题干以免卡片不可解析。区分题 keep 仍走probe:semantic_key。reverse_verify / oos schema 写入followup.probe_year;keep 只取匹配探针或 schema;缺年则省略。删除题干正则。 - 验证:
rectification-adopt-flow-20260902、rectification-question-ownership采用后续跑 set-focus。rectification-adopt-flow-fix-20260903无 schema 年且无匹配探针时 followup 不带probe_year,method-followup.ts无年份正则。 - 防复发:无 semantic_key 的 followup 必须
focus.questionId === frame.question_id。不得从题干/正文抠年份或其它状态。 - 相关记录:BUG-492、BUG-497
- 复发自:无
- 修复版本:待发布
BUG-499 | 采用卡不沉淀进历史,列表末尾裸按钮
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-03
- 影响面:
rectification-agentic-chat、attachOfferResultToTurns、核对题 skip 文案 - 用户现象:采用后三张卡消失,列表末尾出现「用这个时间看盘」;续跑结束后卡又出现在新消息下,与核对题并存。被关掉的采集题无痕消失。状态条「改选」曾是不可点的
<span>。 - 触发条件:采用后续跑
busy;savedStatus=accepted渲染rectification-consult-handoff。 - 根因:卡永远锚在最新助手消息;采用后的界面动作散落在列表末尾,不跟出卡消息走。「改选」赋了
offerSectionRef但从不清滚动。 - 修复:出卡消息拥有
candidateOffer;采用后原地结算「已采用 / 改选」;状态条承接「改选」;核对题「这题跳过」;被关掉的采集题标「已跳过」。「改选」改为<button type="button">,点击scrollIntoView到出卡消息并闪一次rectification-message-flash。showSelectionCards为 false 时不渲染。「用这个时间看盘」见 BUG-502。 - 验证:
rectification-agentic-entry、rectification-activity-receipt、rectification-adopt-flow-20260902组件源码锁。rectification-adopt-flow-fix-20260903锁「改选」为 button +scrollIntoView。 - 防复发:已采用后不得把卡锚到最新消息;不得再渲染
rectification-consult-handoff。「改选」必须是 button。 - 相关记录:BUG-497、BUG-490、BUG-502
- 复发自:无
- 修复版本:待发布
BUG-500 | 校正运行耗时不可观测
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:
get_agentic_rectification_turn_receipt、tool-service、rectification-compare-candidates、聊天进度文案 - 用户现象:采用后续跑约三分钟,回执只有 tool/status/methods;开场 set-focus 失败原因也看不见。
- 触发条件:compare-candidates 走引擎;set-focus
invalid_spoken_prompt;超过 45 秒仍显示「正在处理」。 - 根因:回执不暴露
started_at/elapsed_ms;失败只有safeErrorCode;进度文案不跟活跃步骤切换;read_only仍可能重跑 vedastro-validate。 - 修复:回执增加每步耗时和 fingerprint detail;compare 记引擎各段;45 秒后按当前工具换文案;minute-sensitive 已
passed时跳过 vedastro-validate,failed仍重试且不重算排名。 - 验证:
rectification-adopt-flow-20260902的 elapsed_ms / 45 秒文案;rectification-eight-method失败校验重试。 - 防复发:失败行不得用 GET 时的
now()计算耗时;确认门语义不因沿用校验而放宽。 - 相关记录:BUG-497、BUG-498
- 复发自:无
- 修复版本:待发布
BUG-501 | 采集阶段关采用卡后无题无出口
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
inspectNonTerminalTurnExit、persistNextInterviewIfIdle、persistExhaustionCollect - 用户现象:采集阶段把题问完后 composer 空着,没有下一问、没有采用卡、也没有「先这样」。
- 触发条件:
session_outcome=collect_evidence,内部canAdopt=true,无 active focus,next_followup/deferred_followup都空。 - 根因:
35e5781e收紧公开can_adopt后,非终止轮出口仍把内部decision.canAdopt当成「已有出口」。persistNextInterviewIfIdle不建题,ensureNonTerminalTurnExit又跳过persistExhaustionCollect。此前漏出的采用卡(BUG-497)掩盖了这个死角。 - 修复:
satisfied与 idle 跳过持久化改看publicCanAdopt(decision)。公开不能采用时走persistExhaustionCollect,兜底采集题自带「先这样」。 - 验证:
rectification-adopt-flow-fix-20260903采集期ensureNonTerminalTurnExit建collect_spoken并打rectification_nonterminal_exit_repaired;adopt_representative对照不建题。 - 防复发:没有可回答问题且用户不能采用时,不得把内部
canAdopt当成非终止轮已满足。公开can_adopt只能收紧。 - 相关记录:BUG-497
- 复发自:无
- 修复版本:待发布
BUG-502 | 状态条「用这个时间看盘」无实际作用
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
rectification-agentic-chat、conversational-birth-time-rectification、page.tsx、use-session-management - 用户现象:采用后点「用这个时间看盘」,只开一个空会话并往输入框塞一句服务端不读的话,与自己点「新对话」无异。
- 触发条件:
savedStatus=accepted,状态条渲染onStartConsultation按钮。 - 根因:采用时 RPC 已写
profiles.active_birth_time;按钮没有刷新档案、不带回原问题,只做startNewChat()+ 填 draft。 - 修复:删除按钮与
onStartConsultation/startConsultationAfterRectification链。状态条改为「已采用 … · 改选」加「之后新建对话即按此时间排盘。」;待回问题横幅改为结束后新建对话再问。onSaved仍refreshAccount。 - 验证:
rectification-adopt-flow-fix-20260903扫描frontend/src;rectification-adopt-flow-20260902/rectification-agentic-entry改为doesNotMatch。 - 防复发:不得再提供「用这个时间看盘」或
rectification-consult-handoff。新对话按verified_chart取采用时间。 - 相关记录:BUG-499
- 复发自:无
- 修复版本:待发布
BUG-503 | 一道题选「一时说不好」就交付
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
uncertaintyHighFromAnswers、decisionBudgetFromInference、composeChoiceNarration、publicNextAction、user-copy - 用户现象:整场只收到一道选择题,选 D「一时说不好」后直接交付几十分钟区间;旁白还说「更新了候选比较」。
- 触发条件:
classified_from=choice的用户答题数为 1,answer_class=unsure。连续两道 D 也会因maxPlateauRounds=2交付。 - 根因:
uncertaintyHighFromAnswers没有样本下限,1 答即1 >= ceil(1/2)。applyProbeOutcome把 unsure 记成low_information,平台期从第一道就开始计数。上游 Stop Rule 是「样本够多且大半答不上来」,实现成了任意一道答不上来。 - 修复:
references/rectification_policy.v1.json增加minUncertaintyAnswers: 3。不足 3 答不停。达到下限后仍用「≥ 一半」判定。平台期在decisionBudgetFromInference用同一下限才统计连续low_information,maxPlateauRounds仍为 2。unsure 旁白改为「已记录。这题先不计分,换一件事问。」appliedInference仍持久化。交付话术在进度句前说明user_uncertainty_too_high/tied_first。publicNextAction只读暴露stop_reason。用户主动「先这样」仍立即交付。 - 验证:
rectification-convergence-budget1 答不停且继续出区分题;3 答 2 unsure 停;4 答 2 unsure 停;2 答连续 unsure 不因平台期停,5 答后连续 2 道 unsure 停。rectification-answer-choiceunsure 旁白与 §0 形状(答 D 持久化下一问、不采用)。agent-voice-copy-contract锁停止原因文案。 - 防复发:不确定度停止必须先过
minUncertaintyAnswers。该数字只在references/rectification_policy.v1.json定义。不得把maxPlateauRounds/ 8 轮 / 10 答外层熔断改掉。不得用题干字符串反推停止原因。 - 相关记录:BUG-463
- 复发自:无
- 修复版本:待发布
BUG-504 | 开场采集题用「再说一件」措辞,用户不知从何说起
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
spokenFollowupForUser、GENERIC_COLLECT_QUESTION - 用户现象:新用户开场招呼后看到「也可以再说一件你记得大概时间的事。」不知道该写什么;招呼段按设计不提问、不举例。
- 触发条件:零证据开场,
OPENING_COLLECT_DOMAIN = other,followupsource=method_coverage。 - 根因:
e8c98c37把开场 domain 改成other后,spokenFollowupForUser按 domain 查USER_COLLECT_QUESTION。other是给已有证据后的补问兜底(「也可以再说一件」),真正开场文案GENERIC_COLLECT_QUESTION只在 domain 查不到时才用。domain→文案查表把兜底文案当开场。 - 修复:
source === method_coverage && domain === other && collect_retry !== true时用GENERIC_COLLECT_QUESTION。文案补上「一件事」和「哪年搬的家」。oos_blind兜底与collect_retry的other仍用原补问。不改OPENING_COLLECT_DOMAIN/ questionId / 记账。 - 验证:
rectification-server-focus零证据开场 spoken prompt 等于GENERIC_COLLECT_QUESTION,不含「也可以再」,含「比如」;exhaustionoos_blind与collect_retry仍走USER_COLLECT_QUESTION.other/ retry。源码锁至少两个具体例子。 - 防复发:开场题文案必须带具体例子;
USER_COLLECT_QUESTION.other只用于已有证据后的补问。 - 相关记录:BUG-501
- 复发自:无
- 修复版本:待发布
BUG-505 | 生时校正面进入时先闪普通对话、再空白、再整面重挂;问题槽用「等待服务端更新」裸文案
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-03
- 影响面:
use-rectification-surface、use-session-management、page.tsx(面板 key、prepare 阶段)、rectification-agentic-chat、starter-home、sidebar-session-row - 用户现象:点首页卡片没有任何反馈;从侧栏进已有校正会话先闪一帧普通对话、再是一块空白面板、记录到了再整面重挂;新建校正第一轮结束时画面闪一下、滚动归零;恢复会话后快照没回来前问题区空着;答完题后 composer 上方是「当前没有可回答的问题,正在等待服务端更新。」之类没有动作、没有时限的文案。
- 触发条件:任何进入校正面的路径(卡片、侧栏、深链/刷新);任何一轮结束到下一题到达之间。
- 根因:
selectSession先setActiveSessionId再 open,rectificationSurfaceOpen在 open 返回前为 false;面板 key 带rectificationTurns.length > 0 ? "ready" : "loading",turns 到达即重挂;面板挂载后自拉快照;问题缺口只有裸文案。 - 修复:
/cases/open后用hydrateRectificationCase一次读取 turns + 快照(与BOOTSTRAP_PREPARE_TIMEOUT_MS同一 4 秒上限)再切会话、写 URL;selectSession对未打开的校正会话不先切;启动时选中的校正会话在 prepare 阶段完成 hydration 再揭幕;key 只剩 session/Case 绑定,后到 turns 经渲染期 prop 调整只填空 transcript;卸载中止流与快照读取。入口卡片脚注静态「正在打开…」、侧栏行「打开中」,不转圈。缺口改为rectificationQuestionGapState:preparing(时间线 live 行「正在准备下一个问题…」+ 2s 定时重拉 ≤2 次)→unavailable(「没有拿到下一个问题。」+ 44px「重新加载」);hydration 超时也走同一缺口。 - 验证:
rectification-surface-contract(一次揭幕、无重挂、prepare 阶段 hydration、静态入口反馈、缺口四态、无「等待服务端更新」);rectification-surface-state(hydration 超时/失败、缺口纯函数、turn 解析);rectification-agentic-entry/rectification-question-in-message/rectification-spoken-collect的旧锁按红线改注。 - 防复发:面板一个会话只挂载一次,turns 与快照都是 prop/state 更新;任何等待必须是时间线 live 行或带动作的状态,不得出现「等待服务端更新」类文案;揭幕后不得出现非生成中的 spinner。
- 相关记录:BUG-479(首页两阶段揭幕)、BUG-473(校正面接入共享时间线)
- 复发自:无
- 修复版本:待发布
BUG-506 | 选择题点击后有空帧并闪下一题卡片;采用候选只有按钮变灰
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-03
- 影响面:
rectification-agentic-chat(send、submitStructuredChoice、acceptCandidate) - 用户现象:点完选项,「正在记录本次选择…」那行消失一帧,下一题卡片闪现又消失,再出现「正在处理…」;点「采用此时间」只看到按钮变灰,页面其它部分静止好几秒。
- 触发条件:选择题
willContinue;采用候选。 - 根因:
willContinue分支删掉 thinking 行、置choiceContinuationPending、finally setPending(false),下一次 effect 才send("read_only")再追加新行;中间那一帧busy=false且快照刚装进新choiceCard。采用流程不追加任何 live 行。 - 修复:
send(action, text, continuation),continuation 复用已有 live 行并跳过 busy 守卫;选择与采用在同一个 async 链里await send("read_only", …, { reuseAssistantRenderKey });busy全程为 true;采用点击即追加「正在采用 HH:MM…」行;choiceContinuationPending与 effect 删除。 - 验证:
rectification-surface-contract「一条 live 行贯穿」;rectification-agentic-entry/rectification-answer-choice的旧锁按红线改注。 - 防复发:后续轮不得经 effect 中转;一条链里
busy不得掉回 false。 - 相关记录:BUG-505
- 复发自:无
- 修复版本:待发布
BUG-507 | 记录为空的已有校正会话进入后永远空白
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-03
- 影响面:
rectification-agentic-chat、rectification-surface-state - 用户现象:某个开场当时失败/未持久化的校正会话,从侧栏再进来什么都不显示,composer 可用但不知道先说什么。
- 触发条件:
initialTurns=[]且服务端对 resumed case 一律should_start_opening=false。 - 根因:面板只在
shouldStartOpening时自动开场,空 turns 无任何 CTA。 - 修复:
rectificationConversationState判empty→ 「这段校正还没有开始。」+「开始提问」(send("opening"),openingStarted守卫;服务端幂等抑制重复 opening,不改服务端)。 - 验证:
rectification-surface-state会话态纯函数;rectification-surface-contract空态源码锁。 - 防复发:无 turns 且不自动开场时必须给起点。
- 相关记录:BUG-505
- 复发自:无
- 修复版本:待发布
BUG-508 | 选择题选中无确认感、记录中只有沙漏光标
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-03
- 影响面:
rectification-choice-card - 用户现象:点了选项只看到底色变化,卡里没有进度,不知道有没有点上。
- 触发条件:任何选择题点击。
- 根因:
is-pending只改cursor: wait;选中项无对勾。 - 修复:选中项加
Check+ 「已选择」;pending 时卡顶一行InlineSpinner+ shimmer「正在记录…」(与时间线 live 行同形)。35688015已把选中态收进按钮、d9404976已内嵌进消息,本轮只补差额;用户选择的回显由内嵌卡的answer_option承担(刷新前后一致),不再另加用户气泡。 - 验证:
rectification-surface-contract。 - 防复发:卡内等待只能是时间线 live 行同形。
- 相关记录:BUG-506
- 复发自:无
- 修复版本:待发布
BUG-509 | 校正盘面首态是一整块空面板
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-03
- 影响面:
rectification-board、rectification-board-model、page.tsx、globals.css - 用户现象:桌面端右侧 18–22.5rem 的面板第一阶段只有一句「补充经历后…」,移动端 peek 同样;用户明明填过出生时间。
- 触发条件:
candidateResult为空。 - 根因:首态不用 profile 的填报时间。
- 修复:
declaredTime由page.tsx从 profile 派生(reportedTime || time,须为H:MM)传入;头部时钟位显示填报时间,正文「填报出生时间 HH:MM」+「回答几个问题后…」;peek「填报 HH:MM」;is-board-empty收窄到minmax(16rem, 18rem)。快照 API 不提供填报时间的宫位表,不造数据。 - 验证:
rectification-surface-state板文案纯函数;rectification-surface-contract。 - 防复发:首态必须显示已知的填报时间;不得为填报时间伪造宫位表。
- 相关记录:BUG-505
- 复发自:无
- 修复版本:待发布
BUG-510 | 采集阶段盘外核对选择题画出 A–D 却点不了
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
projectRectificationChoiceCard、GET/api/rectification/cases/[caseId]choice_card、rectification-agentic-chat内嵌卡 - 用户现象:助手记下带月份的职场事后,下面出现「某年前后,有没有开始一段认真关系、分手或结婚?」和 A–D,点任何一项都没有反应。
current_question已是选择题,choice_card为 null。 - 触发条件:采集阶段已有带年份事件,下一问是
intent=out_of_sample_check的四点选存在性题;schema 有合法 A–D,但没有probe_year。该回合未调用rectification-set-focus,题目由 idle persist 挂到最后一条助手消息。selection_allowed仍为 false(采用门,不是点选门)。 - 根因:keep 路径
forceChoice会尝试重铸choice_frame。缺probe_year时hypothesisFor没有具体时期,frame 为空,GET 按设计把choice_card置空。界面仍从 turn 上的question.options画出 A–D,但liveQuestion要求 GET 卡与focus_id一致,disabled={!liveQuestion},submitChoice在没有choiceCard时直接 return。 - 修复:
projectRectificationChoiceCard在重建 followup 没有choice_frame时,若当前焦点已是reverse_verify/out_of_sample_check且 schema 能解析出四点选,按已持久化文案投影choice_card。口述采集 schema 仍不发卡。未放宽区分题身份校验,未打开selection_allowed。 - 验证:
rectification-choice-card锁定无probe_year的盘外存在性卡仍可投影、口述采集盘外题仍不发卡;rectification-agentic-entry锁内嵌卡disabled={!liveQuestion}且点选要求 GET 卡focus_id一致。 - 防复发:GET 已有合法持久化 reverse_verify / out_of_sample_check 四点选 schema 时,不得因重建缺
choice_frame把choice_card置空。界面画出 A–D 时 GET 必须给出可点的卡。不得把selection_allowed当成 A–D 点选门。不得从题干正则反推年份。 - 相关记录:BUG-416、BUG-431、BUG-498
- 复发自:BUG-431
- 修复版本:待发布
BUG-511 | 公开案例复验因默认岁差改 Raman 而失败,标签与计算不一致
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
tests/run_real_case_revalidation.py、scripts/jyotish_api_server.py岁差回执、账户profiles.ayanamsa、咨询/报告/校正/每日星语请求口径 - 用户现象:发布门真实案例复验
valid=false,gated 通过率掉到门槛以下。同一张虚构盘在旧生产与当前默认下 Moon nakshatra / 首段大运不同。回执上的岁差名有时写成 Lahiri 或 Raman,与该次实际计算不是同一套。 - 触发条件:
80102459把引擎默认改成 Raman 后,参考集调用没有显式锁定 Lahiri;VedAstroayanamsa_policy与部分回执字段仍写死字面量。 - 根因:参考集对照依赖「默认值恰好是 Lahiri」;产品默认改为 Raman 后口径漂移。三处标签用字面量兜底,不读该次请求实际岁差。账户没有用户可选岁差。
- 修复:参考集与夹具大运显式
--ayanamsa lahiri。API 回执与 VedAstro 对照改用该次请求实际岁差。profiles.ayanamsa默认raman,设置里可选四个值;咨询、报告、校正、星盘库、每日星语走resolveAyanamsa。DEFAULT_AYANAMSA_NAME本轮不改。 - 验证:夹具大运日期不变;前端
resolveAyanamsa、账户 PATCH 四值校验、咨询 toolInput 带 profile 岁差、校正快照带岁差但不进 fingerprint;npm run test:db跑迁移。公开案例复验命令见本轮 PROGRESS。 - 防复发:参考集与外部对照必须显式传岁差,不得依赖默认值。回执只显示实际名。岁差不进校正
baselineFingerprint,不重算历史报告。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-512 | 岁差设置组件携带无样式类名导致前端发布门失败
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
frontend/src/components/ayanamsa-preference-field.tsx、frontend/tests/class-name-definition-contract.test.ts - 用户现象:staging quality gate 的前端测试
2609 pass / 1 fail,唯一失败为ayanamsa-preference (src/components/ayanamsa-preference-field.tsx)。 - 触发条件:通用类名契约扫描岁差设置组件。
- 根因:外层
<section>同时使用已有样式类sheet-section和没有 CSS、没有行为用途的冗余类ayanamsa-preference。 - 修复:删除冗余
ayanamsa-preference,保留承担实际样式的sheet-section;不新增空 CSS,也不扩大 allowlist。 - 验证:
cd frontend && npx tsx --test tests/class-name-definition-contract.test.ts。 - 防复发:组件新增项目类名必须有真实 CSS 或明确的无样式行为用途;纯冗余 modifier 直接删除。
- 相关记录:BUG-511
- 复发自:无
- 修复版本:待发布
BUG-513 | Gulika 计算泄漏进程级岁差模式,Raman 请求回执与后续计算可能漂移
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
scripts/gulika.py、scripts/jyotish_api_server.py、scripts/prashna_context.py - 用户现象:请求选择 Raman 时 Gulika 曾按其他岁差返回或污染后续恒星黄道计算;同一常驻进程的下一次计算可能继承错误模式。
- 触发条件:Gulika / Upagraha 计算在已设置另一岁差的进程内调用
houses_ex(..., FLG_SIDEREAL)。 - 根因:Swiss Ephemeris 岁差模式是进程全局状态;Gulika 设置请求岁差后没有恢复调用前模式,API 与 Prashna 调用方也没有始终传入本次请求岁差。
- 修复:
ayanamsa_utils.temporary_ayanamsa在finally恢复前一模式;Gulika 的恒星上升计算统一使用该上下文;API 与 Prashna 显式透传并回执实际岁差。 - 验证:Raman→Lahiri、Lahiri→Raman 两向同进程回归测试均保持调用前 Moon 黄经;模式设置所有权测试锁定只有
ayanamsa_utils.py可直调set_sid_mode;API Raman smoke 回执为raman/Raman。 - 防复发:临时切换进程级岁差必须通过
temporary_ayanamsa;业务模块不得直接调用set_sid_mode。 - 相关记录:BUG-511、BUG-512
- 复发自:无
- 修复版本:待发布
BUG-514 | 专业报告导出测试使用 Node 24 模块 mock 选项,Node 22 发布门加载为空模块
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-03
- 影响面:
frontend/tests/professional-report-reference-route.test.ts、Independent Staging Quality Gate - 用户现象:staging quality gate Run 2362 的前端测试
2627 pass / 1 fail;专业报告导出路由测试报does not provide an export named 'ACCOUNT_BIRTH_SELECT',镜像发布被跳过。 - 触发条件:Gitea 的 Node 22 执行新增的
mock.module(..., { exports: ... })测试;本地 Node 24 会通过。 - 根因:Node 22 的实验性模块 mock 使用
namedExports;未知的exports选项被忽略,生成不含命名导出的 mock 模块。本地只在 Node 24 验证,未覆盖发布门运行时。 - 修复:改用 Node 22/24 均支持的
namedExports,按路由实际别名注册 mock;删除共享出生资料 helper 的 mock,改用真实 helper 和虚构数据库行。 - 验证:Node 22.14 与 Node 24.15 分别运行
tsx --test tests/professional-report-reference-route.test.ts,均为 4/4 通过;Node 22.14 全量前端测试 2628/2628 通过,TypeScript 0 错。 - 防复发:使用实验性 Node API 的发布门测试必须在仓库门禁主版本 Node 22 至少跑一次;模块 mock 不得使用仅新版本接受的选项名。
- 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-515 | 缺少 D11 被误判为 finance 必需证据,完整 Shadbala 的 confidence cap 降为 low
- 状态:resolved
- 首次发现:2026-09-03
- 最近更新:2026-09-04
- 影响面:
mcp_server.py::_collect_strict_evidence、finance strict workflow - 用户现象:财富严格工作流在 Shadbala 分量完整、原本应保持
medium-high置信度上限时,仅因没有 D11 Rudramsa 就返回confidence_cap=low。 - 触发条件:finance 证据包没有
modules.varga_full.D11_Rudramsa,其余财富主判据与完整 Shadbala 证据可用。 - 根因:上游加入
d11_rudramsa后,finance 的missing_evidence过滤表没有同步把它列为非阻断支持项;空 D11 因而被计入缺失的必需证据,触发通用missing -> low降级。 - 修复:把
d11_rudramsa加入 finance 非阻断证据集合。D11 仍可进入证据包,但只作支持项;wealth_promise_strength保持主判据,完整 Shadbala 用例的medium-high断言不改。 - 验证:聚焦回归集合 88 passed(31.71s);模板 validator
valid=true, problem_count=0, template_count=15;本地 quick gate 为 Python CORE 585 passed / 1 skipped、前端 2628 passed、lint 0 errors、Next build 通过,退出 0(575.45s);git diff --check通过。 - 防复发:新增证据字段必须同时声明 required / supporting 角色;支持项不得因缺失进入
missing_evidence,也不得单独压低 confidence cap。 - 相关记录:
TASK-upstream-sync-fix-20260903.md - 复发自:
45d13258/f2241463上游同步批次 - 修复版本:待发布
BUG-516 | 校正工具完成后「正在分析」没有实时步骤,看起来卡住
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:生时校正对话时间线、
rectificationTimelineRows、采集输入栏 - 用户现象:用户回答采集题后,分析面板只显示已完成的「读取校正记录」,标题仍是「正在分析」,没有转圈或闪动的实时步骤,会误以为卡住。采集输入栏下方另有「先这样,先看当前范围」出口。
- 触发条件:校正公开流完成一个工具、尚未开始下一工具或写出回答;同一回合仍处于 thinking。
- 根因:公开流故意丢掉
thinking.delta。工具完成后界面把当前活动改成完成文案,时间线因此没有live行。标题虽带微弱闪动,步骤列表全是对勾。 - 修复:工具完成后把当前活动改成「正在分析…」,并在未结束且没有实时步骤时补一条思考行。共享时间线在
live且全是完成步骤时同样补这条行。去掉采集输入栏的rectification-collect-stop按钮。卡片上的同文案按钮见 BUG-518。 - 验证:
rectification-timeline-adapter、chat-stream-settle-contract、rectification-spoken-collect、rectification-adopt-flow-20260902。 - 防复发:未结束的校正时间线在已有完成步骤时必须仍有一条
live行和 spinner。不得把已完成工具名留作当前活动。不得把 token 级 thinking 重新放到公开流。 - 相关记录:BUG-473
- 复发自:无
- 修复版本:待发布
BUG-517 | 选择题选中态右边的对勾「已选择」是多余确认文案
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
rectification-choice-card - 用户现象:点了 A–D 后,选中项右侧再出现对勾和「已选择」。底色和边框已经标明选中,这组标记没有新信息。
- 触发条件:点校正选择题任一选项,或点卡片上的停止项。
- 根因:BUG-508 为了补确认感,在
data-selected底色之外又加了Check+ 「已选择」。 - 修复:去掉选中徽标和对应 CSS。选中仍靠
data-selected="true"的底色与边框;pending 时卡顶「正在记录…」保留。 - 验证:
rectification-surface-contract。 - 防复发:选择题选中态不得再渲染对勾或「已选择」。确认感只来自选项底色和卡顶记录行。
- 相关记录:BUG-508
- 复发自:无
- 修复版本:待发布
BUG-518 | 选择题卡片仍画出「先这样,先看当前范围」
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
rectification-choice-card - 用户现象:A–D 下面还有一颗「先这样,先看当前范围」。输入栏那颗同文案按钮已经去掉,卡片上这颗还在。
- 触发条件:校正判断题(非 reverse_verify)画出选择题卡片。
- 根因:卡片把
stop_label一律画成第五个选项。采集输入栏的rectification-collect-stop去掉后,卡片出口还在。 - 修复:
stop_label为「先这样,先看当前范围」时不画这颗按钮。盘外核对的「这题跳过」仍保留。服务端 stop 语义和文案常量不改。 - 验证:
rectification-surface-contract、rectification-adopt-flow-20260902。 - 防复发:校正判断卡不得再渲染「先这样,先看当前范围」。不得把盘外核对的「这题跳过」一并删掉。
- 相关记录:BUG-516
- 复发自:无
- 修复版本:待发布
BUG-519 | 探针池耗尽出采用卡时旁白说「继续往下收」,并被无 frame 的 nakshatra 绕过采用早退
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
frontend/src/lib/rectification-agentic/v9/method-followup.ts、decision-from-dossier.ts、answer-choice.ts、server-focus.ts、user-copy.ts、adopt-narration.ts、adopt-narration-agent.ts、/api/rectification/agent - 用户现象:采集题答「没有」后,助手说「我按现有材料继续往下收」,同一轮却弹出采用卡;composer 没有下一问,采用原因不明。
- 触发条件:区分探针已答完,receipt 仍带 nakshatra 探针与
window_scan.d*_candidates_differ=true,决策已是offer_provisional_range/adopt_representative/ready_to_adopt。 - 根因:
buildMethodFollowupPlan的 nakshatra 分支只看「有没有探针」,不看决策层是否已丢弃;无choice_frame的distinguish_candidates被放进deferred_followup。isRemainingDiscriminatorFollowup见source=nakshatra_boundary就当成剩余区分题,BUG-472 的采用早退被绕过。随后persistableFocusDomain("appearance")原样返回领域名,DB check 不含该值,写焦点失败,旁白落到hostNarrationFallback。 - 修复:
rectificationFollowupCatalog只在inspectDiscriminatorProbes选中 nakshatra 时传入;无 frame 的 nakshatra followup 丢进dropped_probes(not_renderable)。deferred_followup不再收无 frame 的 distinguish。isRemainingDiscriminatorFollowup对无 frame 的 distinguish 返回 false。persistableFocusDomain对非白名单领域返回null;无 frame 的 appearance distinguish 直接skipped且零次 RPC。采用卡首次打开时由无工具旁白 Agent 写 2–4 句,校验失败/超时走模板,模板在 BUG-503 的stopReasonPrefix之后补「分不开 A 和 B」。 - 验证:
frontend/tests/rectification-adopt-narration-20260904.test.ts;live five-evidence fixture 补 nakshatra 与window_scan后原断言仍过;rectification-server-focus锁appearance/horary/nakshatra → null。 - 防复发:采用早退只留在
shouldSkipFollowupPersist。不得为collect:appearance放宽 DB check。旁白 Agent 说出的每个HH:MM、四位年份、以及去掉这两类之后剩下的整数(含相对支持度)必须能在AdoptDeliveryFacts里找到,否则整段丢弃。Agent 调用与意图分类器相同,不计费。 - 相关记录:BUG-440、BUG-444、BUG-463、BUG-472、BUG-503、BUG-520
- 复发自:BUG-472
- 修复版本:待发布
- 编号说明:任务书撰写时最大号为 BUG-515。同步
cfa82499后 staging 已占用 BUG-516~518,本条落在 BUG-519。
BUG-520 | 区分题答「明确没有发生」被当成整条领域拒答
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
frontend/src/lib/rectification-agentic/v9/method-followup.tsdeclinedDomains、answer-choice.tsdossierWithClosedFocus - 用户现象:带年份的事业/搬家区分卡选 C「明确没有发生」后,同领域后续有年份探针不再出现;D10/D4 观察分支也会被跳过。本案因探针池已空,结局没被改写。
- 触发条件:
focusStatusForAnswer把区分题的「没有」写成declined;v_declined_skipped投影带target_domain但不参与计分身份;declinedDomains把任意 declined 焦点的领域当成拒答。 - 根因:BUG-440 为 D12 同领域卡把「答 C declined 计分」写成领域覆盖。对有年份的
distinguish_candidates,C 只是这一题的计分答案,不是「这条线以后都别问」。 - 修复:
declinedDomains只把intent === "collect_method_evidence"(缺 intent 的旧行仍按采集)算成领域拒答;distinguish_candidates的 declined 不计覆盖。dossierWithClosedFocus追加行带intent。focusStatusForAnswer仍返回declined,以保留 DB 状态与不确定度计数。 - 验证:
rectification-collect-stall「denying the dated family collect declines relatives」原断言不变;rectification-adopt-narration-20260904锁 career distinguish declined 不覆盖d10_career,且有年份事业探针仍可被选中;本案 family collect 与 family collect + career/relocation distinguish 两种 declined 输入决策与旁白相同。 - 防复发:领域拒答只看采集题 intent。不得把区分题的 C 当成整域 skipped_by_policy。
- 相关记录:BUG-440、BUG-519
- 复发自:无
- 修复版本:待发布
- 编号说明:与 BUG-519 同批;任务书原写 BUG-517,因编号冲突改为 BUG-520。
BUG-521 | 点选最后一道区分题时采用旁白 Agent 从不运行
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
frontend/src/lib/rectification-agentic/v9/answer-choice.tspersistNextInterviewAfterChoice - 用户现象:点完最后一道区分题才出采用卡时,旁白仍是模板句;只有采集题答「没有」和空闲兜底才会走 Agent。
- 触发条件:
applyRectificationChoice→persistApplied传入的 dossier 仍是点选前的decisionReceipt;本轮新状态在decisionState,本轮决策在nextDecision。 - 根因:采用早退分支从旧 dossier 再算一遍
decideFromDossier。旧决策还是「还有题要问」,precisionStage !== "ready_to_adopt",shouldWriteAdoptNarration为 false,模型一次都没调。模板范围也来自这份过期决策。同一次调用里决策层算了两遍(BUG-440 防复发条款)。 - 修复:
persistNextInterviewAfterChoice接受调用方传入的decision;未传时用decideAfterInferenceChange({ dossier, state: decisionState })。函数体内不再出现decideFromDossier(。adoptDeliveryFacts/ 模板兜底使用合成后的 receipt(本轮inference_state)。三条调用方都传入本轮决策。 - 验证:
rectification-adopt-narration-20260904点选用例改为点选前 dossier(revision 5、分数 23/16/7)+ 答完后decisionState/nextAction;断言模型调用 1 次、facts 为 05:00 与 21/18/9、ready_to_adopt。非法模型输出时模板范围仍来自本轮决策。源码断言该函数体内无decideFromDossier(。 - 防复发:
persistNextInterviewAfterChoice不得再从旧 dossier 重算决策。点选路径测试不得用已答完 dossier 冒充点选前状态。 - 相关记录:BUG-440、BUG-519、BUG-522
- 复发自:BUG-440
- 修复版本:待发布
BUG-522 | 采用旁白校验把「不是确认」整段打回,且真实环境无法证明 Agent 有没有写
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
adopt-narration-agent.ts提示词、deliverAdoptNarration超时与诊断字段 - 用户现象:校验器全文匹配「确认|精确」,模型写「这不是确认的分钟」会被静默退到模板;部署后日志看不出 Agent 路径命中还是模板兜底。
- 触发条件:采用旁白 Agent 生成含「确认」或「精确」的否定句;或模型挂起超过等待时间。
- 根因:提示词只禁止承诺确认/精确,校验器却禁这两个字。分类器同款调用没有超时。结果没有可观测枚举。
- 修复:提示词改为「不要出现『确认』『精确』这两个词」,与校验器同口径;校验器本身不放宽,「这不是确认的分钟」仍打回。
deliverAdoptNarration打adopt_narration=agent | template:<reason> | template:model_error | template:not_ready(不含模型原文),并用AbortSignal.timeout(8000)超时走模板。意图分类器超时仍是既有缺口,本单不修。 - 验证:提示词源码断言含「不要出现」;
deliverAdoptNarration对 agent / template:unknown_minute / template:model_error / template:not_ready 各有断言;短 timeout 挂起 generateText 走模板且不抛。 - 防复发:采用旁白结果必须留下
adopt_narration=日志。Agent.generate 必须带超时。不得把「确认」从校验器删掉却不改提示词。 - 相关记录:BUG-521、BUG-523
- 复发自:无
- 修复版本:待发布
BUG-523 | 采用旁白超时用 unref 计时器,测试挂死事件循环
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
frontend/src/lib/rectification-agentic/v9/adopt-narration-agent.tscomposedAbortSignal - 用户现象:staging 门禁
npm test退出码 1,13dded9f不能部署。生产请求有监听 socket 撑住事件循环,超时在真实流量里仍会触发;但每次调用都会留下一个无法clear的 8 秒计时器。 - 触发条件:
deliverAdoptNarration用AbortSignal.timeout();测试把generateText挂成永不 resolve 的 Promise。 - 根因:Node 的
AbortSignal.timeout()内部计时器始终 unref,空事件循环会在 abort 前排空。测试运行器判cancelledByParent,同文件后三条用例一并取消。验收只看了# fail,没看# cancelled与退出码。 - 修复:改为
setTimeout+AbortController(计时器保持 ref),返回{ signal, dispose };模型返回、校验完成或外部 abort 后clearTimeout并移除监听。超时仍走template:model_error。 - 验证:
rectification-adopt-narration-20260904超时用例不再 cancelled;新增「调用完不留活跃计时器」用例;源码不含AbortSignal.timeout。定向套件与该文件摘要须含完整六行且# cancelled 0、退出码 0。 - 防复发:采用旁白超时不得再用
AbortSignal.timeout。验收必须贴 Node 摘要完整六行(tests / pass / fail / cancelled / skipped / todo)与进程退出码,不得只报# fail 0。 - 相关记录:BUG-522
- 复发自:BUG-522
- 修复版本:待发布
BUG-524 | consultation_workflow 在 float 时辰上崩溃,全量 HTTP 500
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
POST /api/consultation_workflow、execute_consultation_workflow、_build_birth_time_sensitivity、_birth_datetime_from_args;报告 worker 与聊天深度咨询共用该端点 - 用户现象:staging 上 standard personal_full 生成失败,
failureCode = calculation_unavailable。同一端点对 provisional 出生时间返回 HTTP 500。 - 触发条件:API 出生 payload 的
hour/minute为 float(_high_rigor_birth_payload的既有口径),且birth_time_accuracy为provisional或approximate,因而会走到_birth_datetime_from_args。 - 根因:上游同步把
_build_birth_time_sensitivity无条件接入 consultation_workflow。CLI argparse 的 hour/minute 是 int;API 路径是 float。datetime()不能接受 float,抛TypeError。异常只被except ValueError包住,于是冒成 500。 - 修复:
_birth_datetime_from_args对年月日时分做int()规范化,不改变 API float 口径。敏感度构建失败时写入 blocked/not_available状态对象并继续 workflow,不再打死整个端点。 - 验证:
tests/test_consultation_workflow_birth_time_sensitivity.py(已列入CORE_PYTEST_TARGETS);本地 HTTP 对 career/marriage/wealth/timing/health 与不带敏感度字段的聊天形状均为 200/success=true;五份响应喂给buildReportEvidenceBundleV2得到 5 张 claim card、blockedSections为空。staging quick 门585 passed, 1 skipped。staging 上从 web 容器对虚构 smoke 盘POST http://api:5200/api/consultation_workflow(provisional + float 时分)HTTP 200、success=true、birth_time_sensitivity.status=candidate_window_only,约 74s。产品侧真实 personal_full 仍失败,见 BUG-526,不是本条 TypeError。 - 防复发:float hour/minute 的 API body 必须能走
execute_consultation_workflow且不 500;敏感度层失败必须降级,不得再变成未捕获TypeError。不得把_high_rigor_birth_payload的 hour/minute 改成 int。 - 相关记录:BUG-526
- 复发自:无
- 修复版本:
7b1354a79bb8e9b92c3c816fdc01f5f8de2860b7(含本修复的 staging 部署 SHA)
BUG-525 | 采集题答「没有」后「正在准备下一个问题…」不消失
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
applyCollectFocusDenial、persistCollectDenialTurn、/api/rectification/agentcompletedMessageResponse、rectification-agentic-chat问题缺口重试 - 用户现象:采集口述题答「没有」后,助手气泡已经给出下一问,底部仍转圈显示「正在准备下一个问题…」,不会变成「没有拿到下一个问题」。范围条「目前范围 … 还在收窄」是采集阶段的只读提示,不是本缺陷。
- 触发条件:当前焦点为
collect_spoken;意图分类answer_current_focus+answer_class: "no";走采集拒答快路径(不进 Agent)。 - 根因:拒答快路径把下一问题干整段当成正文流出去,但 turn 上
question为空、新焦点没有asked_turn_id,run.completed也不带turnId。前端liveQuestionOnMessages要求已结算消息的question.focus_id等于current_question.focus_id,缺口因此一直是preparing。applyCaseSnapshot只要快照里有current_question就把重试次数清零,定时重拉永远到不了unavailable。 - 修复:拒答后先落确定性 turn,再
linkFocusAskedTurn。库内正文为确认句 + 精确题干后缀(GET 仍按asked_turn_iddetach);直播只推确认句。run.completed带上turnId。快照里出现current_question不再重置缺口重试。 - 验证:
rectification-collect-stall锁 family collect 拒答 → occupation 题干写入 turn、p_asked_turn_id、直播确认句;route 源码锁persistCollectDenialTurn与finished.turnId。rectification-surface-contract禁止if (nextQuestion !== null) setQuestionRetryAttempts(0)。 - 防复发:采集拒答快路径建立的下一焦点必须有
asked_turn_id,且run.completed必须带该 turn。不得把「快照已有 current_question」当成缺口已闭合。直播不得把下一问题干当作整段回复(题干走 turn.question)。 - 相关记录:BUG-440、BUG-490、BUG-491、BUG-505、BUG-520
- 复发自:BUG-491
- 修复版本:待发布
- 编号说明:rebase 到
origin/staging时 BUG-524 已被 consultation_workflow 占用,本条落在 BUG-525。
BUG-526 | accepted 生时的个人报告 worker 直接读已收回权限的校正表,16 秒 calculation_unavailable
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
frontend/src/lib/personal-report-worker.tscreateProductionWorker的generate;frontend/src/app/api/reports/route.ts的loadCandidateRange;public.agentic_rectification_cases - 用户现象:staging 上 standard personal_full 创建成功后约 16 秒失败,
failureCode = calculation_unavailable,无章节行。表现与 BUG-524 事故码相同,但引擎访问日志里没有POST /api/consultation_workflow。 - 触发条件:资料
birth_time_status为accepted(不是confirmed),worker 与 create route 因此去查agentic_rectification_cases的candidate_accepted行。 - 根因:
20260814010000_immutable_skill_registry.sql已从该表收回service_role的表级权限,只许走 security definer RPC。报告链路仍from("agentic_rectification_cases").select(...)。Postgres 三次permission denied for table agentic_rectification_cases(与 job 三次 attempt、默认 5s/10s 重试对齐)。查询失败被映射成可重试calculation_unavailable,报告从未调用引擎。第二读者:create route 同样if (error) throw error,会把创建请求打成calculation_unavailable。 - 修复:新增只读 RPC
read_report_candidate_range(不 re-grant 表权限)。worker 与 create route 经共享 helper 调用;任何读失败降级为无窗口并继续生成。calculation_unavailable只保留给引擎调用失败。legacy 表终态以库约束为准:confirmed与completed(20260720已纳入枚举;前端不再.in("status", ["confirmed", "completed"])直查)。 - 验证:
tests/report-candidate-range.test.ts三态降级;tests/personal-report-api.test.tsnull 窗口仍 201;tests/database-report-candidate-range.test.ts权限矩阵与列白名单。staging @e27d5dc5:accepted 档案两次 standard personal_full 均 5×POST /api/consultation_workflow200,五章有正文;不再出现 16 秒calculation_unavailable。终稿被 BUG-535final_parse_rejected拦住,见该条。 - 防复发:报告 worker 与 create route 不得再直接 SELECT 已收回
service_role权限的校正表;候选窗读失败不得再变成calculation_unavailable。 - 相关记录:BUG-524、BUG-534、BUG-535
- 复发自:无
- 修复版本:
7baf2300 - 编号说明:rebase 到
origin/staging时 BUG-525 已被采集拒答占用,本条落在 BUG-526。
BUG-527 | 可评分事件不足 3 条时盘外核对抢跑到已拒答领域
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
buildMethodFollowupPlan、holdoutFollowupFor、oos_blind/ holdout 提问 - 用户现象:账本只有 2 条可评分带日期事件时,家人采集刚答「没有」,下一问却变成盘外核对,焦点落在刚拒答的 family。
- 触发条件:
holdoutValidation === "not_started"或sessionOutcome === "validate_holdout",引擎给了oos_blind_prompts,训练门event_quality.passed=false。 - 根因:计划层的两处 OOS 分支只看 holdout 状态和引擎提示,不过
meetsAcceptanceEventQuality,也不跳过declinedDomains(BUG-520 口径)。 - 修复:新增
holdoutFollowupFor:训练门未开返回null;否则取第一个未拒答的 OOS 提示,没有则退到带年份 holdout 事件,再没有为null。两处 OOS 分支都走它。不改holdoutValidationStatus/decideRectification的采用与确认门。 - 验证:
frontend/tests/rectification-collect-direction-20260904.test.ts(2 条事件不出 OOS;4 条事件跳过 family 后问 education holdout;全部拒答且无 holdout 事件时validate_holdout的next_followup为 null)。 - 防复发:OOS 提问必须同时满足训练门与未拒答领域。不得只凭引擎给了
oos_blind_prompts就提问。 - 相关记录:BUG-396、BUG-520
- 复发自:无
- 修复版本:待发布
- 编号说明:任务书写 BUG-524;rebase 后最大号已是 BUG-526(报告 worker),本条从 BUG-527 起。
BUG-528 | 缺第三件带年份的事时先问职业或泛问
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
buildMethodFollowupPlan、nextDatedCollectFollowup、exhaustionSpokenCollectFollowup、persistNextInterviewAfterChoice - 用户现象:2 条可评分事件、家人拒答后,决定路径问「你平时主要做什么工作?」;职业没有日期,答完也不推进训练门。
- 触发条件:
!meetsAcceptanceEventQuality,方法覆盖轮转里感情/事业已有记录、家人已拒答,剩下职业。 - 根因:带年份的学业/财务/搬家/健康只存在于穷尽路径;主流程在方法轮转之后才落到
domain: null泛问。 - 修复:
DATED_COLLECT_ORDER(家人 → 学业 → 财务 → 搬家 → 健康 → 事业 → 感情)由主流程与exhaustionSpokenCollectFollowup共用。训练门未开时,在方法轮转之前取下一件带年份采集题;职业与other泛问排在最后。datedCollectFollowup的source为method_coverage(采集,不是盘外核对)。 - 验证:新文件锁 education → finance → occupation → other;拒答路径落下
USER_COLLECT_QUESTION.education。rectification-eight-method中训练门未开的下一问从感情改为家人(或财务),每条改断言附原值/新值/原因。4 条可评分事件时本分支不触发(rectification-collect-stall、rectification-yearless-ungrounded的 OOS 结论不变)。 - 防复发:训练门未开时不得先问职业或无领域泛问。顺序只许有一处定义。
- 相关记录:BUG-426、BUG-442、BUG-472、BUG-527、BUG-531
- 复发自:无
- 修复版本:待发布
BUG-529 | 采集题干可以不指向任何领域
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
validateSpokenPrompt、rectification-set-focus、COLLECT_DOMAIN_KEYWORDS - 用户现象:Agent 把家人盘外核对改写成「除了工作这条线,还有哪件事你能记起大概的年份?」,用户不知道该往哪想。
- 触发条件:
intent=collect_method_evidence、无choice_frame、领域在关键词表内;题干不含该领域日常词。 - 根因:校验器只查长度、选项字面、内部 token、
domain_mismatch,不要求题干提到领域。 - 修复:采集题必须命中目标领域至少一个关键词,否则
domain_missing。第二次仍不合格时服务端用USER_COLLECT_QUESTION[domain]落焦点。工具描述要求写出领域。区分题与 OOS 题不套这层关键词。 - 验证:
rectification-spoken-prompt(education 泛问 →domain_missing;含「上学」→ ok;OOS 不受影响);新文件两次泛问后焦点 prompt 为USER_COLLECT_QUESTION.education。 - 防复发:采集题方向由服务端定。不得把关键词校验套到带
choice_frame的区分题或 OOS 题上。 - 相关记录:BUG-527、BUG-528
- 复发自:无
- 修复版本:待发布
BUG-530 | 采集进度句没有数字
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:读盘投影
collection_progress、agentic-rectification.ts提示词、MACHINE_VOICE_LEXICON - 用户现象:助手写「当前范围还在继续收窄中,接下来我们继续。」没有任何数值。
- 触发条件:下一问是采集题;Agent 在没有
collection_progress或不用该字段时自行写进度。 - 根因:读盘投影没有给出可评分件数/下限/还差几件;提示词允许空进度句;词表不拦「继续收窄」。
- 修复:
collectionProgressFromReceipt从gates.event_quality取{ scoreable, minimum, missing },缺字段为null。读盘与 GET interview 都带上。提示词要求数字只来自该字段,没有就不说进度。词表加入「继续收窄」「范围还在」,只对spokenPrompt校验生效(旁白无同一套校验)。 - 验证:投影锁 2/3/1 与缺 gate 时
null;提示词测试含collection_progress与「不得写『范围在收窄』」。 - 防复发:没有
collection_progress不得写没有数字的收窄句。数字不得自算。 - 相关记录:BUG-527
- 复发自:无
- 修复版本:待发布
BUG-531 | 新案例第二问被家人抢占感情 / 事业
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
buildMethodFollowupPlan里nextDatedCollectFollowup的插入位置 - 用户现象:新案例已有 1 件事业(或 1 件感情)时,第二问变成家人,而不是感情 / 事业。
- 触发条件:训练门未开(前 3 件事几乎总是如此);
3847e9c9把 dated 补采集插在方法覆盖轮转之前。 - 根因:上游任务书把「缺第三件带年份的事」的顺序写成了训练门未开阶段的总顺序。执行方按字面把
nextDatedCollectFollowup放在!dashaCovered之后、感情 / 事业 / 家人轮转之前。这是任务书顺序错位,不是执行方偏离。 - 修复:删掉轮转前分支。轮转里
!familyCovered之后、!occupationCovered之前,训练门未开且nextDatedCollectFollowup有结果时取它。此时感情 / 事业 / 家人已 covered 或 declined,补采集只会给出学业 → 财务 → 搬家 → 健康。DATED_COLLECT_ORDER与exhaustionSpokenCollectFollowup不动。 - 验证:
rectification-collect-direction-20260904锁 1 事业 →d9_relationship、1 感情 →d10_career、事业+感情 →relatives、再拒答家人 →d5_education、再拒答学业 →d2_finance。家人答「没有」后焦点仍是学业。3847e9c9改写的感情 / 事业断言回到45bdb63e。 - 防复发:dated 补采集不得插在感情 / 事业轮转之前。训练门未开时新案例前两问仍是 D9 / D10。
- 相关记录:BUG-528、BUG-463
- 复发自:BUG-528
- 修复版本:待发布
BUG-532 | 原生 input 间距合同测试仍锁 12px
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
frontend/tests/admin-model-management-ui-contract.test.ts;挡住backend-quality-gate的npm test --prefix frontend - 用户现象:无直接用户可见故障。
7ee7f825之后门禁因这一条失败,staging 部署停在更早的 SHA。 - 触发条件:
globals.css原生 input 为padding: 0 var(--space-3),合同测试仍匹配padding: 0 12px;。 - 根因:
7ee7f825(fix(ui): collapse product weights and radii onto the shared Button)把间距收敛到 token,未改合同测试。本单开工时origin/staging上这条仍红,别处未修。 - 修复:测试接受
padding: 0 var(--space-3);。不改 CSS。 - 验证:
admin-model-management-ui-contract全过;全量失败清单只剩 Docker 并发争用,隔离重跑通过。 - 防复发:改
globals.css原生控件尺寸时同步合同测试。不得为迁就测试把 token 改回裸像素。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-533 | 开场打招呼被 set-focus 空 replace 清掉
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
applyStepAnswerChunk、retractSpoken、开场轮answer.delta - 用户现象:开场先流出「你好,很高兴一起做这次生时校正…」,页面最终只剩「我们慢慢来就好…」和采集题干。
- 触发条件:模型在
rectification-set-focus之前写出带句号的中文正文;该步已live播出。 - 根因:任一公开工具的
tool-call都会retractLive,发出answer.delta{text:"", replace:true}。set-focus 只是把题干挂到同一条消息,前面的打招呼不是过程自述。 - 修复:
rectification-set-focus的 call/result 不再收回已播出正文。读诊断、记证据仍收回。 - 验证:
rectification-step-answer:打招呼 + set-focus 为none,其后短句仍live;read-diagnostics 仍retract。 - 防复发:set-focus 不得把同轮已播出的正文 replace 成空。不得把这个例外扩到 record/compare/diagnostics。
- 相关记录:BUG-584
- 复发自:无
- 修复版本:待发布
BUG-534 | 分章进度 section: 让 job 行无法 round-trip,心跳中断、阅读页误报失败
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
personal-report-job-service-corerecordFromRow;心跳 RPC 返回解析;GET /api/reports/:id读 generating 行 - 用户现象:standard personal_full 已经调到引擎并写出至少一章后,阅读页变成
report_generation_failed/「报告暂时无法读取」。后台 job 停在section:theme-*,心跳不再刷新,直到租约过期才重试。 - 触发条件:worker
onProgress把progress_phase写成section:<section_id>(SQL 与isValidPersonalReportJobProgressPhase允许冒号)。随后心跳或 GET 把该行再读进recordFromRow。 - 根因:
recordFromRow用operationalCodePattern(^[a-z][a-z0-9_]{0,63}$)校验progress_phase,不接受冒号。updateProgress写入成功后解析 RETURNING 行抛storage_invalid;onProgress吞掉非租约错误,生成继续。下一次心跳解析同一行再抛,worker 中止。GET 同一路径变成 HTTP 500。 - 修复:
progress_phase改走已有的isValidPersonalReportJobProgressPhase(与 SQL check、写入校验同一套,允许section:)。 - 验证:
personal-report-job-service:写入section:theme-health_pressure后心跳与getOwnedByRequestId仍成功。staging @e27d5dc5干净一次跑完五章到 assemble 85%,心跳未再中断。 - 防复发:job 行 round-trip 不得比写入校验更严。分章进度相位必须能读回。
- 相关记录:BUG-526、BUG-535
- 复发自:无
- 修复版本:
e27d5dc5
BUG-535 | 五章写完后终稿 final_parse_rejected,accepted 报告仍无 ready 文档
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
assembleReportDocumentV2之后的safeParseServerReportDocument;standardpersonal_full终装 - 用户现象:报告中心显示生成失败。错误码
report_schema_invalid,日志innerReason=final_parse_rejected。五章其实都已写完。 - 触发条件:accepted 生时、默认五主题、staging @
e27d5dc5。干净 attempt 1 与此前被租约打断后恢复的 attempt 都复现。 - 根因:
c2f23131把CHART_IDS扩到 9 个(加 D6/D8/D30),文档 v2 仍charts.max(6)。分盘提取修好后,五主题装配 9 张图,终稿 parse 拒绝。四主题一直 ≤6 所以从未踩中。随后真实五章又踩中actionNotes.max(24),再踩中匿名 contract guard。 - 修复:v2
charts.max(CHART_IDS.length);JSON Schema / Python 合同同步;装配允许集改为CHART_IDS。装配把actionNotes截到合同上限 24,不放宽 schema。final_parse_rejected日志parsePaths仅 path + code;guard 失败改为字段路径 + 种类。blocked 段去掉确定性用语。 - 验证:合同测试 9 图过 parse;五主题分章夹具 READY;每章 6 条行动仍 READY 且 notes=24。staging request
31025c49:actionNotes/too_big。a75929c1requestf10daf6d:三条(guard)/guard。4ef4c406request0679321b:ready,v2 文档 9 张图、五章有正文、actionNotes=22、blocked 披露为空,阅读页可打开。 - 防复发:上限必须绑定
CHART_IDS.length,不得再写裸 6/9。扩枚举必须同时改 Zod / JSON Schema enum / PythonCHART_IDS。装配侧截断须跟合同上限同一常量。终稿失败必须带可区分的 parse path,不得只写(guard)。 - 相关记录:BUG-526、BUG-534;引入半拉子改动的提交
c2f23131 - 复发自:无
- 修复版本:
4ef4c406
BUG-536 | 采用后第一道核对题重复已问过的采集题
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
remainingReverseVerifyProbes、采用后buildMethodFollowupPlan的accepted分支 - 用户现象:采用代表分钟后,第一道核对题与采集阶段已经问过并回答过的带年份题几乎逐字相同。
- 触发条件:采集阶段已按
semantic_key问过该 (domain, year);采用后反向核对从eventProbes再取第 0 条。 - 根因:冲突探针去重用了
askedKeys;BUG-350 单独写下的反向核对漏接。过滤了拒答领域、年份已覆盖、成年下限,没有用semantic_key/candidate_split_hash/domain.year。 - 修复:
remainingReverseVerifyProbes增加askedKeys,与remainingConflictProbes同口径。已问或已跳过的探针不再作为核对题。 - 验证:
rectification-post-adopt-verify-20260904:askedProbeKeys含第一条semantic_key时下一问是另一条;两条都在则next_followup === null且next_user_action.id === "start_consultation";只含candidate_split_hash也能去重。rectification-eight-method采用后有剩余探针 / 无剩余探针两条既有断言仍过。 - 防复发:采用后核对必须走
askedKeys。不得只靠probeYearAlreadyCovered当已问去重。 - 相关记录:BUG-350
- 复发自:无
- 修复版本:待发布
BUG-537 | 核对卡「这题跳过」等于整案停止,已采用仍念采用提示,前端报没有下一问
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
choice-card核对卡 stop 槽、SKIP_PROBE_ACTION、applyRectificationChoice、rectificationQuestionGapState、校正聊天面 - 用户现象:点「这题跳过」后助手又说可以从下面选一个先用着;随后出现「没有拿到下一个问题。」和重新加载。
- 触发条件:已采用,当前焦点是反向核对卡;用户点跳过。
- 根因:三层叠加。(1) 核对卡只把停止按钮改了标签,动作仍是整案
STOP_ACTION,userStopped:true。(2) 结构化旁白只看can_adopt,已采用后publicCanAdopt仍为 true,再念一遍adoptCue。(3) 服务端没有核对结束状态,前端把「无焦点 + 可续」当成问题还没准备好。 - 修复:核对卡绑定
skip_probe:焦点skipped、探针进answered_probes(不计分)、不置userStopped,再取下一道核对题。已采用禁止adoptionNarration/adoptCue。GET 与结构化答题暴露next_user_action;已采用且无下一问时为start_consultation。前端该态为verified_idle:一行postAdoptVerifyDone,无重载、无 handoff。关闭核对结束时不得把已跳过的焦点重新挂到收尾消息上。 - 验证:两道核对题跳第一道 → 下一焦点是第二道,
sessionOutcome不是user_stopped,旁白不含adoptCue。跳最后一道 → 无新焦点,next_user_action.id === "start_consultation",旁白等于postAdoptVerifyDone。verified_idle无 gap copy、无重载、无 consult handoff。采用前区分题停止按钮仍是STOP_ACTION。 - 防复发:核对卡「这题跳过」不得走
STOP_ACTION。已采用旁白不得再念采用提示。start_consultation不得进 preparing/unavailable。 - 相关记录:BUG-350、BUG-499、BUG-502、BUG-518、BUG-520
- 复发自:无
- 修复版本:待发布
BUG-538 | 采用旁白承诺的核对与采用后计划不同源
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
adoptDeliveryFacts.post_adopt_verification、采用旁白模板与 Agent 校验器 - 用户现象:采用卡旁白说会拿若干年前事和几类域外题来核对;采用后一道都没问。
- 触发条件:旁白事实包按 holdout 事件与
oos_blind_prompts生成;采用后实际计划走remainingReverseVerifyProbes。 - 根因:
TASK-rectification-adopt-narration-20260904.md§4 把post_adopt_verification写成 holdout + OOS。BUG-350 已定采用后不得把不计分盘外核对当默认下一步。两套规格矛盾,旁白说的是不会发生的事。这是任务书错误,不是执行方偏离 BUG-350。 - 修复:
post_adopt_verification改为与采用后计划同一过滤(同一函数、同一askedKeys、同一declined),项为{kind:"reverse_verify"; domain; year_label}。空列表时模板说没有还能核对的前事;校验器把「会拿……核对」判为promise失败。holdout / OOS 只留在决策收据,不进旁白。 - 验证:
rectification-adopt-narration-20260904原 holdout/oos 三项改写为 reverse_verify 且与buildMethodFollowupPlan({accepted:true})首题一致。空列表 +「会拿……核对」→promise。模板含「没有还能核对的前事」。 - 防复发:旁白承诺必须与
remainingReverseVerifyProbes同源。空列表不得承诺会核对。不得把 holdout/OOS 写回旁白事实包。 - 相关记录:BUG-350、BUG-519、BUG-520、BUG-521、BUG-522、BUG-523
- 复发自:无
- 修复版本:待发布
BUG-539 | 家庭采集题带年份前缀,又让用户报年份
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-10
- 影响面:
collectionYearFields、spokenFollowupForUser对collect_method_evidence - 用户现象:家庭采集题写成「某年前后,家里如果有结婚、添丁或住院这类事,记得大概哪年就行。」既指定了年份又让用户报年份。
- 触发条件:家庭轮转把 dated 探针的
year_label拼进USER_COLLECT_QUESTION.family。 - 根因:
68f0759e为 BUG-414/418「带年份的口语问、不出无年份分盘选择卡」把 dated 探针年份塞进采集口语。USER_COLLECT_QUESTION.family本身以「记得大概哪年就行」结尾,拼在一起自相矛盾。本单只去年份前缀。家人拒答后若还有未问的财务/搬家/健康/职业采集,不得直接给结果——见 BUG-546(推翻本条原先「出采用卡不是缺陷」的收口)。 - 修复:采集口语不再拼「某年前后,」。
probe_year/semantic_key仍留给计分与去重。不出无年份分盘选择卡的半边保留。 - 验证:家庭采集口语等于
USER_COLLECT_QUESTION.family,同时probe_year > 0。答完最后一道区分题后仍落家庭采集焦点,旁白不含探针年份。BUG-418 无年份分盘卡测试仍过。 - 防复发:采集口语不得既指定年份又说「记得大概哪年就行」。口径被 BUG-642 改为带线索模板:有
probe_year时用「某年前后有没有…别的年份也行」;无线索模板仍用「记得大概哪年就行」。不得为去前缀而丢掉probe_year或放回无年份分盘选择卡抢在采集前面。 - 相关记录:BUG-414、BUG-418、BUG-546、BUG-642
- 复发自:无
- 修复版本:待发布
BUG-540 | 候选区分阶段同一道「上大学」发挥题问两次,第二次绑错证据
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
choice-card.ts::pickProbe、ChoiceCardFollowup.probe_id - 用户现象:候选区分阶段连续两张选择卡题干、选项完全相同(「某年某月那次上大学,更接近如愿、将就调剂、发挥失常还是说不清」)。用户两次都作答,第二次答完范围继续收窄。
- 触发条件:学业域已核实入学与毕业两条事件;引擎对两条各发一条
known_event_quality;第二张卡的 followup 指向毕业探针。 - 根因:
pickProbe在choice_kind === "event_quality"时取池里第一条质量探针就返回,semantic_key匹配写在后面,永远轮不到。periodFor与eventQuestionPrompt都从这条错探针取日期和题干。用户第二次是对着入学题给毕业探针打分。 - 修复:匹配顺序改为
probe_id→semantic_key→ 无键时才按种类兜底。有键但找不到对应探针时返回null,不出卡,不再退回同种类第一条。question_id在有键时带上该键,避免两张质量卡共用一个 id。 - 验证:同域两条质量探针、followup 指向第二条时,
buildChoiceFrame的question_id、日期标签、user_meaning全部来自第二条;指向第一条不受影响。有键找不到探针时buildChoiceFrame为null。rectification-choice-card既有 varga_style / existence 断言仍过。 - 防复发:不得先按
choice_kind取第一条质量探针再匹配semantic_key。有probe_id/semantic_key时不得退回同种类第一条。 - 相关记录:BUG-390、BUG-541
- 复发自:无
- 修复版本:待发布
BUG-541 | 毕业等学业 kind 套高考发挥题,同切分质量探针占满名额
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:
event_probes.py::_quality_distinguish_probes - 用户现象:毕业事件也被写成「上大学 / 调剂 / 发挥失常」。即便卡面绑对了,第二道仍不带来新切分信息。
- 触发条件:学业域同时有入学与毕业;两条
known_event_quality的 yes/no 分钟集合相同(同一 D24 星座切分),只有semantic_key/target_evidence_id/candidate_split_hash不同。 - 根因:(1)
_quality_user_meaning与QUALITY_DISTINGUISH_OPTIONS["education"]只看domain == "education",不看event_kind。采集线会产生education_completion,模板仍是 BUG-390 的高考发挥题。(2)candidate_split_hash掺了年份,同分组拦不住;MAX_QUALITY_DISTINGUISH_PROBES = 2被两条信息相同的探针占满。 - 修复:质量探针只对
education_start/education_change/education_interruption发出;education_completion与其它 kind、以及非学业域不发。同域同 yes/no 集合只保留信息增益最高、并列取时间最早的一条;被去掉的不计入名额。不改四选项文案,不改candidate_split_hash,不新造毕业体验模板。 - 验证:入学 + 毕业 → 一条质量探针且
target_evidence_id指向入学。仅毕业 → 零条质量探针;训练门仍开时仍有 dasha 存在性探针。两条可发事件同分组 → 一条;_select_quality_distinguish_rows不同分组 → 两条,第三条不超过上限 2。test_probe_question_contract四选项合同仍过。 - 防复发:学业质量探针必须看
event_kind(或kind),不得对education_completion发。同域同 yes/no 集合不得发第二条。不得改QUALITY_DISTINGUISH_OPTIONS文案或candidate_split_hash算法来「修」去重。 - 相关记录:BUG-390、BUG-540
- 复发自:BUG-390(质量探针只对学业发出,但学业内 kind 未收紧)
- 修复版本:待发布
BUG-542 | 部署窗口数据库瞬断被报成「服务尚未配置」
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-06
- 影响面:16 处 API 路由的 Supabase/数据库客户端创建兜底;共享 helper
frontend/src/lib/api/service-unavailable.ts - 用户现象:staging 刚切完部署时,生时校正点选答案返回
503 {"error":"服务尚未配置"}。同一时刻/api/health显示数据库检查 degraded,数分钟后自愈。环境变量从未缺失。 - 触发条件:部署切换窗口内数据库短暂不可用;
createServerSupabaseClient()在自托管分支会先读身份会话(数据库查询),连接错误从这里抛出。 - 根因:16 处
catch把任意创建失败一律翻译成「服务尚未配置」。该文案只对应SupabaseConfigurationError(缺环境变量)。 - 修复:共享
jsonForSupabaseSetupFailure:仅配置错误返回「服务尚未配置」;其它异常返回503 {"error":"服务暂时不可用,请稍后重试","code":"service_unavailable"},并console.error路由名与error.name(不打印堆栈/连接串)。不改客户端抛错语义、不加重试、不改 health。 - 验证:
npx tsx --test tests/api-service-unavailable-20260904.test.ts4/4。mockcreateServerSupabaseClient抛配置错误 → 503「服务尚未配置」;抛Error("connection refused")→code=service_unavailable,文案不含「尚未配置」。覆盖POST /api/rectification/agent与GET /api/rectification/cases/[caseId]。grep -rn "服务尚未配置" frontend/src只命中 helper。tsc --noEmit0 错;改动文件 eslint--quiet0 error。 - 防复发:新路由创建数据库客户端失败不得手写「服务尚未配置」;必须走 helper。发布门模块 mock 继续用 Node 22 的
namedExports(BUG-514)。 - 修复版本:
5483649b
BUG-543 | staging 镜像构建因 Google Fonts 拉不到 Inter 失败
- 状态:resolved
- 首次发现:2026-09-05
- 最近更新:2026-09-05
- 影响面:Gitea
backend-quality-gate.ymlpublish、deploy/railway-web.Dockerfile的RUN npm run build、frontend/src/app/layout.tsx - 用户现象:无终端用户可见现象。门禁 validate 通过后 publish 失败,staging 不发布新镜像。
- 触发条件:推送触及门禁路径后,publish 在 Docker 内执行
npm run build。构建环境访问不到fonts.googleapis.com。 - 根因:BUG-430 用
next/font/google在构建期下载 Inter。validate 跑在 hostexecutor,能出网;publish 的镜像构建不能。Gitea run 2408(SHA40c623ed)报Failed to fetch Inter from Google Fonts,railway-web.Dockerfile:26退出 1。 - 修复:把 latin 可变 Inter(OFL)放进
frontend/src/app/fonts/InterVariable-latin.woff2,根布局改next/font/local。不改 workflow、不设构建期代理。字体栈与--font-inter不变。 - 验证:
site-style-isolation-contract锁定next/font/local、仓内 woff2 魔数、以及frontend/src不再出现next/font/google/fonts.googleapis.com。本机npx tsx --test tests/site-style-isolation-contract.test.ts;tsc --noEmit。 - 防复发:不得把西文正文字体改回
next/font/google。新字体必须是仓内文件 +next/font/local或@font-face。 - 相关记录:BUG-430
- 复发自:BUG-430(加载方式在无 Google Fonts 网络的镜像构建里不成立)
- 修复版本:待发布
BUG-544 | 采用卡钉在历史采集题下,最新旁白却说从下面选
- 状态:resolved
- 首次发现:2026-09-05
- 最近更新:2026-09-05
- 影响面:
attachOfferResultToTurns、applyLiveCandidateOffer、rectification-agentic-chat的candidateOffer锚点 - 用户现象:最新助手说「可以从下面选一个先用着」,下面没有时间卡。往上翻,卡钉在更早的「工作上呢,还记得哪年入职…」采集题下面。
- 触发条件:公开
can_adopt曾为真时,当时最新助手消息还是未答完的采集题;之后继续问了家人/核对,再出采用旁白。 - 根因:三层叠加,复发 BUG-497/499。(1) 客户端
useEffect只要某条消息已有同一resultId就不再改锚点。(2)messages.find取第一条candidateOffer,旧采集题永远赢过后面的采用旁白。(3) GET 把offer_result_id写在最新回合上时,mergeTurnQuestions在旧消息offer_result_id为空时仍保留客户端脏锚点。 - 修复:未采用时卡只挂在最新已落地、且没有未答采集/区分题的助手消息上;未答采集/选择题在场则不出卡。已采用后仍不把卡挪到核对题上。GET 水合时空的
offer_result_id清掉该回合的客户端脏锚点。 - 验证:
rectification-candidate-offer-anchor.test.ts:采集题 + 后续旁白 → 卡在旁白;只有未答采集 → 无卡;已采用后卡留在原消息。agent-voice-copy-contract、rectification-agentic-entry、rectification-adopt-flow-20260902既有合同仍过。 - 防复发:未采用不得把采用卡锚在未答
collect_spoken/choice上。不得用messages.find取第一条 offer。GET 空offer_result_id不得保留客户端脏锚点。已采用后仍不得把卡锚到最新核对题。 - 相关记录:BUG-312、BUG-497、BUG-499、BUG-545
- 复发自:BUG-497、BUG-499
- 修复版本:待发布
BUG-545 | 单分钟采用旁白把同一时刻念两遍,并说「分不开当前候选」
- 状态:resolved
- 首次发现:2026-09-05
- 最近更新:2026-09-05
- 影响面:
nonConvergingRangeNarration、templateStopExplain、RECTIFICATION_USER_COPY.adoptCue - 用户现象:交付句为「剩下的题分不开当前候选。更站得住的范围是 05:15,代表分钟 05:15。这只是代表性候选,不是已确认的唯一出生分钟。可以从下面选一个先用着。」听起来像内部状态,不像对人说话。
- 触发条件:可信区间收成同一分钟,且
stop_facts没有可并列的第二分钟。 - 根因:模板把「范围」和「代表分钟」无条件拼在一起;
formatClockRange把05:15–05:15收成05:15后仍套「范围是 X,代表分钟 X」。停问原因用「当前候选」这种内部词。adoptCue说「选一个」,卡片却不在这句下面(见 BUG-544)。 - 修复:区间收成单分钟时改口「眼下更站得住的是 HH:MM」,不再重复。停问改成「再问下去也分不开 A 和 B」或「再问下去也分不出更准的时间了」。
adoptCue改为「我按你说的经历认真分析过了,下面是这次的结果。」不得说「先用着」。边界句「不是已确认的唯一出生分钟」保留。 - 验证:
agent-voice-copy-contract锁单分钟句式与adoptCue;rectification-answer-choice最后一题采用旁白匹配新句式或跨分钟「代表分钟」。机器词表加入旧「当前候选 / 可以从下面选一个先用着 / 下面的时间可以先用着」。 - 防复发:单分钟区间不得把范围和代表分钟念两遍。用户可见文案不得出现「当前候选」。交付收尾不得说「先用着」。交付/采用轮仍须出现代表性分钟边界语义。
- 相关记录:BUG-544、VOICE.md
- 复发自:无
- 修复版本:待发布
BUG-546 | 家人采集答「没有」后直接给结果,未继续收能区分分钟的经历
- 状态:resolved
- 首次发现:2026-09-05
- 最近更新:2026-09-05
- 影响面:
buildMethodFollowupPlan、shouldSkipFollowupPersist、deferAdoption、isRemainingEvidenceCollect - 用户现象:学业、感情、事业都已有带年份经历,家人题答「没有」后,助手直接给出代表分钟和采用旁白。剩下两三个分钟还分得开,财务、搬家、健康、职业都还没问。
- 触发条件:训练门已开(至少 5 条带日期事件、2 个以上领域),家人采集拒答,剩余区分探针被标成
no_split_among_active,决策层canAdopt=true/sessionOutcome=adopt_representative。 - 根因:两层早退叠在一起。(1)
buildMethodFollowupPlan的 dated 补采集只在训练门未开时运行;门已开就跳到职业,再被deferAdoption清掉next_followup。(2)shouldSkipFollowupPersist见canAdopt且阻断方法已覆盖、当前题不是剩余区分题,就跳过写焦点、改写采用旁白。BUG-539 曾写「家人没有后出采用卡不是缺陷」,那是把采用能力门和「还有未问的经历域」混成一件事。 - 修复:家人覆盖或拒答后总是走
nextDatedCollectFollowup(学业 → 财务 → 搬家 → 健康 → 感情/事业补洞)。isRemainingEvidenceCollect覆盖这些域和职业口述。deferAdoption与shouldSkipFollowupPersist都不得把这类采集早退掉。canAdopt能力仍可开着;未答采集在场时采用卡仍按 BUG-544 不出。不放宽精确分钟确认门。职业仍不是 adopt 能力门槛。 - 验证:
rectification-collect-stalllive five-evidence 家人拒答后持久化财务采集;rectification-adopt-narration-20260904仅家人拒答仍问财务;rectification-provisional-adopt1c / after-choice / B2 锁财务采集写焦点;rectification-eight-method家人之后先财务再职业再占问。 - 防复发:训练门已开不得跳过未用的 dated collect。
canAdopt不得单独构成跳过财务/搬家/健康/职业采集的理由。不得把 BUG-539「家人没有后采用」读成「未问完的经历域也可以结束」。 - 相关记录:BUG-519、BUG-539、BUG-544
- 复发自:BUG-519(采用早退把未用采集域一并吃掉);BUG-539 把该收口写成非缺陷
- 修复版本:
998406ce - 修正:
998406ce把家人之后的 dated 补采集做成无条件优先,现成搬家 A/B 卡被财务口述挤掉。见 BUG-547。 - 修正:剩余采集豁免了
deferAdoption,但决策层在分离度够时仍判adopt_representative,出牌与追问同轮。见 BUG-550。
BUG-547 | 现成区分卡被口述补采集挤掉
- 状态:resolved
- 首次发现:2026-09-05
- 最近更新:2026-09-05
- 影响面:
buildMethodFollowupPlan、nextDatedCollectFollowup、精度层 / yearless 区分卡 - 用户现象:学业、感情、事业、家人都已有带年份经历,引擎已经给出搬家 A/B 探针,下一问却变成财务口述采集。
- 触发条件:经典八法覆盖、精度阶段
d4_refine、存在可渲染choice_frame的搬家探针,同时DATED_COLLECT_ORDER里还有未覆盖域。 - 根因:BUG-546 把家人之后的
nextDatedCollectFollowup从「训练门未开才走」改成无条件执行,且排在精度层搬家卡和 yearless 卡之前。BUG-546 的触发条件本是「没有可用区分题」,实现扩成了「不管有没有区分题都先采集」。 - 修复:家人之后若 yearless 或精度层能拿出
choice_frame,先问那张区分卡;只有本轮没有任何可渲染区分卡时才走 dated 补采集。isRemainingEvidenceCollect对deferAdoption的豁免保留。不放宽精确分钟确认门,不改DATED_COLLECT_ORDER。 - 验证:
rectification-choice-card搬家 A/B 卡用例原样通过;无探针时仍问财务采集;occupation 顺序断言改为d2_finance并写三栏说明。 - 防复发:dated 补采集不得抢在可渲染区分卡之前。没有区分卡时仍必须继续收未问过的带年份域(BUG-546)。
- 相关记录:BUG-546、BUG-519
- 复发自:BUG-546
- 修复版本:
cd704a92
BUG-548 | 全量附录表未写入 public 表白名单,staging 质量门失败
- 状态:resolved
- 首次发现:2026-09-05
- 最近更新:2026-09-05
- 影响面:Gitea
Independent Staging Quality Gate/validate、frontend/tests/database-local-business.test.ts - 用户现象:
bab07187推 staging 后质量门失败,镜像未发布。用户看不到新的全量数据附录入口。 - 触发条件:新增
public.personal_report_longform_appendices后跑npm test --prefix frontend。 - 根因:
database-local-business把pg_tables的 public 表名单锁死。附录迁移已应用,名单仍缺personal_report_longform_appendices,唯一失败项是not ok 949。附录定向库测本身通过。 - 修复:把该表按字母序写入白名单;断言
20260905010000_personal_report_longform_appendices.sql已应用且未双写冻结的frontend/db/migrations。 - 验证:Gitea run 2416 日志仅此 1 fail;本机重跑
database-local-business。 - 防复发:新增
public表必须同步改这条string_agg(tablename)白名单,并写原值/新值/原因。npm test会跑tests/*.test.ts,含这条库测。 - 相关记录:无
- 复发自:无
- 修复版本:
52a4570c
BUG-549 | 区分卡答「发生了」后仍问同一领域哪年采集
- 状态:resolved
- 首次发现:2026-09-05
- 最近更新:2026-09-05
- 影响面:
buildMethodFollowupPlan、datedCollectDomainBlocked、rectificationFollowupCatalog - 用户现象:刚在事业区分卡上点选「发生了」,下一问又是「工作上呢,还记得哪年入职、换工作…」。
- 触发条件:账本没有该领域带年份证据;区分卡答案只进推断层;本轮已无剩余区分卡,计划层按账本判断领域未覆盖。
- 根因:点选答案不写证据账本(BUG-389–393 分层,本单不推翻)。计划层
careerCovered/datedCollectDomainBlocked只看账本。BUG-547 之后区分卡问完才进采集链,刚答过的领域会被再问一次。 - 修复:
yes/weak_yes的领域视为采集已覆盖(用eventProbes按semantic_key/probe_id回查 domain,回查不到则忽略)。不写账本,不改meetsAcceptanceEventQuality,不改确认门。no/unsure不算覆盖。 - 验证:
frontend/tests/rectification-collect-direction-20260904.test.ts:账本无事业证据、事业探针答 yes 后下一问不是事业采集;答 no 仍问。BUG-546/547 既有用例仍过。 - 防复发:不得把点选答案写入证据账本来「修」重复采集。不得用
semantic_key前缀硬拆领域。不得加「除了刚才那次以外」这类补丁文案。 - 相关记录:BUG-389、BUG-546、BUG-547
- 复发自:无
- 修复版本:
988d97ae
BUG-550 | 带年份采集没问完就出采用卡,同一轮又被下一道采集题挤掉
- 状态:resolved
- 首次发现:2026-09-05
- 最近更新:2026-09-05
- 影响面:
decideRectification、decideFromDossier、decideAfterInferenceChange;next_user_action、offer 工具、采集焦点持久化、客户端时间卡四个消费者 - 用户现象:家人题答「没有」后出现财务口述题,下面已经挂着可点的候选时间卡;答完财务后助手写出整份生时校正验证报告并说可以采用某个钟点,紧跟着又问搬家,时间卡消失。
- 触发条件:学业 / 感情 / 事业已有带年份经历,家人已拒答,财务 / 搬家 / 健康 / 职业口述还没问也没拒答;候选分离度已经够给代表时间。
- 根因:决策层与计划层各说各的。
datedMethodCollectOpen只数四个阻断方法(Dasha / D9 / D10 / 家人),不数DATED_COLLECT_ORDER里还没问的学业、财务、搬家、健康与职业口述。分离度够时直接adopt_representative。BUG-546 只让剩余采集豁免deferAdoption,所以session_outcome已经是可采用,next_followup却仍是下一道采集。四个消费者听了不同的一层:next_user_action让模型写出牌报告并调 offer;offer 工具放行;焦点持久化仍写下道采集题;客户端按session_outcome出卡,未答题在场时才被 BUG-544 藏掉。 - 修复:传给
decideRectification的datedMethodCollectOpen叠加「采集计划的下一问仍是剩余带年份采集」。分离度够、尚未采用、用户未停止、也未耗尽时,保持collect_evidence,公开can_adopt=false。问完(覆盖、拒答)之后同一轮才adopt_representative,报告和卡片一起出,下面不再跟采集题。不改 Skill、不改引擎、不改 BUG-544 隐藏规则。用户说「没有更多」、已采用、已耗尽仍走原出口。 - 验证:
rectification-decide-next-action:分离且采集仍开 →collect_evidence/ask_fact_collection/ 公开不可采用;userStopped与accepted不因此改口。rectification-collect-direction-20260904:家人已拒、财务未问 → 决策采集、持久化财务、offer 工具offer_not_allowed;财务 / 搬家 / 健康 / 职业补齐后adopt_representative且next_followup为空。npx tsx --test tests/rectification-*.test.ts tests/agent-voice-copy-contract.test.ts904/904。 - 防复发:剩余带年份采集未完时,不得因分离度够就判
adopt_representative。不得只改客户端藏卡或只改 offer 工具描述来掩盖决策与计划矛盾。不得把「卡先给、问题照问」当成默认;那条路要产品另开单。 - 相关记录:BUG-544、BUG-546、BUG-547、BUG-549
- 复发自:BUG-546(计划层继续采集,决策层已经可采用)
- 修复版本:
ca4e2408
BUG-551 | 生成中禁用输入框,焦点丢失,回车消息被丢弃
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
ChatComposer、主咨询page.tsx、use-consultation-run.ts、生时校正rectification-agentic-chat.tsx - 用户现象:发出消息后输入框变灰,不能继续打字;回答结束后必须再点一次输入框。生成中按回车,消息被丢掉。
- 触发条件:任一对话面正在生成回答时打字或按回车。
- 根因:两个对话面把
isLoading/busy写进<textarea disabled>。浏览器会立刻移走焦点且解禁后不归还。send()在已有请求时直接return false,没有任何排队。 - 修复:输入框只在只读、会话加载、onboarding 或另一面打开时禁用。生成中回车把文字排成一条待发送,正常结算后自动发出;停止、失败或断线恢复时放回输入框。发送键在有排队时禁用。
- 验证:
frontend/tests/chat-composer-queue.test.ts;composer-ime-contract、composer-isolation-contract、rectification-surface-contract。 - 防复发:不得再用生成中状态禁用 textarea;生成中的发送必须进入排队或明确放回草稿,不得静默丢弃。
- 相关记录:BUG-216、BUG-329
- 复发自:无
- 修复版本:
055b7adc
BUG-552 | 生时校正停止穿报错衣服,选择题与采用等待中停止无效
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
rectification-agentic-chat.tsx、ChatMessageRow、选择题与采用fetch - 用户现象:生成中点停止后对话区出现红色告警,气泡按失败处理;还没有正文时思考行消失。回答选择题或点采用后的等待里,停止键在但点了没反应。
- 触发条件:生时校正流式回答中点停止;或选择题 / 采用请求尚未回到
send()时点停止。 - 根因:
AbortError把气泡写成failed并setError(RECTIFICATION_STOPPED_NOTICE),唯一出口是role="alert"的.error-message。空正文时flatMap丢掉该行。选择题与采用两条fetch没有挂AbortController到runAbort,停止是空操作。BUG-329 的防复发只锁了send()那一路 fetch abort。 - 修复:停止后气泡
settled+stopped,灰字说明已停止且不扣点,不再setError。无正文也保留气泡。选择题与采用的 fetch 挂上同一个runAbort。 - 验证:
frontend/tests/chat-composer-queue.test.ts、rectification-agentic-entry.test.ts、rectification-surface-contract.test.ts、chat-notice-and-scroll-contract.test.ts。 - 防复发:停止不得走
role="alert";busy时可见的停止键必须 abort 当前 fetch,包括选择题与采用,而不仅是send()。 - 相关记录:BUG-329、BUG-551
- 复发自:BUG-329(停止按钮只绑了
send()的 abort,没覆盖选择题与采用) - 修复版本:
055b7adc
BUG-553 | 历史列表只按置顶排,改名收藏后旧会话跳到最顶
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-15
- 影响面:侧栏历史列表、
GET /api/sessions、PATCH /api/sessions/[id]、use-session-management - 用户现象:刚聊过的会话不在最上面;改名、收藏、换模型或换资料后刷新,这条会话反而排到最顶。
- 触发条件:登录后打开侧栏历史;对已有会话做元数据 PATCH,或不发新消息就刷新。
- 根因:
visibleSessions只按pinned排,开着页面时updatedAt变化不改位置。元数据 PATCH 和服务端metadataUpdateValues一律写updated_at = now(),非对话操作也会把会话顶到列表头。真正该 bump 的是发问 / 回答落库。 - 修复:
sortSessions置顶优先、组内updatedAt倒序且稳定。元数据 PATCH 不再写updated_at;客户端改名 / 收藏 / 归档 / 换模型 / 换资料不再timestamp()。列表改为游标分页,历史按本地日期分组,侧栏标题去掉资料名前缀。 - 验证:
frontend/tests/session-groups.test.ts、session-cursor.test.ts、chat-session-write.test.ts(metadataUpdateValues不含updated_at)、sidebar-state.test.ts(改名不 bumpupdatedAt)、sidebar-contract.test.ts、chart-library-session.test.ts。 - 防复发:元数据 PATCH 不得写
updated_at。updatedAt只由对话活动推进,校正答题也算(BUG-704)。侧栏历史排序必须置顶 +updatedAt倒序,不得只按置顶。打开已有会话不得改title/updatedAt,由 BUG-699 与frontend/tests/session-open-preserves-identity.test.ts执行。?c=不在已加载页时先问服务端(BUG-705)。 - 相关记录:BUG-024、BUG-699、BUG-704、BUG-705
- 复发自:无
- 修复版本:
a1956deb
BUG-554 | 设置弹窗随分区跳变,星盘资料铺开整张表单,账户与点数离开首页
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:设置弹窗、
chart-library-panel、billing-panel、首页page.tsx、/membership路由 - 用户现象:三个设置分区弹窗尺寸不同;星盘资料列表下面无条件铺开添加表单;点「账户与点数」会离开首页打开整页会员。
- 触发条件:登录后打开设置弹窗,切换个人资料 / 星盘资料 / 通用设置,或从余额、头像菜单、余额不足进入充值。
- 根因:
accountDialogClasses给每个分区不同宽度,高度随内容撑开。星盘资料没有列表/详情视图,添加表单始终渲染。第四个导航项直接router.push到/membership。 - 修复:四个分区共用
.settings-modal固定尺寸。星盘资料改为列表→详情。账户与点数成为弹窗第四分区;删除/membership与/membership/orders,旧链接重定向到/?settings=billing。七处入口改为openAccountDialog("billing")。 - 验证:
frontend/tests/account-dialog-overlay.test.ts、billing-panel.test.ts、membership-page.test.ts、membership-navigation-contract.test.ts、chart-library-other-profile.test.ts、settings-url.test.ts、beam-avatar.test.ts、class-name-definition-contract.test.ts、tests/test_api_server_security.py -k capability_audit。next build --webpack中/为 Static,无/membership页面。 - 防复发:设置弹窗必须同时声明 width 与 height;星盘资料默认视图不得无条件渲染
<form;/保持 Static,不得useSearchParams;收银台仍window.open;硬跳转清单不得因本单变长。(2026-09-16 修正,见 BUG-698):「必须同时声明 width 与 height」这条防不住复发——它检查的是声明存在性,不是声明是否生效。.settings-modal后来 width 与 height 都老实写着,却因为 height 只用dvh、不认识该单位的引擎整条作废而再次随分区跳变。固定高度的单位回退口径以 BUG-698 为准。 - 相关记录:BUG-434、BUG-251、BUG-143
- 复发自:无
- 修复版本:
dc6598d7
BUG-555 | 普通咨询历史只留每条开头 4000 字,下一轮看不到应期和审计表
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
POST /api/consult、consultation-session-history.ts、session-context-summary.ts、chat_sessions.context_summary - 用户现象:追问「你上次说的应期」时,模型像没看过上一轮。Level 2 报告的应期、综合判断和技法审计表在下一轮消失,也没有任何截断提示。上下文超限时走普通失败,没有缩窗重试。
- 触发条件:普通咨询多轮追问;单条助手回复超过 4000 字;或累计历史超过模型窗口。
- 根因:历史窗口写死为最近 12 条 × 每条开头 4000 字,不知道模型
context_window,也不保留结论。超限错误没有识别为context_overflow。 - 修复:历史改为摘要之后的 append-only 尾巴,按模型窗口算预算,超长从最旧整条丢。单条上限 12,000 字并写明省略字数。尾巴超过 16,000 字时在结算后异步做检查点摘要(不扣点、15 秒 ref 超时、乐观并发)。识别上下文溢出后同一请求内缩到「摘要 + 最后一对」重试一次。
- 验证:
frontend/tests/consultation-session-history.test.ts、session-context-summary.test.ts、consultation-context-cache-contract.test.ts。 - 防复发:咨询历史不得再按固定 12 条 × 头部截断静默砍结论。摘要超时不得用
AbortSignal.timeout()。溢出重试不得新增用户可见等待态。 - 相关记录:BUG-523
- 复发自:无
- 修复版本:
bf8ad0d1
BUG-556 | 缓存命中已写入账本,后台看不到;Anthropic 历史没有第二断点
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
api/admin/usage/aggregate、api/admin/usage、定价测算页、用量列表、cachedHistoryMessage、skill-binding共享方法段 - 用户现象:后台用量页没有缓存命中率或缓存读 tokens。Anthropic 只缓存了系统块,历史每轮全价重算。跨领域不变的 Full-spectrum 与 Event judgment skeleton 每次跟着工具结果重发。
- 触发条件:普通咨询跑过后打开用量聚合或用量列表;使用 Anthropic 或多领域咨询。
- 根因:
complete_usage已把metadata.cache写入usage_ledger,聚合与列表都不读。历史消息没有 AnthropiccacheControl。共享方法段放在工具结果里,不在可缓存系统块。 - 修复:聚合按模型给出 7 天 / 30 天命中率与读写未命中;列表加「缓存读 tokens」。历史尾巴最后一条在 Anthropic 上打第二断点。共享两段搬进
<jyotish-shared-method>。 - 验证:
frontend/tests/admin-usage-aggregate-contract.test.ts、agent-generation-settings.test.ts、skill-binding.test.ts、consultation-methodology.test.ts。 - 防复发:有
metadata.cache的运行必须进入命中率分母;readTokens = 0计未命中。无缓存数据的行不得进分母。系统块 9 段节选与providesSkillDiscovery: "on-demand"不得回退。 - 相关记录:BUG-555
- 复发自:无
- 修复版本:
bf8ad0d1
BUG-557 | 会话标题守卫比较 RPC 前快照,模型标题写库恒 0 行
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
POST /api/consult首轮会话标题 - 用户现象:新会话首轮模型起的标题,刷新、关标签或换设备后变回问题截断标题。
- 触发条件:普通咨询新会话发第一问,在客户端 PATCH 标题之前刷新或离开。
- 根因:
append_consultation_question会把空标题 / 「新对话」改成问题前 14 字。守卫却用 RPC 前快照做.eq("title"),更新恒 0 行且不报错。 - 修复:
shouldGenerateSessionTitle仍看 RPC 前标题。RPC 成功后再读一次当前标题做守卫。更新带.select("id"),0 行console.warn("session title guard missed")。 - 验证:
frontend/tests/session-title-persist-contract.test.ts、application-billing-contract.test.ts。 - 防复发:标题守卫必须比较 RPC 后的
chat_sessions.title,并断言影响行数。不得用 TS 重算 SQL 的截断规则。 - 相关记录:BUG-553
- 复发自:BUG-553(
a1956deb的测试没断言守卫会命中) - 修复版本:
0102a973
BUG-558 | 采集问完落到「也可以再说一件事」,没有交付出口
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
persistExhaustionCollect、exhaustionSpokenCollectFollowup、ensureNonTerminalTurnExit、口述采集 UI、POST /api/rectification/agent - 用户现象:七个带年份领域和职业都问过之后,助手仍问「也可以再说一件你记得大概时间的事」,界面只剩不可点的范围小字,没有候选卡、没有停止按钮、没有下一步。
- 触发条件:方法覆盖完成、带年份领域都问过或拒过、职业已覆盖;非收敛出牌或非终止修复走穷尽采集。
- 根因:
exhaustionSpokenCollectFollowup在 dated / occupation 之后无条件落到otherCollectFollowup。domain="other"不在剩余采集集合里,也不挡出牌,但作为collect:other:*焦点被写回后下一轮还回到同一句,没有终止条件。口述采集没有停止按钮;ask_about_result只在点选焦点处理。 - 修复:穷尽采集不再问
other。还有 dated / occupation 就继续问;否则acceptance_allowed且训练门开且可采用则adopt_representative(stopReason=probe_pool_exhausted),否则写门槛翻译句、不写焦点,并用terminalNote满足非终止出口。口述采集在输入框上方放与点选卡同文案的停止按钮;只读范围改为同一停止动作。ask_about_result与stop_rectification在点选和口述焦点都映射到停止。keep_collecting仍继续采集,不得劫持成出牌。 - 验证:
frontend/tests/rectification-exhaustion-exit-20260906.test.ts;既有 adopt / spoken-collect / timeline / hidden-e2e 三栏更新。 - 防复发:
exhaustionSpokenCollectFollowup不得再返回other。穷尽日志必须有rectification_exhaustion_collect且不含证据摘要或年份。不得把keep_collecting改成强制采用。 - 相关记录:BUG-550、BUG-546、BUG-586
- 复发自:无
- 修复版本:
150d7ef1
BUG-559 | 同年月拆成多道「有没有 X」,同域同年再问,无锚点性格卡
- 状态:mitigated(点选同年复发见 BUG-592)
- 首次发现:2026-09-06
- 最近更新:2026-09-08
- 影响面:
scripts/rectification/event_probes.py、asked_probe_keys、existenceProbeAsked、rankRenderableDiscriminators - 用户现象:同一大运边界月按领域各问一次「那时候有没有入职 / 有没有搬家」;已问过某年 5 月后又问同年无月份;没有对应经历的「平时做事更接近哪一种」性格卡也会出。
- 触发条件:引擎按领域扫描边界月;去重只看账本年份不看已问探针;
varga_style不要求同域带年月证据。 - 根因:existence 探针按
(domain, year, month, source)独立出题;_existence_blocked_years不并入已问探针年份;性格对照卡没有锚点门槛。 - 修复:请求可带
asked_probe_keys(不进结果指纹)。已问domain.Y.MM.*后,同年与相邻年不再出该领域 existence / activation。varga_style无同域 confirmed 带年月事件则dropped_probes(unanchored_varga_style);有锚点时 hint 带年月、分数 ×0.8,用户可见题干仍不写年份。邻近账本事件以领域+年月(无摘要)写入user_prompt_hint。B2 同年月合并卡choice_kind=boundary_pair本单未做。 - 验证:
tests/test_rectification_event_probes.py已问同年相邻年不再产出;frontend/tests/rectification-probe-year-dedupe-20260906.test.ts。 - 防复发:不得把
asked_probe_keys写入结果指纹。点选答案仍不写证据账本。不得为性格卡在用户可见题干里写年份。 - 相关记录:BUG-540、BUG-390、BUG-592
- 复发自:无
- 修复版本:
3a9ae736
BUG-560 | 相对支持度正比例归一把引擎证据压成 7–9,lead 8 不可达
- 状态:blocked
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
scripts/rectification/decision_policy.py::_relative_support、公开簇代表 prior、前端unionStillValidRange - 用户现象:多件带年月经历之后,公开候选相对支持度仍是 7–9,范围几乎不收;之后只靠点选卡 ±2 推分,问尽仍收不窄。
- 触发条件:公开最多 12 个簇代表按原始分正比例把 100 分掉;原始分底座远大于事件极差。
- 根因:
_relative_support按原始分正比例归一。TASK-rectification-provisional-adopt-20260901.md§2 已看到 lead 停在约 5,当时只改门、四个常数不动,没有改 prior 尺度。 - 根因升级(2026-09-06):校准门「覆盖 ≥ 旧方案 − 1 且中位宽度更窄」设计有缺陷。旧方案 proportional 在 20 例公开 holdout 上中位宽度 21 分钟 = 整个候选窗口(半径 10),覆盖 17/20 只是因为不收窄。先验一旦按引擎原始分拉尖(offset),真实分钟落在领先集合里只有 12/20(中位宽 10),与随机取 10 分钟(≈48%)相当。引擎原始分在分钟级几乎没有区分力;现有八法计分不足以在 20 分钟窗口内可靠收窄。「收敛」只能来自用户答题和更强的引擎证据,不能靠换尺度。本条状态保持 blocked,未启用新尺度。
- 修复:实现 offset(分−全网格最低分)与 softmax,由校准脚本在 20 例公开 holdout 上比覆盖与中位宽度。合入门:覆盖 ≥ 旧方案 − 1 且中位宽度更窄。实测无方案过门(offset 覆盖 12/20 < 16;softmax T=2 中位宽度 21 不小于旧值 21)。默认仍
proportional,POLICY_VERSION/ALGORITHM_VERSION不 bump,旧 Case 不因此自动重算。 - 验证:
scripts/rectification_prior_calibration.pyholdout 表见进度记录;tests/test_rectification_relative_support.py锁定默认 proportional,并断言显式 offset 在拉开的格子上领先不差于 proportional。 - 防复发:不得在校准门未过时启用 offset/softmax 或 bump 政策版本。不得改
MIN_SEPARATION_LEAD=8或采用/确认/熔断常数来「先让分看起来够」。 - 相关记录:BUG-463、
TASK-rectification-provisional-adopt-20260901.md§2 - 复发自:无
- 修复版本:未启用新尺度;代码与校准脚本
62afa521
BUG-561 | 长报告 KP Lord/Sub 与专题 KP 表在失败信封下整段消失
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
scripts/jyotish_engine.pypl9 长报告、专业参考 Markdown - 用户现象:三年 KP 月表还在,但 KP Lord/Sub 原始表和事业/财运/关系章的 KP 结构小表整段不见,页面上也没有 blocked 说明。
- 触发条件:专业参考路径把
cmd_kp收成{status: blocked, reason}且没有 planets/houses;渲染层遇到该信封直接返回空列表。鉴别条件是候选窗/blocked KP 信封,不是 Raman 本身。 - 根因:
_kp_lord_sub_section等装配函数在 KP 模块失败时return [],计划段被静默跳过。 - 修复:失败路径改为输出显式 blocked 标题和原因码;最终 Markdown 再扫一遍计划标题,缺席则补
planned_section_unavailable。 - 验证:
tests/test_report_longform_gaps2.py。 - 防复发:计划内段必须 present 或 blocked,不允许缺席。禁止再对失败 KP 信封直接
return []。 - 相关记录:无
- 复发自:无
- 修复版本:
cfcd369d
BUG-562 | 专业参考路径剥掉 raw_full_reading 后模块审计表只剩表头
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
scripts/jyotish_engine.py长报告审计表、力量章 Yogini/Narayana/Kala/Bhava Bala - 用户现象:逐模块执行审计只有表头、零数据行。力量章缺 Bhava Bala 与三套辅助大运明细。
- 触发条件:professional 路径 sanitize 掉
raw_full_reading;worksheet 只抄了 dasha 家族状态、没有 periods。 - 根因:审计行只从将被剥离的 raw 模块读;辅助大运渲染拿不到规范化 periods。
- 修复:sanitize 前抽出
_iter_audit_modules;规范化 dasha family periods,并从本地 yogini/narayana/kala/bhava_bala 模块补装,缺数据走 blocked 行。 - 验证:
tests/test_report_longform_gaps2.py。 - 防复发:full pack 下审计表数据行必须达到模块执行数下限;力量章六子块缺数据必须有 blocked 标题。
- 相关记录:BUG-561
- 复发自:无
- 修复版本:
cfcd369d
BUG-563 | 报告 MD 详情页在无 IntersectionObserver 时同步 setState,staging 门禁 lint 失败
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
frontend/src/components/personal-report/personal-report-markdown-view.tsx、Giteabackend-quality-gaterun 2446 - 用户现象:staging 未部署本轮报告 MD 页;门禁
npm run lint1 error 后publish/deploy-staging未跑。站点/api/health的deployment.gitCommit仍停在此前 SHA。 - 触发条件:
LazyMarkdownSection在IntersectionObserver未定义时(SSR / 部分测试环境)于useEffect内同步setVisible(true)。 - 根因:react-hooks 编译器规则禁止 effect 内同步 setState,以免级联渲染。仓库红线是 lint 0 error。
- 修复:无观察器时改为
requestAnimationFrame后再setVisible,并在清理函数里cancelAnimationFrame。SSR 首屏仍只渲染 eager 段。 - 验证:
npm run lint -- src/components/personal-report/personal-report-markdown-view.tsx0 error;frontend/tests/personal-report-markdown-view.test.ts仍断言懒加载段不进 SSR 标记。 - 防复发:报告 MD 视图 effect 不得同步 setState;后续 lint 0 error 才允许合入 staging。
- 相关记录:BUG-561、BUG-562
- 复发自:无
- 修复版本:
ebc8380f
BUG-564 | 长报告 heading-ensure 之后仍丢逐月 KP 标题、时间来源与分钟排行
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
scripts/jyotish_engine.pypl9 长报告、scripts/flexible_birth_time_report_section.py、scripts/run_quality_gate.py快速门 - 用户现象:有 KP 月份数据时,事业/财务/关系三条「逐月 MD/AD 支持」标题仍不见;时间节点归因表的来源列被脱敏成空;校时附录没有分钟排行。快速门也不跑 gaps2 合同。
- 触发条件:
kp_monthly_report.months非空;professional 路径 sanitize 掉source_path;候选窗存在但渲染层不读candidate_times。 - 根因:
_kp_monthly_theme_brief成功路径不写标题,空包才靠文末 heading-ensure 补 blocked 标题;parity_gates 用source_path,sanitize 会剥掉任何_path键;CLI provenance 只读显式uncertainty_minutes;分钟排行未装配。顺带d1_d60_ledger/support_topics.kp为 None 时.get会在有月份数据的渲染里炸掉。 - 修复:逐月 brief 始终输出标题,空数据走 blocked;parity_gates 改
source_label;窗口宽度回填 uncertainty;校时附录列出候选分钟;None 守卫;快速门列入 gaps2 / parity / timing / flexible 合同。 - 验证:
tests/test_report_longform_gaps2.py、tests/test_flexible_birth_time_report_contract.py、tests/test_timing_precision_contract.py。 - 防复发:有月份数据时三条逐月标题必须各出现一次,且不得靠文末 blocked 标题顶替;sanitize 后 Markdown 仍须保留可读 source_label;快速门必须跑上述四个测试文件。
- 相关记录:BUG-561、BUG-562
- 复发自:BUG-561(
cfcd369d的 heading-ensure 只覆盖空包缺席,未覆盖成功路径漏标题) - 修复版本:
e4d16b75
BUG-565 | 穷尽门槛句每个回合写成两条相同助手消息
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
persistExhaustionCollect、finalizeSuccessfulTurnExit、runV9AgentTurnopening/evidence、applyRectificationChoice点选路径、/api/rectification/agent - 用户现象:方法覆盖完成、无剩余采集、引擎采用门关闭时,同一段「范围已经收到…还差带月份的经历」连出两三遍。
- 触发条件:A4 用例 2 形状上
finalizeSuccessfulTurnExit({ action: "message" })、action: "evidence"经 agent-run,或点选最后一张关闭天花板的卡。 - 根因:门槛翻译句被当成独立消息。
persistExhaustionCollect自己persistV9DeterministicTurn(requestId: randomUUID())写第 1 条;inspectNonTerminalTurnExit.satisfied不认门槛句为载体,再写第 2 条。opening/evidence 在 finalize 前还会先 idle 一次。点选路径一条独立门槛消息,又把同一句拼进「已记录你的选择」。 - 修复:门槛句是回合正文,写入方只能有一个。
persistExhaustionCollect只返回{ hostNarration, terminalNote: true, persisted: false },不再写 turn。idle 返回terminalNote且本回合尚未写过门槛时,由finalizeSuccessfulTurnExit/ agent-run / route 用幂等requestId写一次并跳过非终止修复。点选路径只把门槛句并入该回合正文。 - 验证:
frontend/tests/rectification-exhaustion-exit-20260906.test.ts:message finalize、evidence agent-run、最后一张卡各计 1 次含门槛句的助手 append;A4 用例 2 后再调ensureNonTerminalTurnExit,append 不增加。非 DB 校正套件 910/910。 - 防复发:
persistExhaustionCollect不得再调用persistV9DeterministicTurn。门槛requestId必须可幂等。inspectNonTerminalTurnExit必须把穷尽态视为已交付。 - 相关记录:BUG-558、BUG-566
- 复发自:BUG-558(穷尽交付把门槛句做成独立消息,出口修复未锁单一写入方)
- 修复版本:待合入
origin/staging(codex/rectification-convergence-exit-fix-20260906)
BUG-566 | 穷尽分支排在问题持久化之前,吞掉仍可问的区分卡和 holdout
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
persistNextInterviewIfIdle、isExhaustedGateState、决策层ask_candidate_discriminator/ask_holdout_validation - 用户现象:引擎采用门关闭(例如
low_date_quality)后,聊天只剩门槛句,不再出现还能分开候选的选择题或盘外核对。 - 触发条件:无剩余采集、方法覆盖完成、
canAdopt=false,但决策层仍给出可渲染区分卡或 holdout 题。 - 根因:idle 穷尽分支只看「无剩余采集且不可采用」,不看
decision.nextAction,排在persistNextInterviewAfterChoice之前。 - 修复:抽
isExhaustedGateState。idle 穷尽分支额外要求nextAction ∈ {offer_provisional_range, complete_with_range, ask_fact_collection}。区分卡 / holdout 照常持久化。inspectNonTerminalTurnExit与 idle 共用穷尽态判定。 - 验证:同测试文件:关闭天花板但留一条可渲染探针 → 持久化区分卡焦点,无门槛句;holdout 仍开 → 持久化 holdout,无门槛句。
- 防复发:不得把穷尽门槛排在
ask_candidate_discriminator/ask_holdout_validation之前。不得重新引入USER_COLLECT_QUESTION.other兜底。 - 相关记录:BUG-558、BUG-565
- 复发自:BUG-558(穷尽交付分支未白名单 nextAction)
- 修复版本:待合入
origin/staging(codex/rectification-convergence-exit-fix-20260906)
BUG-567 | 范围小字伪装成停止按钮,文案仍是状态句
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
RectificationReadonlyRange、口述采集停止按钮、frontend/DESIGN.md - 用户现象:范围一行写着「目前范围 …–…,还在收窄」,看起来不可点,点了却等于「先这样」。同一屏下方已有明确停止按钮。
- 触发条件:口述采集或只读范围可见时。
- 根因:BUG-558 A3 把只读范围改成
<button>并挂onStop,文案未改成动作句。 - 修复:范围行还原为
<p role="status">,删除onStop。口述采集输入框上方的「先这样,先看当前范围」按钮保留。 - 验证:源扫描断言范围函数没有
onStop/<button,口述停止按钮仍用CHOICE_STOP_LABEL。 - 防复发:
RectificationReadonlyRange不得再渲染成按钮或接受停止回调。 - 相关记录:BUG-558
- 复发自:BUG-558(A3 把范围小字做成第二个停止入口)
- 修复版本:待合入
origin/staging(codex/rectification-convergence-exit-fix-20260906)
BUG-568 | 报告与聊天按开工搜索窗口读盘,没用采用时的可信区间
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:
read_report_candidate_range、采用 RPC、consultation-route-serviceverified_chart、jyotish_engine._candidate_minutes、采用旁白 - 用户现象:采用一段 10~30 分钟可信区间后,个人报告的出生时间敏感度仍按开工搜索窗口分层;聊天把代表分钟当成精确出生分钟排盘。
- 触发条件:校正采用成功后打开报告或新建
verified_chart咨询。 - 根因:采用 RPC 只写代表分钟,不落库可信区间。报告 RPC 读开工
candidate_range。引擎窗口超过 15 分钟只取 3 个样本。聊天accepted不传区间。 - 修复:采用写入
adopted_credible_range,不改开工candidate_range。报告 RPC 优先读采用区间。≤31 分钟逐分钟采样,更宽等距 31 点。采用旁白追加服务端「稳定 / 随分钟变」两句。accepted咨询按provisional+ 区间读盘;confirmed不变。声明时段咨询复用同一分层并标明粗看。 - 验证:
frontend/tests/adopted-credible-range-migration.test.ts、frontend/tests/rectification-range-reading-20260906.test.ts、采用旁白范围读盘用例、tests/test_flexible_birth_time_engine.py27/60 分钟采样。Dockertest:db见测试说明;无 Docker 时不得标通过。 - 防复发:采用不得改写
candidate_range。报告窗口必须先读adopted_credible_range。flexible_birth_time_profile上限 31。稳定/敏感句不得由模型补写。 - 相关记录:BUG-560
- 复发自:无
- 修复版本:待合入
origin/staging(codex/rectification-convergence-exit-fix-20260906)
BUG-569 | 答后旁白把搜索窗口当成可信区间,每题都说「范围没变」
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
composeChoiceNarration、applyRectificationChoice、probe-explain.explainRangeChange - 用户现象:顶部只读范围已经随答题收窄,助手旁白仍说「范围没变」。
- 触发条件:点选区分卡 A/B/C,搜索窗口
range_start/range_end不变,而credible_range变了。 - 根因:
applyRectificationChoice把InferenceState.range_start/range_end(开工搜索窗口)传给旁白。答题改的是credible_range。直喂composeChoiceNarration的单测没走这条路径。 - 修复:旁白改比
previous.credible_range与applied.state.credible_range。入参改名credibleBefore/credibleAfter。任一为空则不说范围句。 - 验证:
frontend/tests/rectification-answer-choice.test.ts:搜索窗口固定、credible_range收窄时旁白含「范围从 X 收到 Y」;credible_range不变时仍说「范围没变」。源扫描锁定不再传range_start。 - 防复发:不得把
range_start/range_end再传给答后范围句。新增端到端用例必须走applyRectificationChoice,不能只直喂纯函数。 - 相关记录:BUG-568
- 复发自:过程解释层(
814c924e)把搜索窗口误当可信区间 - 修复版本:
517df002
BUG-570 | 时段支持度按段内原始分求和,长时段先天占优
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
scripts/rectification/api_service.py::block_scan - 用户现象:完全不清楚出生时间时,第一张时段卡下午段支持度偏高,即使各分钟分数相同。
- 触发条件:
stage=block_scan、10 分钟步长扫 24 小时。五段候选数 24/24/36/30/30。 - 根因:
relative_support用段内sum(score)。下午 36 个候选,清晨 24 个;引擎原始分底座远大于事件差,于是在没有证据差异时也是在比时段长度。 - 修复:
raw_support = max(mean(score in block) − min(all scores), 0),再归一。全部为零时五段各 20%。不进采用门,不改分钟级_relative_support。 - 验证:
tests/test_rectification_v5_services.py:144 行同分 → 五段各 20.0;只抬高清晨 → 清晨最高,下午不再因长度领先。minute_step=1指纹用例未改。 - 防复发:
block_scan不得再对段内原始分求和。分钟级 prior 仍走原来的正比例归一(BUG-560)。 - 相关记录:BUG-560
- 复发自:unknown-time
block_scan(814c924e)按时段长度偏置 - 修复版本:
517df002
BUG-571 | 档案里声明的不确定度被忽略,有钟点一律 ±15
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
frontend/src/lib/birth-time-intake-model.ts、birth-time-intake.tsx、account-profile-patch.ts、case-service.ts::deriveRectificationOpenPlan - 用户现象:家人记得大概时间时,intake 选不到前后半小时 / 一小时 / 两小时;校正开工窗口仍是填报钟点 ±15,档案里的
uncertainty_before/after不参与搜索。 - 触发条件:资料有填报钟点;来源本应是
approximate或医院记录。 - 根因:intake 单选只露出
family_exact/period_only(含 unknown)。开 Case 用固定FRESH_CASE_SEARCH_RADIUS_MINUTES = 15,注释写明那是引擎边界、不是用户声明。 - 修复:intake 改三档——医院记录(固定 ±2 无感检查)、家人大概时间(±15/30/60/120)、时段或完全不知道。
family_exact从新选项移除,读档映射成「差不多准」。有钟点时:hospital_record/family_exact→ ±15;approximate→ ±max(15, 声明值),上限 120,前后独立。窗口 exclusive span >120 先进block_scan(BUG-573)。 - 验证:
birth-time-intake.test.ts三档与 120 校验;account-api.test.ts120 合法、90 非法;rectification-v9-case-service.test.ts既有三处family_exact04:45–05:15 保留,新增 approximate ±60 → 04:00–06:00、±120 → 03:00–07:00。 - 防复发:有钟点开 Case 不得再写死 ±15;
approximate校验集合必须与 intake 按钮同为 {15,30,60,120}。 - 相关记录:BUG-572、BUG-573
- 复发自:无
- 修复版本:
8e31680b
BUG-572 | 事件吻合率低且贴窗口边缘时不提议放宽搜索窗
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
decision-from-dossier.ts、window-widen.ts、method-followup.ts、widen_agentic_rectification_case_window、set_agentic_rectification_widen_declined - 用户现象:经历已经明显对不上家人记的时间,校正仍只在原来的 ±15/±30 里出区分卡,没有一键放宽。
- 触发条件:训练门已开、
event_fit_rate.band=low且total≥3、代表分钟距窗口边缘 ≤3 分钟、stage=minute、未采用、有填报钟点、当前证据指纹未拒过、当前半径 <120。 - 根因:
event_fit_rate只进采用后八法报告。分钟阶段没有改candidate_range的 RPC;唯一改窗 RPC 要求stage=block_scan。 - 修复:服务端四选卡
choice_kind: widen_window(不进 Python 探针契约),排在区分卡之前。A/B 扩大窗口(必须包含旧窗、inclusive 宽 ≤241)、重算,并把档案写成approximate+ 新半径;hospital_record只改窗口不改档案来源。C/D 记widen_declined_at_fingerprint。档位 15→±30/±60,30→±60/±120,60→±120 / 「先补一件带月份的经历再说」。D 文案「说不好 / 先这样」(clippedCopy最短 4 字)。不动采用/确认门、MIN_SEPARATION_LEAD、分钟级_relative_support、adopted_credible_range。 - 验证:
rectification-window-widen-20260907.test.ts六条;迁移合同锁 widen RPC 与不得改adopted_credible_range。Dockertest:db在database-rectification-block-scan.test.ts加 widen 扩大/缩小与权限。 - 防复发:放宽卡必须服务端生成;不得让模型问「偏移 / 误差」;widen RPC 只许扩大且包含旧窗。
- 相关记录:BUG-571、BUG-573
- 复发自:无
- 修复版本:
8e31680b
BUG-573 | 选定时段或 ±120 窗口后直接进 4~6 小时分钟网格
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
scripts/rectification/api_service.py::block_scan、contracts.py可选blocks、advance_agentic_rectification_case_from_block_scan、search-window.ts、block-scan.ts - 用户现象:只知道傍晚或家人说前后两小时时,选定后马上在 4~6 小时上按分钟比,网格过密、题也问不清。
- 触发条件:exclusive span >120(例如 18:00–22:59、03:00–07:00、下午 12:00–17:59)。
- 根因:
block_scan只服务全日五时段;advance_*要求原窗 00:00–23:59,选定后一律stage=minute。set_stage也曾要求全日窗。 - 修复:请求可带 1~5 段
blocks(须落在窗口内、除共享端点外不重叠)。无blocks时仍用五声明时段;全日默认步长 10。子窗三等分、无共享分钟;步长 >360→10、>180→5、其余 2。advance_*改为新窗须为当前窗子区间;span>120 且 rounds<3 仍block_scan,否则minute。每 Case 最多 3 张子段卡,超过旁白「时段分不开,直接按分钟比」。unknown 仍先五时段,赢段再切三段。全零相对支持按 100/n 均分(三段 33.3/33.3/33.4),不再写死五段各 20。不改分钟级_relative_support。 - 验证:
test_rectification_v5_services.py自定义三段和为 100、等分 33.3/33.3/33.4、越界/重叠报错;rectification-block-scan-20260906.test.ts±120 开 Case 为 block_scan、傍晚三子段、选下午后仍 block_scan;Docker 上午 08:00–11:59 选定后仍 block_scan,08:00–09:00 进 minute。 - 防复发:exclusive span >120 不得直接
stage=minute。block_scan全零均分必须按段数,不得写死 20%。 - 相关记录:BUG-570、BUG-571
- 复发自:unknown-time 选段后一律进分钟(
814c924e) - 修复版本:
8e31680b
BUG-574 | 报告列表 JSON 路径 select 在 staging PostgREST 上 500
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
GET /api/reports、personal_reports列表查询、报告中心卡片 - 用户现象:登录后报告中心无法打开列表,接口稳定返回 500
{"error":"报告列表暂时无法读取","code":"report_generation_failed"}。同一会话读取已有报告详情 200。 - 触发条件:部署含
cfcd369d的应用后打开报告中心。自托管 PostgREST 解析card_summary:report_document->executiveSummary->>summary失败。 - 根因:列表
select增加了 PostgREST JSON 路径别名。该语法未被test:db或真实 PostgREST 覆盖;supabase-js 返回的 PostgrestError 不是Error实例,列表日志只记UnknownError。 - 修复:列表改为普通列
card_summary;迁移增加可空text列(≤500 字符);complete_personal_report_job从封面文档executiveSummary.summary(MD「摘要」截取)写入。旧行不回填。列表 catch 记录code/hint,不记行数据。 - 验证:
frontend/tests/database-personal-report-card-summary.test.ts(可空、authenticated 只读、service_role 可写、超长拒绝、complete RPC 写入);列表/观测单测;staging72dd5e9d已部署,health 账本为20260907010000_personal_report_card_summary.sql;未登录列表 401。登录态列表 200 与新报告卡片摘要仍待真实会话。 - 防复发:新增 PostgREST JSON 路径、嵌套 embed 或别名必须有
test:db真查询或部署后 smoke;禁止再用未经验证的 JSON 路径变体赌列表查询。 - 相关记录:无
- 复发自:无
- 修复版本:
b466a6fc8cbb17d48c5aa242dbafd44b9a7c651b
BUG-575 | 生时校正选项悬停时间解释闪动,输入框上方步骤提示是套话
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
RectificationChoiceCard、RectificationAgenticChat输入框上方步骤条 - 用户现象:鼠标在 A/B/C/D 选项间移动时,选项下方出现「会让 … 这段领先/落后」并随悬停切换不断闪动;输入框上方还有「第 N 步·… — … — 下一步:…」套话。
- 触发条件:生时校正出现选择题后,鼠标在选项间来回移动;或进入校正对话看到输入框。
- 根因:选择题把
answer_impact绑到onMouseEnter/onFocus,悬停切换会改写同一行文案并撑开布局。步骤条把服务端step_state拼成输入框上方提示。 - 修复:选项不再监听悬停、不再渲染
answer_impact。输入框上方不再展示步骤条;采集题的「先这样」按钮仍保留在输入框上方。服务端仍可生成answer_impact和step_state,只是用户界面不再读出来。 - 验证:
frontend/tests/rectification-surface-contract.test.ts、frontend/tests/rectification-step-state-20260906.test.ts、frontend/tests/rectification-v9-contracts.test.ts。 - 防复发:选择题源码不得再出现
onMouseEnter或rectification-choice-impact;聊天源码不得再出现rectification-step-state。 - 相关记录:无
- 复发自:无
- 修复版本:
1ded6bb0
BUG-576 | MD-only 长报告引擎 200 后附录 upsert 未带冲突键,staging 三次重试仍 failed
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
generatePersonalReportLongform、personal_report_longform_appendices、自托管createLocalPostgresDataClientupsert、报告详情失败态 - 用户现象:personal_full MD-only 首次真实运行在引擎调用阶段失败(
calculation_unavailable,progressPercent=30)。直连引擎同形虚构盘 HTTP 200。 - 触发条件:staging 自托管 web worker 调用
/api/professional_report_reference成功后写入 longform 附录。 - 根因:本地 postgres 客户端的
upsert必填onConflict;附录写入只调用.upsert(row)。请求在写库前抛错,附录表 0 行。Job 三次尝试均打到引擎(三次 HTTP 200,约 26–32s,低于 180s 超时)。不是 api 容器滞后,也不是引擎超时。web 容器 docker logs 几乎只有启动行,附录错误码此前也不在详情 API 中。 - 修复:附录 upsert 显式
onConflict: "report_id";本地客户端缺省冲突键回落到主键;详情失败态透出appendixLastErrorCode与可读原因;引擎调用记录http_status/duration_ms;api 容器与/api/health暴露构建 SHA。 - 验证:定向 frontend 单测、附录
test:dbupsert 无onConflict、health/compose 源合同。未改.gitea/workflows/**,不提升 main。 - 防复发:自托管 upsert 必须能在缺
onConflict时按主键执行;附录写入必须带 PK 冲突键;失败详情必须带附录错误码。 - 相关记录:BUG-574
- 复发自:无
- 修复版本:
e87c58d6e48850749ea9e0a92e532133db03e292
BUG-577 | 性格卡答过之后,后续每次候选比较都被 400 拒绝
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
asked_probe_keys、normalize_rectification_request、rectification-compare-candidates、inference-adapter.ts - 用户现象:性格对照卡或月宿边界卡答完后,再说一件事时候选比较立刻失败(约 30ms,不是引擎超时)。顶部范围不再随新经历变化。
- 触发条件:答过带
candidate_split_hash的性格/边界卡后,再走候选比较。 - 根因:BUG-559 把
probe_id、semantic_key、candidate_split_hash都塞进引擎asked_probe_keys。性格卡 hash 约 128 字符,引擎原上限 120,整单请求 400。去重仍应靠semantic_key,不该把 hash 传给引擎。 - 修复:引擎请求只传
semantic_key与账本年份键;TS 再丢掉>200或含:varga.的键。引擎上限改为 200,超长键跳过并在 receipt 计数dropped_asked_probe_keys,不再 400。128 字符键在新上限下会保留(决策是提上限,不是跳过 128)。 - 验证:
tests/test_rectification_input_contract.py;frontend/tests/rectification-v9-engine-contract.test.ts;frontend/tests/rectification-probe-year-dedupe-20260906.test.ts。 - 防复发:比较请求体不得含
:varga.hash;引擎契约须覆盖真实长度的 split hash,不能只用短键。 - 相关记录:BUG-559、BUG-578、BUG-579、BUG-580
- 复发自:BUG-559
- 修复版本:
4e0db55f
BUG-578 | 候选比较失败被吞掉,过期快照也不自动重算
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
rectification-compare-candidatesreceipt、agent-run.ts、persistNextInterviewIfIdle - 用户现象:比较已经失败,助手仍写「这条记下了、很有帮助」。用户看不到失败,下一轮也不重试。
- 触发条件:比较工具抛错(含引擎 400)后 Agent 继续作答。
- 根因:失败 receipt 只有笼统
tool_failed;Agent 不追加可见说明;闲置下一问不因快照过期重算。 - 修复:失败 detail 带
safe_error_code与引擎信息前 120 字;正文追加「候选比较这次没跑成,下一句话时会自动再试。」;闲置持久化若快照过期且账本有可评分事件,同一证据指纹只重算一次。 - 验证:
frontend/tests/rectification-stale-compare-fix-20260907.test.ts。 - 防复发:比较失败必须有用户可见固定句;过期快照的闲置路径必须先重算。
- 相关记录:BUG-577
- 复发自:无
- 修复版本:
4e0db55f
BUG-579 | 用户停止被过期快照压过,点「先这样」后没有候选卡
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
decideRectification、applyRectificationChoiceSTOP、stop_and_review - 用户现象:点「先这样,先看当前范围」后出现「再问下去也分不开」一类旁白,但没有候选卡,界面写「没有拿到下一个问题」。
- 触发条件:账本已比最新比较结果多出可评分事件(快照过期)时停止。
- 根因:
snapshotCurrent === false排在userStopped之前,停止后仍判采集;停止路径也不先重算。 - 修复:有排序候选时,用户停止优先于快照过期,进入
complete_with_range且session_outcome属于可出采用卡的集合。停止路径先重算;重算失败则按上一次成功比较交付,旁白加「这是按上一次成功比较给出的范围。」 - 验证:
frontend/tests/rectification-decision-authority.test.ts;frontend/tests/rectification-stale-compare-fix-20260907.test.ts;frontend/tests/rectification-answer-choice.test.ts。 - 防复发:
snapshotCurrent=false && userStopped && ranked>0必须交付,不得回到采集。 - 相关记录:BUG-577、BUG-578
- 复发自:无
- 修复版本:
4e0db55f
BUG-580 | Holdout 题用过期盘外领域,被改写成「再说一件事」
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
holdoutFollowupFor、采集题干 - 用户现象:账本里已经有该领域带年月的事,助手仍问「也再说一件你能记得大致时间的事」。
- 触发条件:过期
oos_blind_prompts里的领域其实已在账本;焦点按盘外核对给模型改写。 - 根因:holdout 只排除拒答领域,不排除账本已有带年月事件的领域;题干不是服务端固定采集句。
- 修复:候选领域须既未拒答、账本也没有该领域带年月事件。没有可问领域则不再出 holdout 题。题干用
USER_COLLECT_QUESTION[domain],焦点为口述采集,不给选择题框。决策层不因此改成unavailable,以免走成无卡的 offer。 - 验证:
frontend/tests/rectification-collect-direction-20260904.test.ts。 - 防复发:账本已有财务等带年月事件时不得再出该领域 holdout;可问时题干必须等于对应采集固定句。
- 相关记录:BUG-577
- 复发自:无
- 修复版本:
4e0db55f
BUG-581 | 停止后重算丢掉全部答题,采用报 candidate_state_inconsistent
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
rescoreMinuteAfterWindowChange、scoreAndPersistCurrentEvidence、STOP / 闲置重算、accept_agentic_rectification_candidate_for_case_v2 - 用户现象:答完多张卡后点「先这样,先看当前范围」,范围比答题后更宽,卡片数字变成引擎裸相对支持度;点采用报「候选状态已更新,请刷新后重试」。
- 触发条件:账本已比最新比较结果多出可评分事件(快照过期)时停止;时段选定或放宽窗口后的重算也走同一条裸路径。
- 根因:BUG-579 让停止先重算,但
rescoreMinuteAfterWindowChange只跑引擎分数并persistV9Candidate,不重建inference_state、不带previous、不重放已答探针。采用 RPC 要求decision_receipt.inference_state存在且候选非空。 - 修复:把比较路径的
scoreAndPersistCurrentEvidence抽到v9/score-persist.ts。停止 / 闲置keepAnswers=true重放上一份推断;时段选定 / 放宽keepAnswers=false仍写出空答案的inference_state。采用 RPC 校验不放宽。 - 验证:
frontend/tests/rectification-stale-compare-fix-20260907.test.ts(5 条已答探针 STOP 后仍在 receipt 里,posterior 不是裸支持度;窗口变更后answered_probes为空)。任务书指定 TS 切片 1202/1202。Dockerdatabase-rectification-*.test.ts本机未跑。 - 防复发:STOP / 闲置 / 窗口变更不得再走只写引擎分数、不写
inference_state的裸 persist。 - 相关记录:BUG-577、BUG-578、BUG-579
- 复发自:BUG-579
- 修复版本:待发布(
codex/rectification-stop-rescore-fix-20260907)
BUG-582 | 无框区分题退化成开场采集句
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
persistServerOwnedFocus、spokenCollectFallbackFollowup、spokenFollowupForUser、persistPlanFocus、buildMethodFollowupPlan - 用户现象:健康拒答后助手再问开场那句「从你最容易想起来的一件事开始就好——比如哪年上的大学…」。
- 触发条件:
precision_stage=lagna_frame或观察层给出没有choice_frame的distinguish_candidates;计划仍把它当下一问。 - 根因:无框区分题被
persistSpokenChoiceFallback改写成采集,domain=other、source=method_coverage、method_id=dasha_events,spokenFollowupForUser判成开场 GENERIC。工具层?? GENERIC_COLLECT_QUESTION与persistPlanFocus在 persist 失败后再转一次口述采集是同一条路。与 BUG-558 同一「死问题」现象、不同入口。 - 修复:无框 / 文案无效的区分题一律
skipped,不得改写成采集。计划层记dropped_probes(reason=frameless_distinguish)后继续带年月采集 → 职业 → holdout。GENERIC 只留给真正的collect:other开场。holdout 排到 dated 与职业之后。 - 验证:
frontend/tests/rectification-server-focus.test.ts、frontend/tests/rectification-collect-direction-20260904.test.ts、frontend/tests/agent-voice-copy-contract.test.ts、frontend/tests/rectification-walkthrough-polish.test.ts。 - 防复发:区分题 persist 失败不得
spokenCollectFallbackFollowup;工具层不得?? GENERIC_COLLECT_QUESTION。 - 相关记录:BUG-558、BUG-580
- 复发自:BUG-558
- 修复版本:待发布(
codex/rectification-stop-rescore-fix-20260907)
BUG-583 | 区间交付卡判空顺序使 tsc 失败,staging 无法部署
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:
rectification-agentic-chat.tsx区间卡渲染、采用后首轮旁白、RectificationRangeDelivery「再补一件经历」 - 用户现象:staging 门禁与
next build因 TypeScript 失败,BUG-581~582 的修复无法部署。采用后看不到承诺过的预测窗口。点「再补一件经历」后卡片消失、输入框上方没有下一句。 - 触发条件:
ab57d03f合入后跑tsc --noEmit;或交付轮点「再补一件经历」;或采用后进入核对。 - 根因:渲染条件先读
candidateResult.resultId再写candidateResult &&,收窄发生在属性读取之后。prospectiveWindowsNarration从交付旁白撤掉后没有接到采用后首轮。onContinue只改本地 dismiss,不给采集提示。步骤条类名rectification-step-state已由 BUG-575 禁止,不能靠加回步骤条过关。 - 修复:判空提到
dismissedRangeResultId之前。persistNextInterviewIfIdle在已采用时把prospective_probes接到首轮旁白末尾;deferredCareerWindow仍留在交付旁白。收起卡片后用RangeDeliveryContinueHint显示「第 1 步·收集经历」加continueCollectFallback,不用被禁的步骤条类名。 - 验证:
frontend/tests/rectification-range-delivery-20260907.test.ts;tsc --noEmit原文见PROGRESS-rectification-range-delivery-fix-20260907.md。 - 防复发:区间卡渲染源码必须先出现
candidateResult &&再读resultId。采用后首轮 idle persist 必须调用appendPostAdoptProspectiveWindows。聊天源码仍不得出现rectification-step-state。 - 相关记录:BUG-575、BUG-581、BUG-582
- 复发自:无(区间卡
ab57d03f引入;任务书 4.2 验收未过) - 修复版本:待发布(
codex/rectification-range-delivery-fix-20260907)
BUG-584 | 同一轮采集旁白说两遍
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-08
- 影响面:
applyStepAnswerChunk、KEEP_LIVE_SPOKEN_TOOLS、publishSpokenStep、POST /api/rectification/agent - 用户现象:每一轮采集助手气泡里有两段近似旁白。第一段已经承接用户刚说的事,第二段换个说法再讲一遍。
- 触发条件:模型在
rectification-set-focus前写出正文,拿到工具结果后再写一段。 - 根因:BUG-533 让 set-focus 不撤回已直播正文。set-focus 之后下一个 step 的正文仍按
shouldPublishStepText发布;publishSpokenStep把两段无分隔拼进assistant_message。 - 修复:set-focus 结果后置
stemAttached。本轮已经发布过正文则丢掉后一段;尚未发布则仍放行开场问候。set-focus 调用时把未直播的中文半句publish出去,英文规划稿仍 discard。 - 验证:
frontend/tests/rectification-step-answer.test.ts;frontend/tests/rectification-v9-agent.test.ts文本→set-focus→文本只保留第一段。 - 防复发:set-focus 之后不得再拼接第二段正文。开场仍允许「先问候、后 set-focus」或「只在 set-focus 后说一句」。不得把这个例外扩到 record/compare/diagnostics。
- 相关记录:BUG-533
- 复发自:无(533 的放行例外把第二段也留下了)
- 修复版本:待发布(
codex/rectification-duplicate-narration-20260907)
BUG-585 | 题干在正文和问题块各出现一次
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-08
- 影响面:
stripQuestionSentences、attachQuestionsToTurns、runV9AgentTurn、RECTIFICATION_USER_COPY.collectHandoff - 用户现象:旁白末尾已经用自然语言问了下一题,问题块再显示同一句服务端题干。
- 触发条件:本轮 set-focus 成功,模型把问句写进正文;
detachCollectSpokenAssistantText只剥逐字后缀。 - 根因:不让模型提问的约束只在提示词。BUG-488 防复发写「不得同时让模型提问又拼接同一句服务端题干」,
d9404976把题干改成消息内问题块后没有代码守卫。collectSpokenEmitted是从未使用的常量。 - 修复:有本轮 focus 时,
answer.composed前按句删掉与题干相同、题干前 12 字开头、或以问号结尾的句子;剪空则用collectHandoff。变化时answer.delta replace:true。历史回看同样走stripQuestionSentences。删除collectSpokenEmitted。 - 验证:
frontend/tests/rectification-collect-prompt.test.ts;frontend/tests/rectification-v9-agent.test.ts;frontend/tests/agent-voice-copy-contract.test.ts。 - 防复发:有 focus 的已结算助手正文不得再出现以问号结尾的句子。不得靠提示词单独保证模型不问。不得恢复把题干拼进
assistant_message。 - 相关记录:BUG-488、BUG-461、BUG-471、
d9404976 - 复发自:BUG-488(防线只在提示词)
- 修复版本:待发布(
codex/rectification-duplicate-narration-20260907)
BUG-586 | 七领域问完落到补问兜底句,职业题被覆盖、区间卡出不来
- 状态:mitigated
- 首次发现:2026-09-07
- 最近更新:2026-09-08
- 影响面:
USER_COLLECT_QUESTION、spokenFollowupForUser、active-focus 承接 followup、rectification-set-focus兜底、persistNextInterviewIfIdle - 用户现象:学业、感情、事业、家人、财务、迁居、健康都问过或拒过后,助手不问「你平时主要做什么工作?」,也不出区间交付卡,只剩一句「也可以再说一件你记得大概时间的事」。
- 触发条件:七个带年月领域已覆盖或拒答,职业尚未问完或已有
collect:other:*焦点挂着;同轮可能两次 set-focus。 - 根因:三条活路已由代码确认。职业焦点落库时
persistableFocusDomain("occupation")仍写other(表约束没有 occupation);承接 followup 用focus.targetDomain重建成domain=other,稳定 id 变成collect:other:*并 supersede 职业题。set-focus 两次invalid_spoken_prompt后查USER_COLLECT_QUESTION[domain],domain=other把那句兜底写进焦点。collectQuestionDomain把空领域归为 other。挂着的collect:other:*让persistNextInterviewIfIdle见 active focus 提前返回,出卡路径跑不到。同轮第二次 set-focus 覆盖职业题的具体 receipt 链(任务书 2.2)未从事故 Case 证实,不得写成已闭环。 - 修复:删除
USER_COLLECT_QUESTION.other与 retry。非开场domain=other的口述题返回null。承接 followup 从collect:<domain>:解析领域。set-focus 兜底只允许表内真实领域,否则不落地。同轮已成功的 set-focus 第二次返回idempotent: true。账本已有带年月事件时,闲置路径把存量collect:other:*标skipped。采用跳过持久化时写terminalNote,让 agent-run 能写 exhaustion 闸门轮。 - 验证:
frontend/tests/rectification-other-collect-fallback-20260908.test.ts;frontend/tests/rectification-exhaustion-exit-20260906.test.ts覆盖事故形状出卡;frontendtsc --noEmit0;指定 TS 切片 983/0;grep -rn "也可以再说一件" src tests为空。未拿事故 Case receipt,2.2 仍 investigating。 - 防复发:源码与测试不得再出现「也可以再说一件」整句。
USER_COLLECT_QUESTION不得再有other键。职业承接 id 必须仍是collect:occupation:*。set-focus 不得在domain=other时查表落地。闲置路径必须跳过孤儿collect:other:*。 - 相关记录:BUG-558、BUG-580、BUG-582
- 复发自:BUG-558(同现象、不同来路;558 只堵了穷尽采集
otherCollectFollowup) - 修复版本:待发布(
codex/rectification-other-collect-fallback-20260908)
BUG-587 | 新证据重算后已答题无法重放,范围弹回搜索窗口
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
buildInferenceState、buildCaseInferenceState、分盘对照semantic_key、证据轮 compare 后的inference_state - 用户现象:答完多张对照卡后范围已经收到大约 8 分钟,再补一件带年月经历,交付时范围又回到大约 29 分钟,候选分数跟引擎 prior 一样。
- 触发条件:已有 5 条点选答案;新证据触发 compare,引擎 9 个分钟换掉其中 2 个;本轮
probes按 asked keys 排除已答题。 - 根因:BUG-559 让引擎和对照包不再生成已答题。第一次重算还能从
previous.probes找到定义,但新状态的probes不携带这些题。第二次重算时previous.probes也没有了,if (!probe) continue把 5 条答案变成死账本,rounds: []、posterior === prior。分盘风格题的semantic_key还把分钟列表编进去,候选集一变 key 也对不上。BUG-581 的keepAnswers修的是停止路径同一根因的另一半。 - 修复:已答题的定义随状态携带(
carried: true),重放按候选分钟,新分钟落中性。分盘semantic_key改为星座分区,例如varga.d9.天秤座|天蝎座|射手座,不再内嵌HH:MM。candidate_split_hash仍按当前候选集计算。 - 验证:
frontend/tests/rectification-probe-replay-loss-20260908.test.ts(连续两次重算仍 5 轮、未变分钟 delta 不变、新分钟 delta 0);指定 TS 切片 1000/0;tsc --noEmit0。 - 防复发:候选集变化后
answered_probes对应的题目定义必须仍在state.probes。新生成的分盘semantic_key不得匹配\d\d:\d\d。nextProbe不得把携带题再问一遍。 - 相关记录:BUG-559、BUG-581、BUG-588、BUG-589、BUG-594
- 复发自:BUG-559(去重删生成侧定义);BUG-581(停止路径已修,证据路径未修)
- 修复版本:待提交(
codex/rectification-probe-replay-loss-20260908)
BUG-588 | 证据轮旁白不报范围变化
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
runV9AgentTurn证据/比较轮结算、withRangeChangedAfterEvidence - 用户现象:范围从 8 分钟变回 29 分钟时,三轮证据旁白只说「记下了」,用户要到交付卡才看见变宽。后续复发:补经历走
rectification-record-evidence-batch内部重算,旁白仍只说「接下来我们继续」,没有范围句。 - 触发条件:本轮
credible_range变了。最初只在toolsUsed含rectification-compare-candidates时接句;batch 内 compare 不进该集合。 - 根因:点选题由服务端写「范围从 X 收到 Y」;证据轮旁白是模型写的。第一次修复按工具名触发,batch 路径没有 compare 工具名。
- 修复:结算时先按 BUG-585 剪问句,再比较本轮开始时与结算时的
credible_range。变了就把「范围从 A–B 变为 C–D。」接到正文末尾;没变不写。不再看toolsUsed。 - 验证:
frontend/tests/rectification-probe-replay-loss-20260908.test.ts锁结算段不按 compare 工具名触发,batch 形状 04:47–04:59 → 04:47–05:14 仍接范围句;frontend/tests/agent-voice-copy-contract.test.ts收录新句并锁先剪后接。 - 防复发:范围句看
credible_range事实,不看本轮点了哪个工具。变了必须写进落库assistant_message。不得靠提示词让模型自己报。 - 相关记录:BUG-585、BUG-587、BUG-569、BUG-594
- 复发自:无;后续复发见本条 batch 路径(随 BUG-594 同修)
- 修复版本:
df182c16;batch 路径随 BUG-594 同修
BUG-589 | 交付/选择卡在最后一题出现前可能闪现
- 状态:investigating
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
showSelectionCards、loadCaseSnapshot - 用户现象:最后一题的选择卡出现前一瞬,时间选择卡片弹出又消失。
- 触发条件:用户报告发生在证据轮之后、下一张对照卡出现之前。按导出最终状态
can_adopt=false,正常结算后不应出卡。 - 根因:未证实。可能是
loadCaseSnapshot先写入candidateResult,问题合并进消息是另一次渲染,中间interviewQuestionBlocksAdoptOffer看不到新问题。 - 修复:源码加守卫:
showSelectionCards要求!busy && caseSnapshotLoaded;快照与问题合并放进同一次startTransition。不得声称已复现闪现。 - 验证:
frontend/tests/rectification-probe-replay-loss-20260908.test.ts、frontend/tests/rectification-agentic-entry.test.ts源码锁。无复现实验。 - 防复发:选择卡不得在
busy或快照未加载时出现。 - 相关记录:BUG-587
- 复发自:无
- 修复版本:待提交(守卫已加,现象仍 investigating)
BUG-590 | 范围已收到 2 分钟、决策已是采用,却再问答过的财务采集题
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
method-followup.ts职业覆盖后的 yearless→采集分支、shouldSkipFollowupPersist、holdoutFollowupFor - 用户现象:范围已经收到大约 2 分钟,决策已是采用,输入框上方却再问一遍已经答过的财务采集题,区间交付卡出不来。
- 触发条件:七个带年月领域和职业都覆盖或拒答;yearless 目录里还有无年份分盘对照(本例 D11 财务);闲置路径带着
contrastPacket持久化下一问;财务账本已有带年月确认事件且该领域采集焦点已resolved。 - 根因:职业覆盖后的 yearless→采集分支只取
yearlessDiscriminators[0],只跳过拒答,不看该领域是否已有带年月事件、是否已经问过collect:<domain>:*。财务已覆盖仍被改写成 dated collect。shouldSkipFollowupPersist只要isRemainingEvidenceCollect为真就不跳过,采用被这道题挡住。holdout 的occupied不认health/health_pressure同义,答过健康后仍可能再问健康线。 - 修复:遍历全部 yearless 对照,跳过拒答、已确认(含健康同义)和已问过采集的领域,全部跳过则不再派采集。采用且
probe_pool_exhausted时,剩余采集只计「该领域没有确认事件且未拒答」的题,否则跳过持久化并写terminalNote。holdoutoccupied把health与health_pressure归并。不动采用门、确认门、Skill 版本(仍 10.0.15)。 - 验证:
frontend/tests/rectification-occupation-coverage-exit.test.ts事故形状:yearless→采集next_followup === null;迁居未覆盖仍问迁居;idle persistterminalNote: true且 0 次 set-focus;agent-run 闸门轮与canShowRectificationSelectionCards;holdout 健康同义。frontend/tests/rectification-server-focus.test.ts见 BUG-591。frontendtsc --noEmit0;指定 TS 切片 1006/0。 - 防复发:yearless→采集不得只取第一道分盘题。已覆盖或已问过的领域不得再派 dated collect。采用穷尽时不得把已覆盖领域的采集当剩余采集挡住出卡。holdout occupied 必须与
hasConfirmedHealth同口径。USER_COLLECT_QUESTION.other不得回潮。 - 相关记录:BUG-586、BUG-587、BUG-591
- 复发自:BUG-586(同一「职业覆盖后多问一题」现象,不同分支);BUG-587(收敛成功后才暴露)
- 修复版本:待提交(
codex/rectification-covered-domain-recollect-20260908)
BUG-591 | 非 retry 采集焦点撞 id 时无条件加 :next 再插一次
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
server-focus.ts::persistCollectFocus、persistFocusAfterChoice - 用户现象:财务采集题已经问过并关闭,下一轮仍以
collect:finance:collect_method_evidence:next落库,题干与已答财务题相同。 - 触发条件:计划层再次派出同一领域的非
collect_retry采集题;set_agentic_rectification_conversation_focus报focus_idempotency_conflict。 - 根因:
75fc456d(BUG-462 附带)在撞 id 时一律改${questionId}:next再插,原本给穷尽采集撞开场题号用。BUG-586 删掉 other 穷尽采集后,这条路径只剩副作用:已覆盖领域只要被计划层重复派出,就能以:next落地。 - 修复:撞 id 时仅
followup.collect_retry === true才用:next;否则返回duplicate_focus、不插入。persistFocusAfterChoice把duplicate_focus当已处理、不再读 dossier 重试。 - 验证:
frontend/tests/rectification-server-focus.test.ts:非 retry 冲突status: "duplicate_focus"且 RPC 一次;collect_retry: true仍以:next落地。 - 防复发:非 retry 采集撞 id 不得再加
:next。retry 采集仍须能以:next落地。 - 相关记录:BUG-462、BUG-586、BUG-590
- 复发自:BUG-462(
:next重试本为穷尽采集撞开场题号) - 修复版本:待提交(
codex/rectification-covered-domain-recollect-20260908)
BUG-592 | 答完「2023 年 5 月前后」又问「2023 年前后」(同域同年点选题)
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
inspectDiscriminatorProbes、rankRenderableDiscriminators、existenceProbeAsked/sameYearProbeAsked、点选后decideAfterInferenceChange - 用户现象:已经答过某年某月「有没有入职/换工作」,下一张点选卡又问同一领域同一年的整年题,题干几乎同一句。
- 触发条件:同一次候选比较生成的
inference_state.probes里同时有domain.YYYY.MM.*和domain.YYYY.*;点选不重跑引擎,按信息量从高到低问。 - 根因:BUG-559 引擎侧只在重新生成时按
asked_probe_keys屏蔽同年与相邻年;点选下一题走 TS 对照包。inspectDiscriminatorProbes只认精确semantic_key/ hash /probe_id,没有领域+年份规则。rankRenderableDiscriminators虽调existenceProbeAsked,结果只是把asked=true交给rankDiscriminatorScore降权,不排除。旧验证只锁了引擎重生成和existenceProbeAsked返回值。 - 修复:同领域同年份硬排除(
dropped_probes(reason="same_year_asked")),year=0的分盘风格题不动。相邻年(±1)继续降权、不硬排除。existenceProbeAsked拆出sameYearProbeAsked。引擎asked_probe_keys、SCORE_DELTA、四选项合同、Skill 版本(仍 10.0.15)不动。 - 验证:
frontend/tests/rectification-probe-year-dedupe-20260906.test.ts:答完 5 月后不得再问同年 activation、相邻年 2024 仍可问、buildMethodFollowupPlan同口径、事故五题回放第五题不再是同年整年题。frontend/tests/rectification-inference-machine.test.ts锁核心 identity 不去重、inspect 硬排除。tests/test_rectification_event_probes.py不改、须仍绿。 - 防复发:同一份 probes 列表里,同领域同年份答过一道就不得再问第二道。不得只靠降权。相邻年不得被硬排除。点选路径必须走
inspectDiscriminatorProbes/rankRenderableDiscriminators的同年硬排除,不能只依赖引擎重生成。 - 相关记录:BUG-559
- 复发自:BUG-559(引擎侧只管重生成;TS 侧只降权)
- 修复版本:
8ae17a26
BUG-593 | 交付轮验证报告引用推断前的引擎事实
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-10
- 影响面:
latestResultToolProjection、buildSkillVerificationPacket、jyotish-birth-time-rectification@10.0.16 - 用户现象:区间卡已是约 3 分钟可信窗,交付轮正文却写宽度约半小时、双轨偏向已淘汰分钟、某候选 D10 升成下一座。
- 触发条件:推断层已把候选收到短区间并出交付卡;引擎
tied_minute_count/dasha_agreement仍按全部候选计算;window_scan换升在代表分钟之后。 - 根因:结果投影把
candidates换成推断层,但报告仍喂引擎indistinguishable_width_minutes和dasha_agreement。分盘上升没有按候选分钟给出星座,模型按换升时刻自己算。 - 修复:有推断层时报告宽度用
credible_range含两端分钟数;双轨在 active 候选内重算,引擎原值只留dasha_agreement_pre_inference供审计。顶层宽度改名engine_indistinguishable_width_minutes。报告给出sign_by_candidate,与分歧面板共用换升函数。确认门不改。Skill 10.0.16 要求只抄该表。 补正(2026-09-10):代码里已有dasha_agreement_pre_inference。BUG-638 修的是引擎原值仍按网格分钟判冲突、与报告口径不一致,不是这个字段缺失。 补充(2026-09-27,TASK-rectification-grounding-20260927 R1):旁路。rectification-offer-candidates一直把完整投影交给模型(公开 AA 盘约 100KB:*_pre_inference、31KB 对照包、探针),compare 返回里engine_indistinguishable_width_minutes可被引用、验证报告 Markdown 出现两次。现在 offer 与 compare 走同一套给模型的剥离(offerModelProjection/agentVisibleLatestProjection):不给*_pre_inference、对照包、探针、引擎宽度,报告 Markdown 只留skill_verification_report一份;offer 100,012 → 28,799 字节,compare 38,026 → 29,698 字节。病例 API 投影与回执指纹不变。测试frontend/tests/rectification-grounding-projection-20260927.test.ts;本条原测试未改。 - 验证:
frontend/tests/rectification-delivery-report-facts.test.ts:推断窗 04:51–04:53、引擎宽 29、双轨 top 为已淘汰分钟时,报告width_minutes === 3且正文不含 29 / 已淘汰分钟;D10 05:00 换升时sign_by_candidate["04:53"].d10 === "巨蟹座"。agent-voice-copy-contract/skill-registry锁 10.0.16 新句。 - 防复发:交付轮宽度、双轨、分盘星座必须来自推断层报告字段,不得再把引擎原跨度或换升时刻交给模型自算。
- 相关记录:BUG-545、BUG-568、BUG-290、BUG-638
- 复发自:BUG-290(宽度字段进投影后未随推断层更新);BUG-568(区间读盘,交付正文仍用开工跨度)
- 修复版本:
06e44104
BUG-594 | 重算后新分钟不继承已答题结论,范围从 13 分钟回弹到 28 分钟
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
apply-probe-outcome.ts::directionFor、buildInferenceState携带题重放、window_scan.transitions、证据轮范围句 - 用户现象:5 道点选题把范围收到 04:47–04:59 后,再补迁居经历,交付卡变成 04:47–05:14。新进候选的 05:14 在已答题上全是中性、零冲突,把右端拉宽。
- 触发条件:引擎重算换入不在旧
supports/conflicts里的分钟;已答题按 BUG-587 携带重放。 - 根因:BUG-587 任务书决策 2 规定新分钟一律
neutral、不做邻近插值。directionFor按候选 id 查成员,查不到就记 0。分盘题本可按换升星座判定,大运/激活题的 outcome 本是连续分钟段,结论可以确定推出。 - 修复:推翻该中性决策。分盘风格题用
signFromTransitions按已答星座判 support/conflict,缺 transitions 则仍中性。其余题:新分钟落在某 outcome 集合最小与最大分钟之间、且两侧最近旧分钟同属该集合时继承该集合,否则中性。携带题写入outcome_by_minute。buildCaseInferenceState把window_scan.transitions传进核心层。范围句改看credible_range事实(见 BUG-588 batch 路径)。淘汰阈值、SCORE_DELTA、四选项合同、Skill 版本不动。 - 验证:
frontend/tests/rectification-probe-replay-loss-20260908.test.tsBUG-594 (a)–(d):05:14 重放后strong_conflict_count ≥ 3且eliminated,范围仍 04:47–04:59;两集合之间的分钟保持中性;无 transitions 时分盘题保持中性;连续换入被覆盖的新分钟不把范围拉宽。 - 防复发:新分钟不得只因不在旧名单里就零冲突存活。分盘题必须能用换升星座继承;大运/激活题必须能用连续分钟段继承。不得再用「不做插值」挡住可确定推出的结论。
- 相关记录:BUG-587、BUG-588
- 复发自:BUG-587(任务书决策 2 定错;重放定义已在,结论层把新分钟放生)
- 修复版本:待发布(
codex/rectification-new-minute-inherit-20260908)
BUG-595 | 交付面四个按钮、输入框「先这样」和采用状态条把界面撑乱
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
rectification-range-delivery.tsx、rectification-agentic-chat.tsx、user-copy.ts、divergence-panel.ts、Skill 10.0.17 - 用户现象:区间卡下面四个动作(分歧两列、「都不像」「说不好」、「按这个范围用」「再补一件经历」);输入框上方一直挂「先这样,先看当前范围」;采用后另有一条状态条。交付正文是长篇八法 markdown,表格在气泡里摊成纯文本。
- 触发条件:访谈收口出区间交付卡;采用代表性分钟。
- 根因:
TASK-rectification-range-delivery-card-20260907.md把交付对象做成「两个动作 + 分歧面板」,DESIGN 又在口述采集上方放先这样。产品决定推翻该设计。 - 修复:卡片改为按候选分钟逐行点选采用。删分歧列、四个按钮、composer「先这样」、composer-meta、采用状态条。交付正文三句;八法报告进默认收起的「查看验证报告」。没有核对题时
postAdoptVerifyDone改成「已采用 HH:MM,新建对话即按这个时间排盘。」Skill 10.0.17。 - 验证:
frontend/tests/rectification-range-delivery-20260907.test.ts行选 DOM;frontend/tests/rectification-delivery-ui-simplify-20260908.test.ts三句旁白、折叠块、composer-wrap 无先这样/状态条。任务书 TS 切片 1039/1039。 - 防复发:composer-wrap 不得再出现
CHOICE_STOP_LABEL或rectification-adopt-status。逐行点选卡已被 BUG-597 的三列对照取代,不得把「一行一个分钟」或「排盘用」标签加回去。 - 相关记录:BUG-544、BUG-545、BUG-558、BUG-583、BUG-597
- 复发自:无
- 修复版本:
7e3b6cdc
BUG-596 | 交付轮连续写出两条助手消息
- 状态:mitigated(触发链仍
investigating) - 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
persistNextInterviewIfIdle、persistExhaustionGateTurn、runV9AgentTurn、finalizeSuccessfulTurnExit、客户端send("opening")、regenerate - 用户现象:职业答完后出现两条助手消息。第一条已写摘要;第二条又把完整八法报告写一遍。中间没有用户输入。
- 触发条件:出牌/穷尽路径
persistNextInterviewIfIdle返回terminalNote后,同一 Case 在无用户消息时再跑一轮 agent。 - 根因:未知。已在四条路上打
rectification_delivery_turn日志。runV9AgentTurn在latestResult.resultId未变的 60 秒窗口内,对无用户消息的第二次运行返回already_delivered,不计费、不写 turn。agent-run 与 turn-exit 对齐:已有askedTurnId时不再persistExhaustionGateTurn。 - 修复:日志 +
already_delivered守卫。 - 验证:
frontend/tests/rectification-delivery-ui-simplify-20260908.test.tsmock 两次连续无消息运行,第二次already_delivered。任务书 TS 切片 1039/1039。 - 防复发:交付后无用户消息不得再开一轮计费 turn。
- 相关记录:BUG-462、BUG-565
- 复发自:无
- 修复版本:
7e3b6cdc
BUG-597 | 交付卡逐行点选看不出候选差别,又像直接给了一个时间
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
rectification-range-delivery.tsx、divergence-panel.ts、refinement_packet.py、event_probes.py、user-copy.ts、Skill 10.0.18 - 用户现象:区间卡每个候选分钟一行。与代表分钟 D9/D10 相同的行右侧是空的;代表分钟预先标成「排盘用」,用户觉得是直接出一个时间,而不是在几个分钟里对照后选择。
- 触发条件:访谈收口出区间交付卡;范围内有两个以上候选分钟。
- 根因:BUG-595 把交付做成「点一行即采用」,右侧只写与代表分钟的分盘差异。产品决定改成并排对照:每列写该分钟自己的相对可能性、性格处事、经历吻合和未来一年窗口。
- 修复:卡片改成一行至多三列,按后验概率排序、按精确 HH:MM 去重。每列由服务端写相对可能性、D9/D10/月宿性格处事、经历强/中/弱计数与最不吻合一件(年-月+领域)、往后 12 个月 1–2 个窗或固定句「未来一年没有明显的窗口」。按钮「更像这个」走现有 accept RPC,选择不计分。同一次 compare 增加
event_dasha_ledger_by_time与prospective_windows_by_time。不预标「排盘用」。Skill 只改references/candidate-comparison.md,bump 10.0.18。 - 验证:
frontend/tests/rectification-range-delivery-20260907.test.ts三列 DOM、「更像这个」、无「排盘用」;frontend/tests/rectification-delivery-ui-simplify-20260908.test.ts三句旁白仍不含「排盘用」;frontend/tests/agent-voice-copy-contract.test.ts锁新文案;tests/test_rectification_v5_services.py::test_compare_returns_ledger_and_windows_by_candidate_time三分钟各一份 ledger/窗口。 - 防复发:交付卡必须是列对照而不是逐行空差异;列文案必须来自服务端类型表与 ledger/窗口映射,不得让模型写。超过三列时必须出现「还有 N 个分钟可能性更低,已并入范围」。没有窗口时必须写固定句,不得空白。
- 相关记录:BUG-595、BUG-583、BUG-568
- 复发自:BUG-595(逐行卡上线后用户看不出为何要选另一分钟)
- 修复版本:待发布
BUG-598 | 点选题成年下限只在 followup 生效,inspect 回退会漏掉
- 状态:resolved
- 首次发现:2026-09-08
- 最近更新:2026-09-08
- 影响面:
inspectDiscriminatorProbes、adult-floor.ts、decideAfterInferenceChange、answer-choice.ts - 用户现象:已经按年龄下限滤掉的迁居等点选题,在答完上一题后的 inspect 回退路径里又出现。
- 触发条件:
decideAfterInferenceChange走matched ?? inspected.selected;method-followup已按DOMAIN_AGE_LO过滤,但 inspect 没有出生日期。 - 根因:
probeBelowAdultFloor只在method-followup.ts三处生效。inspectDiscriminatorProbes不接收birthDate,回退选中的探针可以低于成年下限。 - 修复:抽出共享
adult-floor.ts。inspectDiscriminatorProbes接收birthDate,低于下限写入dropped_probes(reason="below_adult_floor")。decideAfterInferenceChange与答题持久化从出生快照传入该日期。采集题「没有」按钮本单不做,等产品决定。 - 验证:
frontend/tests/rectification-candidate-contrast-packet.test.ts:出生日期 2000-01-01 时 2015 迁居题被丢掉;1996-01-01 时保留。按年份计算:2015 低于下限当且仅当出生年 > 1997;1997 年生在 2015 年满 18 岁,不越线。 - 防复发:inspect 与 followup 必须共用同一套成年下限。点选回退不得在没有
birthDate时把已过滤的领域年再选出来。 - 相关记录:BUG-597
- 复发自:无
- 修复版本:待发布
BUG-599 | 回访登录自动打开生时校正并挤歪首页
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:登录后首页、
resolveBootstrapSessionSelection、生时校正 Case open、.welcome布局 - 用户现象:以前登录过的账户一进首页,主栏内容挤到左边;用户未点任何入口就出现「校正服务暂时不可用」。
- 触发条件:登录落到
/(没有?c=)。会话列表第一项是历史生时校正;该会话列表不带消息,校正会话又跳过消息水合。 - 根因:bootstrap 把
sessions[0]当成当前会话。校正会话会在 prepare 阶段自动POST /api/rectification/cases/open。打开失败时 catch-all 文案是「校正服务暂时不可用」,并画在首页卡片下方。同时.conversation.is-empty要求messagesHydrated,校正会话保持 false,首页 1040px 容器没有水平居中。 - 修复:仅当地址栏
?c=指向该校正会话时才自动打开。默认/落地改到空咨询会话(没有则新建)。.welcome/.starter-list增加margin-inline: auto;空校正会话在未打开 surface 时也使用is-empty。 - 验证:
frontend/tests/home-bootstrap-reveal.test.ts、frontend/tests/rectification-surface-contract.test.ts、frontend/tests/session-conversation-layout.test.ts。 - 防复发:裸
/不得因最近一条历史是生时校正就调用 Case open;首页居中不得依赖校正会话的messagesHydrated。 - 相关记录:BUG-027、BUG-034
- 复发自:无
- 修复版本:待发布
BUG-600 | 新用户保存账户资料返回 PATCH /api/account 500
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
PATCH /api/account、staging 自托管 PostgreSQL、首次称呼/出生资料保存 - 用户现象:新账号登录后保存称呼或出生资料,接口返回
500 {"error":"暂时无法保存账户资料"}。 - 触发条件:self-hosted 新用户已有 trigger 创建的
profiles行,前端persistProfile提交默认ayanamsa。 - 根因:
20260903010000_profile_ayanamsa.sql只把ayanamsa的UPDATE授给authenticated。账户 PATCH 走service_role;PostgreSQL 对未授权列返回42501 permission denied for table profiles。该错误不含column,现有缺列回退不会去掉ayanamsa,因此首次保存直接 500。无档案 INSERT 不受影响,所以已有 trigger 建档的新用户会踩中。 - 修复:新增迁移为
service_role补齐ayanamsa的列级SELECT / INSERT / UPDATE。 - 验证:本地 PostgreSQL 在补授权前,含
ayanamsa的service_roleUPDATE 返回42501,去掉该列后 UPDATE 成功;补授权后含ayanamsa的 UPDATE 返回一行。frontend/tests/profile-persistence.test.ts锁定新旧迁移的授权差。 - 防复发:账户 PATCH 新增列必须同时授予
authenticated自助保存和service_role并发读写;缺列回退不得把 table 级42501当成可忽略的缺列。 - 相关记录:BUG-039、BUG-005
- 复发自:BUG-039(全球地点列漏授 service_role 的同类权限缺口)
- 修复版本:迁移
20260909010000,待发布
BUG-601 | 报告生成进度后端已产出,前端解析后一字未渲染,8 分钟只见转圈
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
/reports/[reportId]生成等待屏、personal-report-page.tsx、personal-report-route-core.ts - 用户现象:生成个人报告后停在等待页,屏幕上只有一个
InlineSpinner、一句固定文案「正在生成报告,大约 10–30 秒」和「已等待 X 分 Y 秒」。实际耗时以分钟计,轮询预算 8 分钟。用户无从判断是在推进还是已经卡死,普遍在三分钟左右刷新或离开。 - 触发条件:任何一次个人报告生成,必现。
- 根因:两段断链。其一,
personal-report-page.tsx的classifyReportEnvelope已经把progressPercent/progressPhase解析进generating状态对象(原第 97–102 行),但该分支的渲染(原第 231–246 行)完全没有引用这两个字段,等待屏因此与后端进度无关。其二,resolveReportRead只在row.status === "failed"时读取分章行,reportView也不含分章字段,所以生成中根本没有任何章节状态离开服务端——即便前端想渲染也无数据可用。worker 侧的阶梯(loading_context10 →generating_report30 →section:<id>30–85 →persisting_report90)一直在正常写库,只是没有读者。 - 修复:分章进度成为等待屏的主体。服务端在
generating与failed两种状态下读取分章行(ready路径不变,不增加查询);新增personal-report-progress.ts把行状态映射为只读的四态done / failed / writing / waiting并随reportView下发,只带id与state。等待屏按 phase 分三段:准备阶段保留 spinner,写作阶段换成"已完成 N / M 章"加按章分格的进度条与章节清单,收尾阶段回到 spinner。停滞满 90 秒(REPORT_PROGRESS_STALL_MS)时当前章文案改为「用时较长,仍在写」,该计时复用既有的一秒 tick,未新增定时器或 effect。 - 验证:
frontend/tests/personal-report-progress.test.ts16 项全绿,覆盖:认领态判定为「正在写」而非按完成数顺推(字典序返回时顺推会指向timing而正确答案是marriage)、phase 命名的章已完成、blocked 计入完成数且hasFailure为真、全 ready 与部分 blocked 两种收尾、90 秒停滞文案切换且不出现「第 N 次尝试」、准备/写作/收尾三分支、分章行缺失时回落准备态而非渲染「已完成 0 / 0 章」、面板无 percent/定时器/预计剩余、attemptCount等后台字段不出服务端。全量npm test2929→2945(pass 2887→2903),失败 28 条与基线逐条一致(全部为无 Docker 的数据库/部署套件)。tsc --noEmit0 错,npm run lint0 error。 - 防复发:三条写进
frontend/DESIGN.md§9「报告生成等待态」并由上述测试锁死。其一,进度条按章分格,不得改画百分比条——job 的 percent 在 0→30 与 90→100 是瞬间跳变,线性条会演出后端没做的动作。其二,"正在写哪一章"只能由行状态(pending且已认领)判定,不得由section:<id>的 phase 名或"已完成数 + 1"推导:前者命名的是刚写完的那一章,后者在字典序列表上会指错。其三,界面上不得出现按定时器推进的插值动画或预计剩余时间。 - 相关记录:BUG-043(禁止把后台评分状态渲染成用户需要管理的面板。本处边界不同且不冲突:等待屏是只读的,展示的是用户交付物自身的章节结构,
attemptCount、原始错误码、lease、job id、payload 一律不出服务端)、BUG-576、BUG-574 - 复发自:无
- 修复版本:待发布
BUG-602 | 时间轴宽度按差值算,卡片与报告按含两端算
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
rectification-timeline-scale.ts区间读数widthLabel、frontend/DESIGN.md§10 - 用户现象:同一屏上时间轴写的宽度比三列卡和验证报告少 1 分钟。例如区间 04:51–04:59 条上写「8 分钟」、卡上写「9 分钟」;05:07–05:09 条上写「2 分钟」;整天窗口写「23 小时 59 分」。
- 触发条件:分钟阶段已有
credible_range,或时段阶段轴铺满整天窗口。必现。 - 根因:
widthLabel用timelineDurationLabel(bandEnd - bandStart),少算两端都计入的那一分钟。BUG-593 已把交付报告与三列卡定为含两端分钟数;任务书示例「05:07–05:09 · 2 分钟」是差值口径,首版时间轴照抄了。 - 修复:改为
timelineDurationLabel(bandEnd - bandStart + 1)。整天 00:00–23:59 显示「24 小时」,单分钟显示「1 分钟」。DESIGN.md §10 示例改为「05:07–05:09 · 3 分钟」。 - 验证:
frontend/tests/rectification-timeline-20260909.test.ts(含两端宽度表:04:51–04:59 → 9 分钟、05:07–05:09 → 3 分钟、04:51–04:53 → 3 分钟、单分钟 → 1 分钟、整天 → 24 小时);与frontend/tests/rectification-delivery-report-facts.test.ts的width_minutes === 3同一形状。 - 防复发:时间轴读数必须与报告/卡片同一套含两端分钟数;禁止再把
bandEnd - bandStart当宽度。 - 相关记录:BUG-593
- 复发自:无
- 修复版本:
92e3d5e7
BUG-603 | 被排除的分钟到不了客户端,时间轴空心点画不出来
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
rectification-candidate-result.ts、rectification-timeline-scale.ts、rectification-agentic-chat.tsx的timelineView候选来源 - 用户现象:DESIGN.md §10 写「被排除的点留在原地变空心、不消失」;真实会话里答完一题后那些分钟从条上消失,剩下的全是实心。看不到刚才那一答排掉了哪几分钟。
- 触发条件:分钟阶段已有推断层、至少淘汰过一个候选。必现。
- 根因:
inference-adapter.ts投影只保留status !== "eliminated"的候选;时间轴把candidateResult.candidates.map(time)当点,再按是否落在区间带内判 in/out。到达客户端的点都在带内,全是实心;被淘汰的分钟根本不在列表里。测试里的["out","in","in","in","out"]是合成输入,不是这条数据路径。 - 修复:
RectificationCandidateResult增加只读inferenceMarks: [{time, eliminated}],从decisionReceipt.inference_state.candidates的time/status解析(组件不读原始 receipt)。state = status === "eliminated" ? "out" : "in",不再按点是否落在带内判断。没有inference_state时退回引擎候选全部实心。 - 验证:
frontend/tests/rectification-timeline-20260909.test.ts:推断层 9 个候选、7 个 eliminated、可信区间 04:51–04:53 → 9 个点、7 空心 2 实心;再淘汰 1 个 → 8 空心 1 实心,key 不变。frontend/tests/rectification-candidate-result.test.ts:有inference_state时解析出全集,无则[]。聊天源码合同:timelineView读inferenceMarks,不读decisionReceipt。 - 防复发:时间轴标记必须来自推断层候选全集;禁止再用引擎 active 投影或带内位置当 in/out。空心/实心只看
status === "eliminated"。 - 相关记录:BUG-560、DESIGN.md §10
- 复发自:无
- 修复版本:
92e3d5e7
BUG-604 | 开场不讲做法、一次只收一件,同一批经历被拆成多轮计费
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
buildOpeningBrief、GENERIC_COLLECT_QUESTION、jyotish-birth-time-rectification@10.0.19OpeningPolicy、口述开场正文 - 用户现象:开场不说当前窗口和最后会给什么,也不点出可以讲哪些方面;题干只请人说一件,一条消息里的多件经历被拆成多轮,每轮都要重算。
- 触发条件:新建生时校正、进入开场采集。
- 根因:opening brief 和 Skill OpeningPolicy 禁止领域清单、禁止一次说完;题干带年份例子。产品决定推翻该口径:列领域不列年份,一条消息可以报多件。
- 修复:brief 写入当前搜索窗口与 intake 来源、做法三句要点、六类领域。开场正文不合格时服务端换成模板。首题仍是
collect:other:*,题干改为「先说你最容易想起的一两件,年月大概就行」。批量写入路径仍是rectification-record-evidence-batch。Skill 10.0.19。只报一件走既有逐领域采集,不插入「还有吗」追问轮。 - 验证:
frontend/tests/rectification-v9-agent.test.ts(opening brief、落库正文替换)、frontend/tests/agent-voice-copy-contract.test.ts(开场模板与六类)、frontend/tests/rectification-spoken-collect.test.ts(三件已确认领域不再被逐个追问;单事件开场下一问为逐领域采集且正文不含「还有吗 / 别的吗」)、frontend/tests/rectification-server-focus.test.ts(GENERIC 题干)。 - 防复发:开场落库正文必须 ≤4 句、至少五类领域、不含四位年份。不得要求先准备材料。不得把具体年份写进开场。不得因只报一件就插入追问轮或重复开场邀请。
- 相关记录:BUG-488、BUG-558
- 复发自:无
- 修复版本:
aa7ccb30、1453fb16
BUG-605 | 口述采集只能打字「没有」,没有「记不清」,也不知道可以跳过
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
rectification-agentic-chat.tsxcomposer-wrap、applyCollectFocusDenial、isCollectSkipUtterance、采集回执文案 - 用户现象:采集题只有输入框。想说这方面没有或这题记不清时,不知道可以跳过;打字「没有」才能走拒答。
- 触发条件:
collect_spoken焦点激活且未 busy。 - 根因:BUG-595 删掉输入框上方「先这样」后没有替代采集快捷回答。产品不要示例骨架条,只要「没有 / 记不清」。
- 修复:composer-wrap 内两个 44px 次要按钮,类名
rectification-collect-reply,不复用rectification-step-state。「没有」与打字「没有」同一 POST,走 declined,回执「记下了,这方面先跳过。」「记不清」走 skipped,回执「记下了,这题先放着。」精确这两句不调模型。skipped 领域本会话不再采集;采用后核对仍可碰到 skipped,不碰 declined。 - 验证:
frontend/tests/rectification-spoken-collect.test.ts(按钮只在 collect_spoken、精确「没有 / 记不清」短路分类器、skipped 不再采集、reverse-verify 仍可碰)。 - 防复发:采集快捷回答不得画成「先这样」,不得用
rectification-step-state。不得在回答条预填年份。后续产品否决该芯片,见 BUG-628。 - 相关记录:BUG-546、BUG-618、BUG-628
- 复发自:无
- 修复版本:
aa7ccb30
BUG-606 | 每轮气泡太长:方法句、领先落后、价值评价叠在正文里
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
chat-message-content.tsx、consultation-run-timeline.tsx、composeChoiceNarration、trimEvidenceTurnBody、agent-run开场/证据守卫 - 用户现象:助手气泡里除了记下经历,还写「本轮对照了…」、点选后「这段领先那段落后」,以及「对校正特别有用」之类评价。
- 触发条件:证据轮或点选题答完后看助手正文。
- 根因:方法句渲在气泡;旁白拼接
explainScoreMovement;证据轮只靠提示词,模型仍写三句加评价。 - 修复:气泡不再渲染
vargaSentence,展开的活动时间线保留。点选旁白只有「已记录,范围没变。」或「已记录,范围收到 / 变为 A–B。」证据轮超过两句只留首句,并去掉「很有帮助 / 很有价值 / 很有分量 / 特别有用」。 - 验证:
frontend/tests/rectification-varga-style-copy.test.ts、frontend/tests/rectification-answer-choice.test.ts、frontend/tests/rectification-v9-agent.test.ts、frontend/tests/rectification-collect-prompt.test.ts、frontend/tests/agent-voice-copy-contract.test.ts(机器词表含「领先」「落后」)。 - 防复发:用户可见旁白不得出现「领先」「落后」。证据轮落库不得含价值评价四词。方法句不得回到气泡正文。
- 相关记录:BUG-545、BUG-585
- 复发自:无
- 修复版本:
aa7ccb30
BUG-607 | 长报告页分盘标题下没有图
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
/reports/<id>Markdown 阅读页、personal-report-markdown-view.tsx、scripts/report_chart_block.py、vedic-chart-svg.tsx - 用户现象:长报告里
#### D1 — Rashi Chart(本命盘)、#### D9 — Navamsa(婚盘)、#### Moon Chart(月亮参考盘)以及 Vargas I 下 19 个分盘标题后面全是空白,只有标题没有星盘图。 - 触发条件:打开任意一份 2026-09-06 之后生成的个人长报告详情页。
- 根因:引擎在 Markdown 里内联了南印度 SVG HTML。09-06 把阅读页改成
react-markdown+skipHtml后,内联 HTML 被静默丢弃。skipHtml本身是红线(出生地等用户字符串会进正文),不能靠放开rehype-raw修。 - 修复:引擎在每张 SVG 旁追加
jyotish-chart围栏 JSON;阅读页只认围栏,zod 校验后用北印式菱形组件自绘。导出.md前剥掉围栏,保留 SVG 给外部阅读器。 - 验证:
tests/test_report_chart_block.py7 项(22 块围栏 == 22 个<svg>,度数/星座/逆行与 core_chart 一致,且该文件在CORE_PYTEST_TARGETS);frontend/tests/report-chart-block.test.ts、frontend/tests/personal-report-markdown-view.test.tsx(组件 SVG 有role="img",不含引擎viewBox="0 0 420 480",坏块显示「图盘数据无效」且无<script)。全量npm test2975 tests / 2971 pass / 4 fail,失败 4 条均为 Docker Postgres fixture,与图盘无关。tsc --noEmit0 错;next build --webpack后/仍为 Static。 - 防复发:Markdown 阅读页继续
skipHtml/ 禁止rehype-raw;图盘文本只来自白名单映射。上述测试锁住围栏块数与渲染通路。 - 相关记录:
TASK-report-md-page-20260906.md、TASK-report-chart-render-20260909.md、BUG-616、BUG-617 - 复发自:无(09-06 直渲回归,不是旧 BUG 编号复发)
- 修复版本:待发布
BUG-608 | Venus / Jupiter / Moon 大运被压成一句婚恋机会
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
/api/relationship、/api/consultation_workflow婚恋主题证据、scripts/relationship_analysis.py、mcp_server.py婚恋裁决 - 用户现象:问「我什么时候会结婚」时,只要当前是金星、木星或月亮大运,回答就容易写成笼统的婚恋机会,把心动、成对、领证混成一件事。
- 触发条件:婚恋主题且请求带了大运信息;本仓旧路径原先没把大运传进感情分析,所以上游 09-04 审计的那句假阳性在本仓既不会出现、也不会被拆开。
- 根因:感情分析把 5 宫主 / 7 宫月亮 / 金星大运收成同一条机会话。法律婚、成对、心动没有分层;更细一层的命中还会被升格。
- 修复:大运信息传入感情分析后输出三层
event_class_split。心动层只作观察;法律婚只认大运/小运命中。Punarphoo 只进观察上下文,不改写legal_marriage标签。证据项由新模块组装,不改咨询响应已有键。 - 验证:
tests/test_punarphoo_observation.py、tests/test_relationship_event_class_evidence.py、tests/test_mcp_strict_workflow_relationship.py新增「Punarphoo 不改法律婚标签」;grep 婚姻机会在感情分析与增强大运文件为 0。 - 防复发:咨询契约只增不改。Punarphoo / 心动不得写成会结婚或领证窗口。条件大运不进多系统交叉投票、不进校正打分。
- 相关记录:
TASK-upstream-sync2-20260909.md;上游 09-04 婚恋触发审计 - 复发自:无(上游审计结论在本仓补登记)
- 修复版本:待发布
BUG-609 | 网页咨询婚恋路径喂的是出生大运,事件类 hits 恒空
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
/api/consultation_workflow婚恋主题、_thematic_dasha_info、report_orchestrator婚恋timing - 用户现象:问结婚时机时,心动 / 成对 / 领证拆分没有大运命中;时间标签可能出现
Unknown,并仍写出「缔结或调整伴侣关系的关键期」。 - 触发条件:网页咨询走复用 chart 模块的婚恋主题,chart 模块 dasha 没有
vimshottari_analysis。 - 根因:
_thematic_dasha_info只从 full-reading 的vimshottari_analysis取当前 MD/AD。咨询路径 09-06 起复用 chart 模块后这个字段不存在,于是只剩出生时current_md。BUG-608 把dasha_info接上后,断链第一次变成空 hits。激活句也不看event_class_split。 - 修复:当前 MD/AD/PD 改从
chart.modules.dasha_sub_periods.current读取,缺了再回退vimshottari_analysis;婚恋activation_description按事件类出句;AD 缺失时dasha_period只写 MD。 - 验证:
tests/test_thematic_dasha_info_source.py、tests/test_report_orchestrator_marriage_timing.py、tests/test_relationship_event_class_evidence.py咨询形黄金件。 - 防复发:上述测试列入
CORE_PYTEST_TARGETS。咨询契约只增不改。感情分析模块逻辑未改,只修喂数。 - 相关记录:BUG-608、BUG-555、
TASK-upstream-sync2-fix-20260909.md - 复发自:无(BUG-608 接入后的喂数断链)
- 修复版本:
7d3bb0c5
BUG-610 | 分盘围栏构造失败会让整份长报告 500
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
render_pl9_markdown、/api/reports/<id>/professional-reference、pl9-export - 用户现象:某一张分盘上升星座名不在英文十二星座内时,专业参考 Markdown 与导出一起失败。
- 触发条件:分盘
Ascendant.sign缺失或为中文星座名,围栏build_chart_block/_sign_name抛ValueError。 - 根因:南印度 SVG 在
try内,失败只写「图盘生成失败」;紧接着的围栏构造在try外,一张分盘出错就中断整份报告。 - 修复:围栏失败只丢掉该张围栏、保留 SVG,并打一条不含用户串的 warning。
- 验证:
tests/test_report_chart_block.py中文上升名用例:Markdown 仍含<svg,该处无jyotish-chart围栏。 - 防复发:该文件已在
CORE_PYTEST_TARGETS。失败路径不加围栏,也不再打断报告。 - 相关记录:BUG-607、
TASK-upstream-sync2-fix-20260909.md - 复发自:无(BUG-607 围栏成功路径未覆盖失败分支)
- 修复版本:
7d3bb0c5
BUG-611 | /api/health 的 version 写死旧 Skill 号
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
GET /api/health、chart 成功/回退响应里的version - 用户现象:健康检查报的 Skill 版本与
jyotish_vedic.__version__不一致。 - 触发条件:访问
/api/health或读取 chart 响应version。 - 根因:版本字符串散落三处硬编码,没有从
jyotish_vedic.__version__读。 - 修复:三处改为读取
__version__;回退路径保留-fallback后缀。 - 验证:
tests/test_api_server_security.py::test_health_endpoint_exposes_runtime_accuracy_metadata。 - 防复发:健康检查断言
version == jyotish_api_server.__version__。 - 相关记录:
TASK-upstream-sync2-fix-20260909.md - 复发自:无
- 修复版本:
7d3bb0c5
BUG-612 | 首页「深入看今日」计算完成却没有回答(empty_answer)
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:首页「深入看今日」、
POST /api/consultagentic 路径、canonicalDomainPlan、分段写作composeSection/composeByHeadings - 用户现象:活动区走完「读取分析方法 → 计算本命盘 → 先整理本盘的统一参数 → 接下来分析你的综合 → 用审计表收口后再落到生活」后,正文只有「计算已完成,但这次没有生成回答,本次不会扣点。请再发送一次。」(
empty_answer)。不扣点是对的,但用户拿不到回答。 - 触发条件:已校验星盘账号从首页点「深入看今日」(
theme: timing+entrypoint: daily_starlanguage);计费配置已有chat.standard。staging 观测到该 runretryCount=5、modelFinishReason=tripwire、墙钟约 110s。 - 根因:两层叠在一起。其一,
canonicalDomainPlan在模型显式传了domains时以模型为准,路由选的timing被改成general,分段标题变成本命「综合 / 技法审计表 / 现代生活」,与入口扩展的「今日趋势 / 适合推进 / 避开 / 行动」错位。其二,每个分段maxSteps = 1且工具仍可调,模型在分段里再发起工具调用(想换回timing),一步用完、零正文;四个分段全空后再走 BUG-280 的retryForAnswer,面对的还是同一套错位标题,救不回来。 - 修复:
daily_starlanguage/birth_time_rectification入口钉死路由theme,模型改写只记planOverrideIgnored,不报错不扣步。今日入口改用三节写作计划(今日趋势/适合推进 / 需要避开/一个行动,审计表和边界句折进最后一节)。分段toolChoice: "none";某一节没有新正文时同节再写一次(section-empty-retry),仍空才进入既有answer-retry。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts新增入口钉死、分段空写重试、全空才answer-retry;consultation-thinking-plan.test.ts锁三节标题与applyThinkingSectionProgress;consultation-workflow-contract.test.ts锁toolChoice: "none"与 entrypoint 接线。定向tsx --test上述文件 +public-thinking+consultation-entrypoint共 119 项通过;tsc --noEmit清洁。staging[agent-observability]已见活动区「综合」、tripwire、110s、retryCount=5。tool.input.domains未出现在该次日志里,记investigating,不挡住决策 1–3(模型改写领域是活动区文案已证明的事实)。 - 防复发:入口请求不得执行模型自选领域。分段写作禁用工具。空切片必须先就地重试,不能把「标题错了所以写不出」交给 BUG-280 的整轮回答重试。
- 相关记录:BUG-280(同一「计算成功、模型没写」出口;本条是新来路)、BUG-613(同一次事故的思考区词渣)、
TASK-consultation-daily-empty-answer-20260909.md - 复发自:BUG-280(现象同类,防线只覆盖「整轮没写」;没有挡住入口领域被改写,也没有挡住分段再调工具)
- 修复版本:待发布
BUG-613 | 咨询思考流按 chunk 过滤,英文被削成词渣
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
frontend/src/lib/public-thinking.ts、咨询thinking.delta、校正思考映射复用同一 sanitizer - 用户现象:与 BUG-612 同一次「深入看今日」里,思考区出现缺词英文(
The a for 2026-09-09 (, per). The is a - an, not a. … Let me run-jyotish-.),夹杂工具名前缀。 - 触发条件:模型思考以 1–2 个英文词为一个
reasoning-deltachunk 流出;其中部分 chunk 含 ≥4 字母英文且无中文。 - 根因:
sanitizePublicThinkingText按单个 chunk 判定「含 ≥4 字母英文且无中文就丢」。长词所在 chunk 被丢,短词(The / a / for / is)和带中文的 chunk 被放行,拼出来就是词渣。工具名正则只剥了带后缀的完整 id,留下run-jyotish-。 - 修复:改为有状态的按句缓冲(
。!?、换行、英文.!?)。无中文句整句丢;中文句里再删 ≥4 字母英文词和完整工具名/UUID;未闭合的尾巴等下一个 chunk,一次模型循环结束时 flush。咨询流式通道共用同一个 sanitizer 实例。sanitizePublicThinkingText(text)仍是 push+flush,校正映射不用改调用点。 - 验证:
frontend/tests/public-thinking.test.ts把事故英文按 3–6 字符切开喂入,输出不含 ≥4 字母英文词、不含run-jyotish,中文句完整;另锁「未闭合英文不得粘到后一句中文」。既有consultation-agentic-runtime/rectification-v9-stream思考通道回归仍绿。 - 防复发:思考过滤不得按流式 chunk 单独判定英文。工具名要从
run-jyotish前缀整段删,不能只剥后缀。 - 相关记录:BUG-612(同一次事故的空回答)、
TASK-consultation-daily-empty-answer-20260909.md - 复发自:无
- 修复版本:待发布
BUG-614 | 三列卡把「还没对照」显示成 0 和没有窗口
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
scripts/rectification/refinement_packet.py(column_times_for_compare)、frontend/src/lib/rectification-agentic/v9/divergence-panel.ts、rectification-range-delivery.tsx、user-copy.ts - 用户现象:区间卡第 2、3 列写「强相关 0 · 有关联 0 · 弱关联 0」,三列都写「未来一年没有明显的窗口」;相同性格句每列重复一遍。
- 触发条件:推断层后验前三与引擎分数前三不是同一组分钟;客户端按列时间查
event_dasha_ledger_by_time/prospective_windows_by_time找不到键。 - 根因:
column_times_for_compare只取引擎分数前三,卡片列按后验前三投影。缺键时投影把空表当成 0 次吻合、把缺窗口当成「没有窗」。 - 修复:
columns改为引擎candidate_times去重全集(上限 9)。客户端缺键写fit: null/windows: null,灰字「这一分钟还没对照」,不得回退成 0。经历对照改成「N 件里 M 件对得上」;无窗写「未来一年没有明显的时段」。三列共有的性格句只出现在卡顶一次。 - 验证:
tests/test_rectification_v5_services.py::test_compare_ledgers_cover_all_engine_candidate_times;frontend/tests/rectification-range-delivery-20260907.test.ts后验前三与引擎前三不同时fit非 null,缺键 DOM 出现「这一分钟还没对照」、不出现0 · 0 · 0。 - 防复发:
set(ledgers) == set(candidate_times);用户可见文案禁用「强相关 / 有关联 / 弱关联」;列内不得再出现<h3>「经历对照 / 往后 12 个月」。对照盘高亮一旦给.is-changed写规则,该类名必须离开knownUnstyled(见 BUG-622)。 - 相关记录:BUG-597、BUG-615、BUG-622、
TASK-rectification-compare-card-polish-20260909.md - 复发自:BUG-597(三列对照的 by_time 只覆盖引擎前三)
- 修复版本:待发布
BUG-615 | 交付旁白被证据轮裁句裁成一句
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
frontend/src/lib/rectification-agentic/v9/collect-prompt.ts、agent-run.ts - 用户现象:交付轮助手正文只剩「校正到这里可以收口了。」一类单句,BUG-595 定的三句(范围、经历与吻合率、边界句)被裁掉。
- 触发条件:访谈收口出区间卡;该轮
action === "evidence",模型写了三句以上。 - 根因:BUG-606 的
trimEvidenceTurnBody(超过两句只留首句)对所有 evidence 轮生效。交付也走 evidence 轮。 - 修复:先跑
persistNextInterviewIfIdle。仍有下一问时继续裁到一句;该函数返回terminalNote时保留 ≤3 句。裁剪不得发生在 idle 之前,否则交付四句会先被裁掉。裁完与流式正文不同时发answer.delta replace,气泡跟落库同一段。 - 验证:
frontend/tests/rectification-v9-agent.test.ts交付轮模型写四句 → 落库三句;普通证据轮三句 → 一句。frontend/tests/rectification-collect-prompt.test.ts锁trimSpokenTurnForInterview。 - 防复发:
agent-run必须在 idle 之后调用trimSpokenTurnForInterview(answerText, interviewIdle?.terminalNote === true)。不得把交付轮重新裁成一句。 - 相关记录:BUG-606、BUG-595、BUG-614、
TASK-rectification-compare-card-polish-20260909.md - 复发自:BUG-606(证据轮裁句没有把交付轮排除)
- 修复版本:待发布
BUG-616 | 报告页 22 张北印盘叠在同一位置
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
/personal-reportMarkdown 阅读页、personal-report-markdown-view.tsx、globals.css、report-chart-grid-rehype.ts - 用户现象:「Birth Chart / Vargas」里 D1 / D9 / Moon 三张盘叠在同一块;Vargas I 的 19 个
####标题挤在左上角,19 张盘全部叠在一起,「Shodashvarga 星座总表」和后面的表也压在盘上。 - 触发条件:打开含
jyotish-chart围栏的个人长报告详情页,宽于 720px 的桌面视口。 - 根因:BUG-607 用浮动 +
margin-left: -50%在扁平 Markdown 兄弟节点上凑两栏。负 margin 让每个 figure 的 margin box 宽度为 0,浮动算法认为它不占横向空间,连续多对标题+盘必然重叠。renderToStaticMarkup只看 DOM,看不见布局,所以当时的 golden 全绿。 - 修复:删掉
.personal-report-chart-*上的 float / 负 margin。rehype 插件把「h4(D… / Moon Chart)+ 可选一段说明 +pre > code.language-jyotish-chart」包进.personal-report-chart-card,相邻卡片再包进既有的.personal-report-chart-grid(单张加is-single)。不放开skipHtml,不引入rehype-raw。 - 验证:
frontend/tests/personal-report-markdown-view.test.tsx:3 对连续标题+围栏(第 3 对夹一段p)得到 1 个 grid / 3 张卡,卡内顺序为h4→(p)→figure,后面的普通h4不在卡内;单对带is-single;golden 22 张figure全在卡内;globals.css中同时含personal-report-chart与float:/margin-left: -50%的规则为 0。 - 防复发:布局合同走 DOM 结构(grid/card 包裹)和 CSS 禁令,不只看「有没有
<svg>」。 - 相关记录:BUG-607、
TASK-report-chart-layout-fix-20260909.md - 修复版本:
87daffe2
BUG-617 | 滚动报告时整篇 Markdown 卸载重建,页面卡顿
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
personal-report-markdown-view.tsx目录高亮与文章树 - 用户现象:长报告页(尤其 Vargas I 19 张盘揭开后)一滚动就明显卡,像整页在闪。
- 触发条件:打开含星盘围栏的长报告,滚动使目录高亮从一个
h2/h3切到下一个。 - 根因:
activeId放在PersonalReportMarkdownView顶层,IntersectionObserver 每次切换都setActiveId。markdownComponents()每渲染新建一套h2/h3/h4/pre函数;React 按元素 type 引用比较,type 变了就卸载重建整棵子树。09-06 Markdown 直渲时已是这个结构,当时没有 SVG,代价没被感知;22 张盘进来后每次滚过一个标题就销毁再创建全部多边形和<text>。 - 修复:目录高亮下沉到
ReportToc;PersonalReportMarkdownView不再持有滚动态。lead 与LazyMarkdownSection的renderMarkdown结果整树useMemo,LazyMarkdownSection本身memo()。 - 验证:源码合同:
renderMarkdown(只在useMemo内调用;PersonalReportMarkdownView函数体没有useState。本仓测试是renderToStaticMarkup,数不了重渲染次数;真机条目写在docs/testing/report-chart-render-20260909.md第 7 节。 - 防复发:文章树不得订阅目录高亮 state。
components映射可以每次renderMarkdown新建(id 去重 cursor 需要),但调用点必须被useMemo包住。 - 相关记录:BUG-616、
TASK-report-md-page-20260906.md、TASK-report-chart-layout-fix-20260909.md - 修复版本:
87daffe2
BUG-618 | 采集题「没有 / 记不清」浮在输入框上方,离问题太远
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
rectification-agentic-chat.tsx口述采集快捷回答、globals.css.rectification-collect-replies - 用户现象:口述采集时,「没有」「记不清」两个按钮贴在底部输入框上方,和问题气泡分开,看起来像多余的条。
- 触发条件:
collect_spoken焦点激活且未 busy。 - 根因:BUG-605 把快捷回答挂在
composer-wrap输入框上方,没有跟选择题卡一样嵌进同一条助手气泡。 - 修复:两个按钮改到问题
afterAnswer里,紧挨题干;composer 不再渲染它们。点选仍走原来的「没有 / 记不清」短路。 - 验证:
frontend/tests/rectification-spoken-collect.test.ts(按钮在 afterAnswer,不在 composer-wrap)。 - 防复发:采集快捷回答不得回到 composer。后续产品否决该芯片,见 BUG-628;不得按本条把「没有 / 记不清」按钮加回。
- 相关记录:BUG-605、BUG-628
- 复发自:无
- 修复版本:待发布
BUG-619 | 生时校正时间轴比对话内容更贴边
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
globals.css.rectification-timeline水平内边距 - 用户现象:校正对话上方的时间轴比下面的候选卡、助手气泡更靠屏幕外边,左右对不齐。
- 触发条件:打开生时校正,尤其是已出现三列候选卡时。
- 根因:条用
space-5/ 窄屏space-4,对话列是space-8/space-4再加头像让位--assistant-content-inset;条又不在.message-list里,继承不到该变量。 - 修复:条的内边距改为对话沟槽加头像让位;背景和底边发丝线仍拉满。高度不变。
- 验证:
frontend/tests/rectification-timeline-20260909.test.ts。 - 防复发:时间轴水平内边距必须含
--assistant-content-inset,不得写回贴边的space-5。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-620 | 点「更像这个」后三列卡消失,收尾句贴左不对齐
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
rectification-agentic-chat.tsx区间交付卡、verified_idle收尾句、globals.css.rectification-pending-note - 用户现象:点「更像这个」后三列时间卡忽然消失,底下出现未对齐的「已采用 HH:MM,新建对话即按这个时间排盘。」
- 触发条件:交付卡出现后点其中一列「更像这个」;尤其是没有后续核对题、
next_user_action为start_consultation时。 - 根因:
showSelectionCards用!busy整张卸卡;采用请求把对话标成 busy。收尾句用未定义样式的.rectification-pending-note直接挂在消息列表里,没有助手列让位。采用后若 live offer 锚点被清掉,卡也不再回到原消息。 - 修复:采用中或已采用时 busy 不再卸卡;用 resultId 锁住首次出示卡片的那条消息。收尾句改到卡片下方,样式与卡片同一条左边线。已采用列按钮保持按下并禁用。不恢复输入框上方状态条。
- 验证:
frontend/tests/rectification-adopt-cards-stay-20260909.test.ts、frontend/tests/rectification-candidate-offer-anchor.test.ts。rectification-surface-contract.test.ts锁verified_idle为verifiedIdleCopy && !showSelectionCards(BUG-622)。 - 防复发:交付卡不得因采用 busy 卸载;
verified_idle不得使用无 inset 的裸段落;不得把采用状态写回 composer。改verified_idleJSX 条件时必须同步表面合同正则。卡锚锁必须走渲染期useState,不得在 render 里读写 ref(react-hooks/refs)。 - 相关记录:BUG-595、BUG-619、BUG-622
- 复发自:无
- 修复版本:待发布
BUG-621 | 升 Skill 版本后历史生时校正从列表点不开
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
openRectificationCase、open_agentic_rectification_case_v2、历史会话列表、KNOWN_RPC_ERROR_CODES - 用户现象:历史对话里的生时校正(含当天较早创建的)点了没反应,主栏也不换会话。
- 触发条件:生时校正 Skill 已从 10.0.15 升到 10.0.19;点开绑定旧版本的历史校正。
- 根因:08-14 不可变注册表让 Case 钉住自己的
skill_name/version/sha256/source_commit,agent 也按绑定版本跑。打开 RPCopen_agentic_rectification_case_v2却把当前注册版本与 Case 做相等比较,任一不同就skill_identity_mismatch。该错误码未映射,变成 500「校正服务暂时不可用」;客户端再兜成「暂时无法打开生时校正」。错误只画在首页起始卡下,而 BUG-599 之后selectSession等 Case 打开成功才切会话,历史列表点击看起来像没反应。 - 修复:
intent === "session"时先走 v1 按会话取 Case,再读绑定身份,用resolveExactSkillPackage核验快照存在且 sha 一致,核验通过后把绑定身份交给 v2。homepage/new仍用当前注册版本。映射skill_identity_mismatch→ 409。历史打开失败把错误画在被点的那一行下面,起始卡不重复同一条。不改迁移、不改采用门。 - 验证:
frontend/tests/rectification-v9-case-service.test.ts、frontend/tests/rectification-history-open-20260909.test.ts、frontend/tests/skill-registry.test.ts。Docker DB 套件本机跑不了(环境缺口);本轮无新迁移。 - 防复发:按会话打开必须用 Case 绑定身份,不得把当前注册版本交给 v2 做相等比较。升 Skill 版本后,历史校正必须仍能从历史对话打开,且以其绑定版本运行。废弃版本快照须仍可
resolveExactSkillPackage。 - 相关记录:BUG-599、BUG-583、BUG-593、BUG-595、BUG-597、BUG-604
- 复发自:无
- 修复版本:待发布
BUG-622 | staging 质量门因两条过期前端合同红掉,Deploy staging 未派发
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:Gitea Independent Staging Quality Gate(
npm test --prefix frontend)、class-name-definition-contract.test.ts、rectification-surface-contract.test.ts - 用户现象:staging 推送后质量门失败,镜像不发布。产品 UI 无新故障。
- 触发条件:
ca921c3f(BUG-618–620)改了verified_idleJSX;5c476ba7(BUG-614/615)给.personal-report-chart-cell.is-changed写了填充。随后af70ae77再推 staging 仍是同一对失败(runs 2514 / 2516 / 2517)。 - 根因:产品代码已改,合同测试仍锁旧字面量。
knownUnstyled还列着已有 CSS 规则的is-changed;表面合同仍要求questionGap === "verified_idle" && (,实际已是verifiedIdleCopy && !showSelectionCards。测试转绿后 lint 才跑到:BUG-620 在渲染期读写selectionOfferLockRef,react-hooks/refs4 error。 - 修复:
is-changed移出 allowlist;verified_idle断言改跟现行 JSX。采用卡锁改为useState,在渲染期按seededTurns同一模式更新(nextSelectionCardLock同值返回同一对象,避免循环 setState)。 - 验证:
frontend/tests/class-name-definition-contract.test.ts、frontend/tests/rectification-surface-contract.test.ts、frontend/tests/rectification-adopt-cards-stay-20260909.test.ts、frontend/tests/rectification-candidate-offer-anchor.test.ts;npm run lint0 error。 - 防复发:给已在 allowlist 的 class 加规则必须同时删 allowlist 行;改
verified_idle条件必须改表面合同正则,并写原值/新值/原因。跨渲染保留的卡锚不得写进 ref 再在 render 里读。 - 相关记录:BUG-614、BUG-615、BUG-620
- 复发自:无
- 修复版本:待发布
BUG-623 | 一小时窗口按时间取前 12 簇,后段真实出生时间从未进入候选
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
scripts/rectification/candidate_contrast.pyselect_signature_representatives、MAX_PUBLIC_CLUSTERS、公开候选集 - 用户现象:intake 填一小时窗后,第一次比较给出的范围停在窗口前段(例如 14:00–15:00 只收到约 14:04–14:43)。用户知道的更晚时段从头到尾不在候选里。
- 触发条件:分钟网格上签名簇超过 12 个。半小时窗(±15)通常 ≤12,所以以前测不到;「前后半小时」的 60 分钟窗必现。
- 根因:
cluster_contexts_by_signature按代表分钟时间排序后,循环里len(representatives) >= MAX_PUBLIC_CLUSTERS直接break。多出的整簇被丢掉,而且永远是窗口尾部。这发生在证据评分之前。 - 修复:不再按时间截断。安全上限改为 64;超过 64 时合并相邻低分簇,不丢尾部。公开候选仍是每簇一个代表分钟。
- 验证:
tests/test_rectification_v5_services.py的 17 簇夹具(含 14:46–14:51)与 65 簇相邻合并保尾。 - 防复发:禁止再对时间排序后的簇做
break截断;超上限只能按分数合并相邻簇。 - 相关记录:BUG-560(以前「分钟级≈随机」的校准是在 ±15 窗口上得出,一小时窗还叠加了本截断)
- 复发自:无
- 修复版本:待发布
BUG-624 | 可信区间按代表分钟跨度算,簇内其余分钟被视觉淘汰
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
build_candidate_decisions、推断层cluster_range、credible-range.ts - 用户现象:即便某簇还活着,区间右端只写到代表分钟。例如 14:40–14:45 这一簇的代表是 14:40 时,读数停在 14:40。
- 触发条件:签名簇覆盖连续多分钟,代表分钟不是簇的末端。
- 根因:引擎候选只带
time。推断层用换升时刻在代表分钟之间重新聚类,多数变成单分钟;unionStillValidRange再取这些点的跨度。 - 修复:每个公开候选带
cluster_times/cluster_start/cluster_end。推断层优先用引擎簇覆盖;换升聚类只作缺字段时的兜底。可信区间是仍有效簇覆盖的并集。时间轴实心/空心点仍画代表分钟。 - 验证:Python 簇 14:40–14:45、代表 14:40 →
cluster_end=14:45;frontend/tests/rectification-window-cluster-cap-20260909.test.ts区间右端 14:45。 - 防复发:可信区间不得只用代表分钟;缺
cluster_times才允许换升兜底。 - 相关记录:BUG-623
- 复发自:无
- 修复版本:待发布
BUG-625 | 中途说出出生时段时助手口头答应改窗口,但什么都没改
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
frontend/src/app/api/rectification/agent/route.ts自由文本、declared-window-utterance.ts、Skill 10.0.20、agent-run正文守卫 - 用户现象:校正做到一半,用户说「我的出生时间是 14 点 45 到 14 点 50」。助手回答「明白了,以你说的为准」,搜索窗口和候选集不变。下一句「继续吧」被当成没听懂的点选题回答。
- 触发条件:点选或采集焦点下,用户用自由文本申报钟点范围或「HH:MM 左右」。
- 根因:15 个工具里没有改搜索窗口的入口;opening brief 还禁止改窗口。自由文本进意图分类器,枚举没有「申报时间段」,落
unclear。模型在采集焦点下会自己答应。产品否决了中途改窗口。 - 修复:自由文本在分类器和选模型之前做确定性解析。命中则落库固定回复,不调模型,不改
candidate_range,当前焦点保持。正文守卫删除「以你说的…为准」。Skill 写明不得口头改窗口。intake 自定义更小范围等产品答复,本单不做。 - 验证:
frontend/tests/rectification-window-cluster-cap-20260909.test.ts(解析、路由在选模型前拦截、簇覆盖区间、禁用口头承认)。 - 防复发:中途申报时段不得进模型、不得改窗口;助手不得写「以你说的为准」。
- 相关记录:BUG-623、BUG-572
- 复发自:无
- 修复版本:待发布
BUG-626 | 跳过的健康线在 holdout 里被当成没用过,撞同 id 焦点后静默断流
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
holdoutFollowupFor、persistNextInterviewAfterChoice、persistExhaustionCollect - 用户现象:健康采集点「记不清」后再答完职业,界面只剩「没有拿到下一个问题。」
- 触发条件:七领域采集接近穷尽,健康焦点
target_domain=health且skipped,盘外提示仍是health_pressure。 - 根因:BUG-590 只给 occupied 归并 health / health_pressure,holdout 的 declined 也曾漏过这一层;即便归并后,
duplicate_focus仍可能落到空旁白。DB 约束只有health,计划层用health_pressure。 - 修复:holdout 的 declined 与 occupied 一样归并 health / health_pressure。
duplicate_focus记rectification_focus_duplicate并走persistExhaustionCollect;穷尽旁白为空时用noCandidatesGate兜底。 - 验证:
frontend/tests/rectification-collect-direction-20260904.test.ts、frontend/tests/rectification-skipped-health-deadend-20260909.test.ts。 - 防复发:跳过的健康线本会话不得再作 holdout;
duplicate_focus必须留下可见载体。 - 相关记录:BUG-558、BUG-590、BUG-591、BUG-605
- 复发自:BUG-590 决策 4(occupied 已归并,declined 曾漏)
- 修复版本:待发布
BUG-627 | 出口闸门把穷尽当成已交付,断流后不修复
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
inspectNonTerminalTurnExit、ensureNonTerminalTurnExit、agent-runidle catch、POST /api/rectification/cases/[caseId]/repair-exit - 用户现象:穷尽后没有任何问题、卡或句子落库,闸门仍放行;「重新加载」只重取空快照。
- 触发条件:
exhausted为真,无活动焦点、未采用、无 terminalNote host turn。 - 根因:
inspectNonTerminalTurnExit.satisfied含exhausted。穷尽只是状态,不是用户能看见的出口。idle 异常被agent-run吞掉后也到不了闸门。 - 修复:satisfied 只认活动问题 / 已采用 / 已确认 / 已落库的 terminalNote host turn /
publicCanAdopt。无载体时ensureNonTerminalTurnExit写出门槛 host turn。idle 失败后仍调用闸门。缺口按钮改调repair-exit,文案「接着问」;连续两次仍无载体才提示新建。 - 验证:
frontend/tests/rectification-exhaustion-exit-20260906.test.ts、frontend/tests/rectification-skipped-health-deadend-20260909.test.ts。 - 防复发:穷尽不得单独满足出口闸门;无可见载体必须写出 host turn。
- 相关记录:BUG-558、BUG-626
- 复发自:BUG-558 防复发条款被闸门绕过
- 修复版本:待发布
BUG-628 | 口述采集「没有 / 记不清」按钮被用户差评
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:口述采集快捷回答、
rectification-agentic-chat.tsx、globals.css - 用户现象:生时校正口述采集题下面挂着「没有」「记不清」,刚进入开场和后续分领域追问都会出现;用户一致觉得像在鼓励关掉采集。
- 触发条件:任意 live
collect_spoken题。 - 根因:BUG-605 / BUG-618 把拒答芯片做成默认采集 UI。开场是开放叙述,后续口述题也不是点选题;芯片把「没有」做成最显眼动作。
- 修复:删除
CollectSpokenReplies及其样式。打字「没有」仍走 declined,「记不清」仍走 skipped。选择题卡选项不动。 - 验证:
frontend/tests/rectification-spoken-collect.test.ts(组件、样式、composer 均无芯片;打字短路仍在)。 - 防复发:口述采集不得再渲染「没有」「记不清」按钮,也不得挂回 composer。
- 相关记录:BUG-605、BUG-618
- 复发自:无
- 修复版本:待发布
BUG-629 | 无年月性格题与带年月题同权,参与了淘汰
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:
applyProbeOutcome、buildMethodFollowupPlan、技法审计表、Skill 10.0.21 - 用户现象:真实校正里头两道题就是性格自评(相处方式 / 做事风格)。这些题没有年月锚点以外的分辨力验证,却按 ±2 计分并计入三次淘汰。
- 触发条件:账本已有感情或事业锚点(BUG-559),候选尚未分开,计划层把
varga_style/nakshatra_boundary与带年月区分题排在同一池。 - 根因:
varga_style与带年月dasha_boundary共用SCORE_DELTA和strong_conflict_count。分盘星座是确定计算,「星座 ↔ 性格」「用户自评 ↔ 类型」两层从未校准。 - 修复:性格题只在可问的带年月区分题为空、且仍有 ≥2 个未分开候选时才成为下一问,否则
dropped_probes(reason="yearless_deferred")。分值 ×0.5(±1),不递增冲突次数,rounds.kind=tie_break。报告D9 / D10 类型对照与月宿边界标reference。离线脚本只对医院记录且不确定度 ≤2 分钟的匿名导出算命中率。 - 验证:
frontend/tests/rectification-yearless-probe-downgrade-20260909.test.ts;tests/test_varga_style_calibration_report.py;既有rectification-probe-replay-loss-20260908.test.ts断言未改。 - 防复发:不得把
varga_style/nakshatra_boundary加回带年月区分池;不得让性格冲突计入STRONG_CONFLICT_ELIMINATION_COUNT;不得改SCORE_DELTA数值来代替权重。asInferenceState的ROUND_KINDS必须含tie_break,否则答过性格题的inference_state整份被丢掉。审计列 CHECK 仍只认informative/low_information;tie_break只活在 JSON,不得为迁列把 kind 改回informative。 - 相关记录:BUG-559、BUG-560、BUG-623
- 复发自:无
- 修复版本:待发布
BUG-630 | 初始化后点首页「家庭」报运行合同未完成(runtime_contract_incomplete)
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:首页主题卡、「家庭」等 onboarding 建议、
POST /api/consult本命 agentic 路径、run-jyotish-consultation合同门禁 - 用户现象:填完生辰和地址后点「家庭」,对话里出现用户问题(例如「请帮我看看我家庭关系的整体模式和特点」),随后红字「Agent 未完成必要的方法与计算步骤,本次不会扣点。」不扣点是对的,但没有回答。
- 触发条件:已有可用出生分钟的账号完成初始化,从首页「从一个主题开始」点家庭(或其它主题卡);模型为 thinking 模式(事故为
deepseek-v3-flash)。 - 根因:本命合同要求本轮成功调用一次
run-jyotish-consultation。Skill 已由服务端写入系统提示,用户回合仍写「先加载 Jyotish Skill」,thinking 模型会把第一步花在skill_read或直接按 Level 2 骨架空写。家庭路由没有 named strict checklist,模型更容易不调计算工具。主题卡也不带入口身份,领域钉死只覆盖「深入看今日」和生时校正,家庭卡的theme: family可被改写成别的合法领域。Python 家庭 workflow 本身能算完,不是计算 400/500。 - 修复:本命 Agent 第一步
prepareStep只开放run-jyotish-consultation,toolChoice: "auto"(thinking 提供商拒绝 named/required)。合同补跑与空回答补跑用同一钩子。分段写作和续写仍用原来的streamOptions,禁止把该钩子接到无分钟 / 声明窗口 Agent。首页主题卡带entrypoint: guided_topic,钉死路由主题且不改写可见问题。钉死入口的用户回合去掉「先加载」,并要求调用时不要填domains。普通手输问题仍可自选领域。 - 验证:
frontend/tests/consultation-agentic-runtime.test.ts家庭guided_topic忽略domains: ["general"]、第一步工具 id;consultation-workflow-contract.test.ts锁 natalprepareStep不进共享streamOptions、分段仍toolChoice: "none";consultation-entrypoint.test.ts/starter-questions.test.ts锁入口枚举与主题卡接线。 - 防复发:本命
requireTool: true的第一步必须能调到计算工具。prepareStep不得套到 general/window Agent,也不得套到composeSection。主题卡必须带guided_topic。不得把domains改成自由字符串来绕过 BUG-255 的枚举拒绝。 - 相关记录:BUG-205、BUG-214、BUG-255、BUG-257、BUG-612
- 复发自:BUG-214(同一句用户文案和合同门;本条是模型根本没成功调计算工具,不是失败计数把成功次数算超)
- 修复版本:待发布
BUG-631 | 申报时段拦截把带钟点的经历当成改窗口
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-10
- 影响面:
parseDeclaredBirthWindow、POST /api/rectification/agent自由文本、采集/点选题下的经历落库 - 用户现象:用户报经历时带了钟点(例如「2024 年 8 月 8 日 20:00 左右分手」),助手立刻回复「搜索范围开始时按资料定、过程中不改」,这句话不进模型,证据也不落库。
- 触发条件:校正做到一半,自由文本里出现
HH:MM/X 点 Y 分加「到 / 至 / -」或「左右 / 前后」,同时这句话其实是经历而不是申报出生时段。 - 根因:BUG-625 只按钟点样式拦截。经历里的钟点和申报出生时段共用同一套样式,没有出生语境或年月日门。
- 修复:命中钟点后再判语境。含出生语境词才算申报;含年月日且无出生语境一律当经历;只有裸钟点范围(可带「大概 / 下午」这类虚词)仍拦截。
- 验证:
frontend/tests/rectification-declared-window-20260909.test.ts;既有rectification-window-cluster-cap-20260909.test.ts补了三句经历不拦截。 - 防复发:不得只靠钟点正则拦截自由文本;带年月日的经历即使含「20:00 左右」也必须进模型。走查见
docs/testing/rectification-window-cluster-cap-20260909.md第 4 节。 - 相关记录:BUG-625
- 复发自:BUG-625(拦截范围过宽)
- 修复版本:待发布
BUG-632 | 经历对照只算引擎前 9 个候选,推断前三会显示还没对照
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-10
- 影响面:
column_times_for_compare、三列对照卡event_dasha_ledger_by_time/prospective_windows_by_time - 用户现象:一小时窗有 17 个引擎候选时,对照卡某一列写「这一分钟还没对照」。
- 触发条件:BUG-623 之后公开候选可以超过 9 个;推断层前三不在引擎候选列表的前 9 个里。
- 根因:BUG-614 把对照从「引擎分数最高的三个」改成「引擎候选列表」,但仍保留
limit=9。卡片按推断前三查表,键不在这 9 个里就显示没对照。 - 修复:默认上限改为公开候选全集(
MAX_PUBLIC_CLUSTERS,64)。column_times仍可传入更小的 active 集合;column_compare_ms继续记录耗时,预算 3 秒。 - 验证:
tests/test_rectification_v5_services.py:17 个候选 → by_time 17 键,65 个截到 64,column_compare_ms低于 3000;column_times子集 3 键。前端columnTimesForSlowCompare在上一轮超过 3 秒时把 active 分钟写入引擎请求。 - 防复发:不得把对照列截回前 9;推断层查表的分钟必须落在 by_time 键集合里。超 3 秒时只允许改传入的
column_times,不能再静默丢掉后段候选。 - 相关记录:BUG-614、BUG-623
- 复发自:BUG-614(对照集合仍被 9 截断)
- 修复版本:待发布
BUG-633 | 证据轮模型无正文被判整轮失败,下一问已落库却只剩「没有拿到下一个问题」
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
agent-run.tsstreamAttempt收尾、host-fallback.ts、rectification-agentic-chat.tsx失败刷新、rectificationQuestionGapState - 用户现象:补完带年月经历后,助手气泡下面直接是「没有拿到下一个问题。」和「本轮没有生成可展示的回复,状态已记录。」输入框写着「请回答上面的问题…」,上面没有问题。刷新无效。
- 触发条件:证据轮
rectification-record-evidence-batch已 completed,下一问焦点已落库,模型以stop/length/max_steps结束且没有可展示正文。 - 根因:
empty_stream不在可重试集合(fe87a9ec为避免重放证据而移出)。失败路径不结算、不写主持人正文、客户端run.failed后不刷新快照。问题只挂在 settled 助手消息上,失败轮没有助手行,缺口走preparing再unavailable。 - 修复:batch 已完成且无正文时用工具返回的事件复述写成「记下了:…。」,
finalizeTurn("completed"),phases 含answer.host_fallback,公开 receiptanswer_origin=host_fallback。empty_stream仍不进RETRYABLE_ERROR_CODES;仅当本 attempt 没有任何公开写工具(batch / set-focus / compare)完成时才 retryable 一次。失败也loadCaseSnapshot;快照有current_question且question_source=focus时渲染主持人问题行。 - 验证:
frontend/tests/rectification-v9-stream.test.ts、rectification-host-fallback.test.ts、rectification-agentic-entry.test.ts、rectification-surface-state.test.ts、rectification-spoken-collect.test.ts。 - 防复发:有写工具完成不得重试 empty_stream(BUG-186);兜底正文只能来自 batch 返回;问题行
focus_id只能来自current_question。 - 相关记录:BUG-186、BUG-359、BUG-558、BUG-627
- 复发自:无
- 修复版本:待发布
BUG-634 | 答题旁白只会说「范围没变」,时间线写死「还在收窄」
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
composeChoiceNarration、explainScoreMovement、RectificationReadonlyRange - 用户现象:多道选择题答完,每次都是「已记录,范围没变。」顶部一直「目前范围 X–Y,还在收窄」,看不出分数有没有动、选择题是不是已经问完。
- 触发条件:答题后可信区间宽度不变(落后峰值不足淘汰阈值),或带年月区分题已经问完。
- 根因:旁白只用
explainRangeChange,explainScoreMovement算得出领先/落后却没说出来。时间线「还在收窄」是写死的,不看快照里还有没有带年月探针。 - 修复:范围不变且 deltas 非零时旁白补「04:52–04:53 领先,05:08–05:12 落后」;全零只说「已记录,范围没变。」范围变了仍不讲领先落后(BUG-606)。时间线在
discriminate_candidates且没有带年月探针时写「选择题已问完,再补带年月的经历才会变」,否则「还在核对」,不写「收窄」。 - 验证:
frontend/tests/rectification-answer-choice.test.ts、rectification-surface-state.test.ts、agent-voice-copy-contract.test.ts。 - 防复发:静态用户文案词表禁止「还在收窄」;聊天源码合同禁止写死该句;范围变了的旁白不得带领先/落后。
- 相关记录:BUG-593、BUG-606、BUG-629
- 复发自:无
- 修复版本:待发布
BUG-635 | 证据轮只说「记下了」却没写入,财务经历静默丢失、流程停在原题
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
runV9AgentTurnexpectedWrite、streamAttempt收尾、RECTIFICATION_USER_COPY.evidenceNotRecorded、liveQuestionOnMessages - 用户现象:财务采集题下回一句带年月的欠债/收入变化后,助手只回「记下了:…。」下面没有下一问也没有卡;时间线仍是原范围;证据数不变;该轮照常扣点。
- 触发条件:collect 焦点下用户提供带年月经历,模型只调
rectification-read-case后按格式写「记下了」,不调rectification-record-evidence-batch/rectification-set-focus。 - 根因:路由分类结果没传给运行器,收尾只要正文非空就
completeAttempt,不看有没有公开写工具。客户端把旧消息上的同focus_id当成问题仍在显示,缺口不补主持人问题行。 - 修复:路由把
expectedWrite设为"evidence" | "none" | "unknown"传进运行器;provide_new_evidence或answer_current_focus+has_new_dated_event→"evidence"。分类器 null/出错重试一次,仍失败则"unknown"且守卫 fail-open,不用年份正则或关键词兜底。无焦点的message路径也跑同一分类器。无写入的「记下了」第一次evidence_not_written可重试(零写工具,不进RETRYABLE_ERROR_CODES);第二次主持人正文「这件我还没记上。请再说一次大概年月和发生的事。」,answer.host_fallback,不结算计费。liveQuestionOnMessages只认最后一条 settled 助手消息。 - 验证:
frontend/tests/rectification-unwritten-evidence.test.ts,以及 host-fallback / spoken-collect / agentic-entry / turn-intent-classifier / agent-voice-copy-contract。rectification-v9-agent.test.ts的 persist 三句夹具必须带 completed batch,否则 635 会把它收成未写入(Gitea run 2545)。 - 防复发:有公开写工具 completed 不得重试(BUG-186);兜底正文不得由宿主复述用户原话冒充「记下了」;旧消息同
focus_id不得挡住主持人问题行。 - 相关记录:BUG-186、BUG-278、BUG-359、BUG-449、BUG-633
- 复发自:BUG-633(只补了 batch 已完成且无正文,没补正文声称记下了但 batch 没跑)
- 修复版本:待发布
BUG-636 | 校正流把 Mastra inputSchema 拒绝信封报成 tool completed
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
mapStreamChunkToActivity/mapStreamChunkToPhase/batchResultFromToolChunk - 用户现象:本事故未触发。若模型给 batch 传了不在
EVIDENCE_KINDS的 kind,回执会显示evidence.proposedcompleted,写工具守卫会被假 completed 绕过。 - 触发条件:
createTool的inputSchema校验失败时 resolve{ error: true, message, validationErrors },流层当普通tool-result。 - 根因:咨询流 BUG-278 已按信封改发
tool.failed;校正流tool-result没有同样判定。 - 修复:信封结构
error === true且validationErrors为对象时发tool.activity failed code=tool_call_rejected,不发 completed phase;batchResultFromToolChunk与composeHostFallbackNarration对信封返回 null。公开回执不含validationErrors。 - 验证:
frontend/tests/rectification-unwritten-evidence.test.ts、rectification-host-fallback.test.ts。 - 防复发:不得把 Mastra 拒绝信封当 batch 返回值;不得把
validationErrors写进公开事件。 - 相关记录:BUG-278、BUG-635
- 复发自:BUG-278(咨询流已修,校正流未跟)
- 修复版本:待发布
BUG-637 | 账本同年键把已记学业的发挥质量题一并丢掉
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
askedKeysForSameYearDedup、inspectDiscriminatorProbes、renderableEventProbe、remainingReverseVerifyProbes - 用户现象:账本已有某年学业经历,
dropped_probes里education.YYYY.known_event_quality被标same_year_asked,界面再也问不到「那次发挥怎么样」。 - 触发条件:账本派生
domain.year键进入askedDiscriminatorKeys;发挥质量语义键必然同域同年。 - 根因:BUG-592 的同年硬排除对账本键和已答键一视同仁。发挥质量题存在的前提就是账本已有那年事件,于是被误杀。BUG-389 的测试没覆盖「账本已有同年事件」。
- 修复:账本派生的精确
domain.year键只拦存在性探针。known_event_quality/event_quality仍受回执已答键约束,同一道不问两遍。不改SCORE_DELTA、四选项、确认门。 - 验证:
frontend/tests/rectification-probe-year-dedupe-20260906.test.ts、rectification-engine-convergence.test.ts。 - 防复发:账本
domain.year不得单独让发挥质量题变成same_year_asked。已答过该质量键后仍须丢掉。 - 相关记录:BUG-389、BUG-592
- 复发自:BUG-389(被 BUG-592 覆盖)
- 修复版本:待发布
BUG-638 | 双轨一致性按网格分钟判冲突,同簇两个峰值被降置信度
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
dasha_agreement、dashaAgreementAmongActive、decision_policy - 用户现象:主限和分盘大运峰值落在同一段候选里,回执仍写冲突并降低把握;报告另算一套说只作观察。
- 触发条件:两轨 argmax 分钟不同,但同属一个
cluster_start–cluster_end。 - 根因:Python 对整窗网格分钟逐分取峰值,分钟不等即
conflict。决策把该冲突写进 reasons 并降置信度。TS 报告只在推断层 active 候选上重算。 - 修复:按簇比较:两轨 argmax 映射到所在簇,同簇即
agree。没有簇时仍按分钟比较。推断层 active 簇一致也视为 agree,即使某一轨峰值不是代表分钟。引擎原值保留为dasha_agreement_pre_inference。确认门不因本次agree从关到开。 - 验证:
tests/test_rectification_refinement_packet.py、frontend/tests/rectification-delivery-report-facts.test.ts。 - 防复发:同簇不同分钟不得再写
vimshottari_narayana_conflict。跨簇仍须conflict。不得把confirmation_allowed因本次 agree 打开。 - 相关记录:BUG-593
- 复发自:无
- 修复版本:待发布
BUG-639 | 不可分宽度按簇代表分钟少算两端延伸
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
indistinguishable_width_minutes、indistinguishableWidthMinutes、确认门adjacent_passed - 用户现象:报告宽度跟区间两端对得上,引擎「不可分宽度」却按各簇代表分钟算,少算两端延伸;极端时可把 ≤5 分钟的确认门误开。
- 触发条件:各簇
cluster_start/cluster_end超出代表分钟。 - 根因:宽度公式用代表分钟的
max-min+1,不是簇覆盖。 - 修复:改为
max(cluster_end)-min(cluster_start)+1;缺簇字段时回退到time。tied_minute_count不动。决策层与客户端解析把clusterStart/clusterEnd传进确认门。 - 验证:
tests/test_rectification_confirmation_and.py、frontend/tests/rectification-delivery-report-facts.test.ts、rectification-confirmation-gate.test.ts、rectification-candidate-result.test.ts。 - 防复发:两簇 04:50–04:55 与 04:56–05:02 必须得到 13,不得算成 7。确认门不得因代表分钟看起来相邻而打开。
- 相关记录:BUG-593
- 复发自:无
- 修复版本:待发布
BUG-640 | 宫位表和本命重算跟的不是卡片上的代表分钟
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
latestResultToolProjection、publicChartForMinute、natalRecastMeaning - 用户现象:卡片代表分钟是推断层的一分钟,回执宫位表和「已按该分钟重算」写的是引擎另一分钟;两者若跨分盘换升,正文会写错盘。
- 触发条件:推断代表分钟 ≠ 引擎代表分钟,且
house_tables_by_time两分钟都有表。 - 根因:投影已能按推断分钟取表,但引擎原表没有改名留下;Agent 看见的事实仍可能混用。
- 修复:有推断层且两分钟不同时,公开
house_table/natal_recast跟推断代表分钟;引擎原值改名representative_time_pre_inference/house_table_pre_inference/natal_recast_pre_inference。Agent 可见投影剥掉这三个字段。不改引擎。 - 验证:
frontend/tests/rectification-delivery-report-facts.test.ts。 - 防复发:公开
house_table.time必须等于投影representative_time。Agent 可见 JSON 不得含*_pre_inference。 - 相关记录:BUG-593
- 复发自:无
- 修复版本:待发布
BUG-641 | 财务与健康被写成「只有主动说才问」,服务器却会主动采集
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
skills/jyotish-birth-time-rectification/SKILL.md、conversation-strategy.md、scripts/rectification/event_probes.py、Skill 10.0.22 - 用户现象:Skill 写财务/健康只有用户主动说才问,实际对话里服务器仍会问钱和身体;Python 采集探针又不给这两域年份线索。
- 触发条件:新校正走到方法覆盖采集;账本只有学业/感情时看
evidence_collection_probes。 - 根因:三处口径各自为政。Skill L84 与 conversation-strategy 写 volunteer-only;TS
DATED_COLLECT_ORDER自 BUG-462 起含 finance;PythonVOLUNTEER_ONLY把 finance / health_pressure 挡在missing_collection_domains和evidence_collection_probes外。 - 修复:Skill 10.0.22 改为财务、健康与其他领域同权,服务器按
method_followup_plan.next_followup主动问。删除VOLUNTEER_ONLY。MAX_COLLECTION_PROBES3→7,避免学业+感情账本被前三个领域占满后发不出财务/健康。历史 10.0.21 包冻结,resolveExactSkillPackage仍能打开。 - 验证:
tests/test_rectification_event_probes.py学业+感情账本含 finance 与 health_pressure;frontend/tests/skill-registry.test.ts10.0.21 精确解析;live SKILL.md 与versions/10.0.22逐字节相等。 - 防复发:现行 Skill / 10.0.22 不得再写「主动说才问」。不得把冻结历史包改写成新口径。不得把
.workbuddy镜像当运行主仓。 - 相关记录:BUG-462、BUG-621、BUG-642
- 复发自:无
- 修复版本:
52db714b
BUG-642 | 带年份线索的采集题被整句去前缀,无年份性格卡还能抢在采集前面
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
user-copy.ts、spokenFollowupForUser、nextDatedCollectFollowup、方法覆盖链 - 用户现象:家人采集题号带 2021,题干却没有年份;同领域无年份性格卡可以抢在带线索采集题前面。
- 触发条件:
evidence_collection_probes给出age_band年份;同域还有 D12 等无年份对照。 - 根因:BUG-539 因「某年前后…记得大概哪年就行」自相矛盾而把年份前缀整句删掉。
sameDomainYearlessCard在感情/事业/家人未覆盖时优先于采集。 - 修复:采集题拆成无线索模板与带线索模板。有
probe_year时写成「家里在 2021 年前后有没有…有就说个大概年月,别的年份也行。」不得同时出现「记得大概哪年就行」。未覆盖的感情/事业/家人先采集;nextDatedCollectFollowup在DATED_COLLECT_ORDER内先排有年份线索的领域。带年月区分点选题仍在一切采集之前。 - 验证:
rectification-spoken-collect/rectification-eight-method/rectification-collect-stall:家人 2021 采集口语含「2021 年前后」、不含「记得大概哪年就行」;感情/事业/家人未覆盖时next_followup是采集而不是无年份性格卡;existence 类 D12 仍记yearless_ungrounded_contrast,没有单独的「拒答后出 D12 风格卡」用例。无probe_year用无线索模板。 - 防复发:有线索的采集口语必须带「某年前后」且允许别的年份。无线索模板不得带年份。不得让同域无年份性格卡抢在该域采集之前。
- 相关记录:BUG-539、BUG-418、BUG-629、BUG-641
- 复发自:BUG-539(整句去前缀把回忆线索一并删掉)
- 修复版本:
52db714b
BUG-643 | 「没有 / 记不清」靠原字匹配,分类器分不出「没有」和「先放着」
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
turn-intent-classifier.ts、POST /api/rectification/agent、expectedWriteFromCollectIntent - 用户现象:只有整句等于「没有」或「记不清」才走跳过;「不记得了」「忘了」落到 Agent。采集题分类器只教了
no。用户用带年月的事回答采集题时,expectedWrite仍是 none,BUG-635 守卫只剩正文「记下了」。 - 触发条件:采集焦点下打「没有」「不记得了」「2018 年 3 月开始欠债」。
- 根因:
isCollectDeclineUtterance/isCollectSkipUtterance去掉句末标点后要求整句相等。shouldDeclineCollectFocus只认no。expectedWriteFromCollectIntent不把 collect 焦点上的 yes/weak_yes 当写入。 - 修复:删除两个原字匹配函数和路由短路。分类器:没有→
no(declined),记不清→unsure(skipped),带年月直接回答采集题→yes且expectedWrite=evidence。collectFocusCloseStatus走既有applyCollectFocusDenial。分类器失败记collectIntent=unclassified,不做关键词兜底。 - 验证:
rectification-turn-intent-classifier.test.ts、rectification-spoken-collect.test.ts、rectification-unwritten-evidence.test.ts(collect + yes + 无写入 → 重试)。源码合同:route 与 classifier 不再引用那两个函数。 - 防复发:采集跳过/没有不得用正则或词表。collect 焦点 yes/weak_yes 必须
expectedWrite=evidence。分类器 null 不得回退到关键词。 - 相关记录:BUG-605、BUG-626、BUG-635
- 复发自:无
- 修复版本:
52db714b
BUG-644 | 被顶替的采集题号永久占位,落库撞号后当成已问过并断流
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
persistCollectFocus、persistExhaustionCollect、set_v10_conversation_focus - 用户现象:同一轮里分两次记下两件经历后,再答完各领域采集题,最后一条线回「没有」,助手改口说「这次给出的范围…」,下面是「没有拿到下一个问题。」感情采集题从未出现过。
- 触发条件:一轮里
record-evidence-batch被调两次,第一次落下的采集焦点被第二次顶替成superseded;之后计划层仍要问那个领域。 - 根因:RPC 把旧焦点改成
superseded而不删行,unique (case_id, question_id)仍占着原题号。计划层看不到 superseded,collect_retry为 false,persistCollectFocus撞号直接duplicate_focus。persistExhaustionCollect把duplicate_focus当成「这题已经问过并关掉了」,跳过口语采集走进交付旁白。 - 修复:采集题撞号一律试
:next/:next2/:next3;collect_retry只换重问文案。duplicate_focus与其它落库失败一样返回采集口语或中间态范围句,并在rectification_exhaustion_collect日志写下persist_status与题号。点选探针题号不加后缀。 - 验证:
frontend/tests/rectification-superseded-focus.test.ts、frontend/tests/rectification-server-focus.test.ts。 - 防复发:采集题撞号不得因
collect_retry为 false 就放弃换号;duplicate_focus不得落到 adopt 分支。 - 相关记录:BUG-462、BUG-520、BUG-525、BUG-559、BUG-626、BUG-627、BUG-633、BUG-645
- 复发自:BUG-462、BUG-626(题号唯一约束叠上顶替不删行)
- 修复版本:
a3a51c32
BUG-645 | 出口旁白说「这次给出的范围」却没有卡、公开 can_adopt 为 false
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
persistExhaustionCollect、persistNextInterviewAfterChoice、POST /api/rectification/agent非收敛区间分支、deliveryNarrationAllowed - 用户现象:助手正文是交付三句(「这次给出的范围…代表性候选」),公开
can_adopt=false、没有对照卡,下面是「没有拿到下一个问题。」 - 触发条件:内部
decision.canAdopt为真,但session_outcome=collect_evidence,公开can_adopt仍为 false;穷尽采集落库失败后走进 adopt 旁白。 - 根因:交付旁白只看内部
canAdopt。公开can_adopt只在采用结果态为真,采集期即使内部可采纳也不出卡。 - 修复:新增
deliveryNarrationAllowed:必须publicCanAdopt为真,或selection_allowed且下一动作为offer_provisional_range。三处调用在不允许时改用中间态范围句加当前采集题。 - 验证:
frontend/tests/rectification-superseded-focus.test.ts、frontend/tests/agent-voice-copy-contract.test.ts、frontend/tests/rectification-delivery-report-facts.test.ts、frontend/tests/rectification-exhaustion-exit-20260906.test.ts。 - 防复发:
can_adopt=false时源码合同禁止交付句路径;公开不能采用时不得输出「这次给出的范围」。 - 相关记录:BUG-644、BUG-626、BUG-627
- 复发自:无
- 修复版本:
a3a51c32
BUG-646 | 采集穷尽后无下一问,却显示「没有拿到下一个问题」并说做不了
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
persistExhaustionCollect、收集问题池、rectificationQuestionGapState、Skill 10.0.23 - 用户现象:各领域采集都答完或只有一两件带年月经历时,助手停在门槛句或交付口吻,下面是「没有拿到下一个问题。」,没有结果卡,也没有可继续说的入口。
- 触发条件:训练门仍关且收集池已空;或训练门已开、选择题增益见底。
- 根因:穷尽出口只写「还差 N 件,领域不限」且不落焦点。客户端把「无焦点 + 列表成功」当成问题加载失败。产品不允许「做不了」,也不允许固定题数。
- 修复:训练门关且池空时只写精确缺口句,Case 保持开放,占位「再说一件带年月的事」,客户端进入
collect_waiting,不显示「没有拿到下一个问题」。训练门开且增益见底时交付区间卡加一句具体「再补什么」。Skill 升 10.0.23。 - 验证:
rectification-exhaustion-exit-20260906.test.ts、rectification-surface-state.test.ts、rectification-collection-question-pool.test.ts。 - 防复发:训练门关不得出卡、不得出现禁词;无焦点且训练门关不得走
unavailable。 - 相关记录:BUG-558、BUG-627、BUG-645、BUG-647、BUG-648
- 复发自:BUG-558(关顶不落焦点)
- 修复版本:
dd8f35f7
BUG-647 | 恰好三件带月经历时一件被留作 holdout,训练门永远差一件
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
split-holdout.ts、scripts/rectification/case_holdout.py、trainingScoreableGate、验证报告 Real Case Calibration 行 - 用户现象:按要求给了三件带年月经历,仍到不了选择题,看起来像还差一件。
- 触发条件:带月事件恰好 3 件、2 个种类;旧规则从第 2 件起就留 holdout。
- 根因:
MIN_EVENTS_TO_RESERVE_HOLDOUT=2,3 件里留 1 件后训练只剩 2,达不到MIN_ACCEPTANCE_EVENTS=3。 - 修复:≥4 件才留 1 件 holdout;恰好 3 件全部训练。回执
holdout.status="not_reserved_min_events",报告 Real Case Calibration 写not reserved。确认门与引擎计分不变。 - 验证:
rectification-split-holdout.test.ts、tests/test_candidate_discriminator_contract.py。 - 防复发:3 件必须全进训练;4 件才允许 holdout。
- 相关记录:BUG-646、BUG-648
- 复发自:无
- 修复版本:
dd8f35f7
BUG-648 | 开场后按领域轮转盘问,题干年份来自生日而不是用户说过的事
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:
collection-question-pool.ts、method-followup.ts、user-copy.ts、Skill 10.0.23 - 用户现象:刚说完一两件,系统立刻按家人/学业/财务轮着问,题干还带着「2020 年前后」这类不是用户说的年份。
- 触发条件:开场写入第一件带年月经历后,
DATED_COLLECT_ORDER与COLLECT_QUESTION_YEAR_CUE(年龄段中点)生效。 - 根因:采集顺序按方法覆盖轮转,年份线索来自出生年+典型年龄段。这是上午写进 BUG-642 的口径,产品已撤回。
- 修复:收集池按信息价值排序:邀请「还有吗」→ 用户年份锚定追问 → 无年份通用补问。题干不得出现年龄段年份。邀请拒答不把整个
other列入禁问。 - 验证:
rectification-spoken-collect.test.ts、rectification-collection-question-pool.test.ts、agent-voice-copy-contract.test.ts。 - 防复发:
user-copy.ts不得再出现无用户年份的「年前后」;邀请在产出期间必须排第一。 - 相关记录:BUG-642、BUG-546、BUG-641、BUG-646
- 复发自:BUG-642(年份线索口径错误)
- 修复版本:
dd8f35f7
BUG-649 | 职业采集题答出的年月被抹成不计分备注
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-11
- 影响面:
applyOccupationCollectLedgerNorm、rectification-v9-toolsbatch/propose、职业采集焦点 - 用户现象:职业题被问到开始年份并答出年月后,助手只回「记下了」,训练门仍关,账本只有不计分的职业备注。
- 触发条件:职业采集焦点下回答带年月的工作开始(例如某年某月开始做某一行)。
- 根因:BUG-442 P0 把职业焦点下任何 career/occupation 条目一律改成
occupation_note且精度unknown、日期置空。occupation_note不计训练门。batch/propose 又用??把归一后的 null 日期回落成原日期,出现「unknown 却带日期」的账本行。 - 修复:无年月仍只写
occupation_note(覆盖判定仍不读正文、不用关键词、不依赖模型 domain)。带年月时另记一条domain=career主评分事件,kind 沿用模型给出的 career kind,否则career_entry。归一后的 null 不再用??回落。职业通用题不问年份;开始年份走 career 锚定题。 - 验证:
frontend/tests/rectification-occupation-dated-answer-20260911.test.ts、frontend/tests/rectification-replay-20260911.test.ts、frontend/tests/rectification-collection-question-pool.test.ts。 - 防复发:职业焦点下无日期仍不得把
occupation_note算进 3 件;带日期必须另有 scoreable career 行;源码合同禁止?? item.occurredFrom。 - 相关记录:BUG-442、BUG-356、BUG-389、BUG-646、BUG-647、BUG-648、BUG-650
- 复发自:BUG-442(职业覆盖归一过宽)
- 修复版本:待发布
BUG-650 | 工具轮结束时精确缺口句未并入本轮正文
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-11
- 影响面:
persistNextInterviewIfIdle、composeIdleGapIntoSpoken、agent-run证据轮出口 - 用户现象:训练门仍关、收集池已空时,工具轮结束只见「记下了」,没有精确缺口句,客户端像停住。
- 触发条件:证据轮有
turnId(因此不会走persistExhaustionGateTurn),且 idle 路径写出了精确缺口。 - 根因:BUG-596 禁止再开第二条门槛 turn。缺口只停在 idle 的
hostNarration,没有并入本轮p_assistant_message。 - 修复:证据轮在
trimSpokenTurnForInterview之后,若 idle 缺口含「现在记下的是 / 就能开始筛」,把缺口接到同一条复述后面。交付旁白不会被误接。 - 验证:
frontend/tests/rectification-replay-20260911.test.ts无日期职业变体;rectification-v9-agent.test.ts交付轮仍裁成三句。 - 防复发:工具轮出口的缺口必须出现在本轮正文;不得为缺口再开一条 turn。
- 相关记录:BUG-646、BUG-596、BUG-649
- 复发自:BUG-596(禁止第二条门槛 turn 后缺口没并入正文)
- 修复版本:待发布
BUG-651 | 带年月区分题池空必须交付区间,不得改问性格题
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-11
- 影响面:
decideRectification、discriminatorProbeIfFollowupCanAsk、buildMethodFollowupPlan - 用户现象:训练门已开、带年月选择题问完后,决策仍是
discriminate_candidates,界面没有结果卡。 - 触发条件:训练门开、
acceptance_allowed为真、带年月探针无可问,只剩 D9/D10varga_style或月宿边界题。 - 根因:
!separation.sufficient把无年月性格题当成下一道区分题;buildMethodFollowupPlan在带年月池空时走firstRenderableYearlessFollowup/ 性格卡。重设计单 S3「探针池空 → 交付」以带年月题为准,性格题不得挡在结果前面。 - 修复:probe 只认带年月探针;性格题 / 月宿一律
yearless_deferred,不再作为ask_candidate_discriminator。池空时按canAdopt落到offer_provisional_range/adopt_representative。带年月精筛卡(probe_year或 period 含年份)仍问。本单按让步顺序先不问性格题,可选「再答两道参考题」入口另开一单。 - 验证:
frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts;rectification-yearless-probe-downgrade-20260909.test.ts带年月池空不再出 D9。 - 防复发:不得把
varga_style/nakshatra_boundary加回带年月区分池(BUG-629);不得让性格题参与淘汰。 - 相关记录:BUG-629、BUG-442、BUG-627、BUG-652
- 复发自:BUG-629(性格题降权后仍被当成必答区分题)
- 修复版本:
66f63c76;门禁跟进f870d3d7
BUG-653 | 带年月题池空必须按剩余候选刷新探针,不得直接出卡
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-11
- 影响面:
refreshDiscriminatorProbes、persistNextInterviewIfIdle/AfterChoice、event_probes.py - 用户现象:六道带年月选择题答完后,范围仍是约 20 分钟、头名约 29%,系统却直接出三列区间卡。
- 触发条件:训练门已开、引擎一次性生成的带年月探针问完、TypeScript 侧只更新分数、未再拿剩余活跃候选去引擎生成探针。
- 根因:区分探针只在引擎跑账本时按初始簇生成一次。选择题答完后不刷新。
MAX_PROBES=8、每域每年一道、家人存在题被 0.85 先验整域丢弃,把还能切开剩余簇的题掐掉。 - 修复:池空且未收敛时
refreshDiscriminatorProbes用活跃候选column_times、全部已答键asked_probe_keys、refresh_probes=true调引擎,探针并入inference_state.probes,不改candidate_set_id/ 已答题。每个候选集最多刷新 2 次。剩余候选 ≤5 时刷新上限 12/4。带月份的家人dasha_boundary不再因先验丢弃,只用于排序。 - 验证:
frontend/tests/rectification-probe-pool-exhausted-20260911.test.tsT0/T1;tests/test_candidate_discriminator_contract.py刷新上限与家人月级边界。 - 防复发:刷新必须走现有
keepAnswers回放路径(BUG-587 / BUG-594);asked_probe_keys带全部已答键,同域同年去重(BUG-559)。性格题不得进刷新池(BUG-651)。 - 相关记录:BUG-651、BUG-654、BUG-655、BUG-629、BUG-559、BUG-587、BUG-594
- 复发自:BUG-651(池空即交付,未先按剩余候选再出题)
- 修复版本:待发布
BUG-654 | 刷新后仍无带年月题须定向补事,卡片标题为目前范围
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-11
- 影响面:
targetedCollectPool、decideRectification、区间卡RectificationRangeDelivery - 用户现象:带年月题问完后直接出卡,标题写成「这次给出的范围」,没有「还能再收窄」;家人 / 财务 / 迁居等能切开 05:00 换升的线从未被问。
- 触发条件:刷新后带年月池仍空,或用户尚未说「没有了」;交付文案把当前范围写成结束。
- 根因:S1 锚定追问在训练门开后停;S2 池空直接 S3。交付条件把「池空」当成结束。卡片标题用「这次给出的范围」,BUG-651 的「再补什么」句只在 persist 旁白、不在卡上。
- 修复:刷新后仍无带年月题则出定向补事口述题(剩余换升层映射到领域,≥2 个具体例子,不带推算年份)。答新事则重算回 S2;答「没有了」才交付。交付条件改为收敛,或刷新与定向补事都用尽,或用户主动停。卡片标题「目前范围」,卡下「还能再收窄」取定向补事首条。禁用「这次给出」「最终」。
- 验证:
frontend/tests/rectification-probe-pool-exhausted-20260911.test.tsT3/T4;rectification-collection-question-pool.test.ts定向补事;agent-voice-copy-contract.test.ts禁词。 - 防复发:
!separation.sufficient && !probe在refreshExhausted && targetedCollectExhausted之前不得completeWithRange/finish。不得静默空载体(BUG-652)。 - 2026-09-29 产品修订:见 TASK-rectification-futile-collect-stop-20260929 D1——训练门开后定向补事关闭,
targetedCollectExhausted门开后恒为真;「刷新先于交付」不变(BUG-1084)。 - 相关记录:BUG-653、BUG-651、BUG-652、BUG-655、BUG-629
- 复发自:BUG-651(池空即交付,未做定向补事)
- 修复版本:待发布
BUG-652 | 答题事务有决策无载体时必须回落到交付或缺口句
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-11
- 影响面:
persistNextInterviewIfIdle、persistNextInterviewAfterChoice、persistServerOwnedFocus、时间线rectificationReadonlyRangeCopy - 用户现象:六道带年月选择题答完后,助手只回「已记录,范围没变…」,时间线写「选择题已问完,再补带年月的经历才会变」,界面「没有拿到下一个问题。」没有结果卡、没有采用入口、没有缺口句。
- 触发条件:训练门已开、引擎已允许交付,决策仍是
ask_candidate_discriminator,但下一张区分卡没落下(persist 返回 skipped / invalid_choice_schema / 空正文)。 - 根因:
persistNextInterviewIfIdle对ask_candidate_discriminator/ask_holdout_validation豁免了persistExhaustionCollect。persistNextInterviewAfterChoice在焦点不可渲染时用spokenFollowupForUser(followup) ?? fallback,空串不触发兜底。时间线在没有结果卡时仍写「才会变」。 - 修复:撤回 IfIdle 豁免;答题事务在无可渲染焦点且无正文时同一事务回落到
persistExhaustionCollect;persist 非 created/already_open 时console.warnrectification_discriminator_persist_skipped;时间线有卡写「选择题已问完,下面是当前范围」,门关写「再说一件带年月的事就能继续」,其余「还在核对」。 - 验证:
frontend/tests/rectification-probe-pool-exhausted-20260911.test.tsT3;rectification-surface-state.test.ts时间线合同;agent-voice-copy-contract.test.ts禁词「才会变」。 - 防复发:决策有动作就必须有载体;不得靠客户端「接着问」补救。
- 相关记录:BUG-651、BUG-627、BUG-442
- 复发自:BUG-627(
ensureNonTerminalTurnExit只在点「接着问」时触发,不在答题事务里) - 修复版本:
66f63c76
BUG-655 | 刷新无新题不得写推理账本;并入探针须与已答同名
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-12
- 影响面:
refreshDatedDiscriminatorPoolIfNeeded、persistV9InferenceState、GETdecideFromDossier与persistServerOwnedFocus - 用户现象:带年月题问完后刷新,GET 已选出下一道探针,但持久化层拒绝,界面再次停在没有下一问。
- 触发条件:训练门已开、带年月池空、刷新调用引擎后没有可并入的新题,或并入探针 id 与已答 id 命名不一致,或写入时候选被清空 / 候选集被改掉。
- 根因:刷新在引擎没有新题时仍写
reason=supersede的推理记录;GET 侧可能选中未映射进inference_state.probes的引擎探针,expectedAnswerSchemaFor盖不上probe_id后 persist 返回invalid_choice_schema/skipped。 - 修复:只在引擎映射出可问新题时写库;写入前断言候选非空且
candidate_set_id不变,候选沿用刷新前集合;并入探针 id 与已答同一规则(probe:<semantic_key>或带 split hash);刷新只把可问探针写进inference_state.probes和 receipt,GET 选中的 key 必须能 stamp 上probe_id。 - 验证:
frontend/tests/rectification-probe-pool-exhausted-20260911.test.tsT0 打印最后一行推理记录与 GET 探针 key / persist 状态;有新题时supersede,空刷新改由 BUG-658 写尝试记录。 - 防复发:新探针写库必须同时满足新题、候选非空、候选集不变、探针 id 与已答同名。空刷新不得写
supersede。不得靠客户端「接着问」补救(BUG-652)。 - 相关记录:BUG-653、BUG-654、BUG-652、BUG-587、BUG-594、BUG-656、BUG-657、BUG-658
- 复发自:BUG-653(刷新总会写库,未校验新题与候选集)
- 修复版本:
512be9b7
BUG-656 | 采集焦点 kind 必须映射到表约束枚举
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-12
- 影响面:
persistCollectFocus、persistSpokenChoiceFallback、agentic_rectification_conversation_focuses.target_kind - 用户现象:带年月题问完后应出现定向补事口述题,但焦点写不进库,界面停在没有下一问。
- 触发条件:下一问是定向 / 锚定 / 通用采集,
kind_hint为targeted:<domain>、anchor:…或generic:…。 - 根因:
targetKind直接写入kind_hint,违反target_kindCHECK;插入失败后 persist 返回skipped,定向题从未成为活动焦点。 - 修复:写库前经
collectFocusTargetKind映射到 CHECK 枚举(产品别名relationship_change→relationship_start、education_milestone→education_start);原kind_hint写入expected_answer_schema.collect_kind;读取侧先读collect_kind再回退旧字段。不放宽 CHECK、不加迁移。 - 验证:
frontend/tests/rectification-server-focus.test.ts用真实 CHECK 列表断言三类 followup 的targetKind合法且status=created,invite_more写target_kind=null+collect_kind;rectification-probe-pool-exhausted-20260911.test.ts第六题后定向题成为活动焦点。门禁跟进:家人拒答后的邀请 persist 与 idledecideFromDossier源码锁仍通过。 - 防复发:采集焦点
target_kind必须落在 CHECK 列表内或为 null;kind_hint原值只进 schema,不进列。invite_more等非枚举 hint 不得写进target_kind。 - 相关记录:BUG-648、BUG-442、BUG-658
- 复发自:BUG-648(采集题号可重试,但 kind 仍未映射到表约束)
- 修复版本:待发布
BUG-657 | 引擎刷新必须按剩余候选生成探针
- 状态:resolved
- 首次发现:2026-09-11
- 最近更新:2026-09-12
- 影响面:
scripts/rectification/refinement_packet.py、event_probes.pyrefresh and remaining分支 - 用户现象:第六题后刷新仍按整段约 31 分钟网格出题,剩余几分钟上没有新边界时永远
no_new_probes。 - 触发条件:
refresh_probes=true且请求带非空column_times(剩余活跃候选)。 - 根因:
build_refinement_packet把grid_times交给探针生成,column_times只喂三列对照;refresh and remaining看不到剩余分钟。 - 修复:刷新且
column_times非空时,用它们作为_discriminating_event_probe_lists的candidate_times;回执写refresh_result=new_probes|no_new_probes。 - 验证:
tests/test_candidate_discriminator_contract.py「31 格 + column_times=5」:探针candidate_ids只含这 5 个;无边界则refresh_result=no_new_probes。 - 防复发:刷新探针生成不得默认整网格;
column_times非空时必须作为剩余候选。 - 相关记录:BUG-653、BUG-658
- 复发自:BUG-653(刷新调用了引擎,但探针仍按全网格算)
- 修复版本:待发布
BUG-658 | 刷新尝试必须落库;等待收窄按已问过判定;定向题落库失败同一事务出卡
- 状态:resolved
- 首次发现:2026-09-12
- 最近更新:2026-09-12
- 影响面:
refreshDatedDiscriminatorPoolIfNeeded、narrowingExhaustion、persistExhaustionCollect、interviewCollectWaiting - 用户现象:引擎没有新题时界面永久「等待收窄」,每次答题再调一次引擎(约 20s),并出现「没有拿到下一个问题」。
- 触发条件:训练门已开、带年月池空、刷新无新题;或定向补事焦点因 CHECK/落库失败。
- 根因:512be9b7 空刷新什么都不写,
refresh_count恒 0;GET 不调刷新,refreshExhausted永假,waitToNarrowCapability无题无卡。shouldRefreshDatedPool只看refresh_count,同一已答数会反复打引擎。定向题 persistskipped时仍停留在无焦点的口述。 - 修复:空刷新把
inference_state.refresh_attempts[]以reason=already_answered写入(幂等键refresh_attempt:<set>:<count>);有新题仍走supersede并附带尝试记录。refreshExhausted由同一候选集、同一已答数下已有尝试记录(或剩余换升层为空)判定;shouldRefreshDatedPool同源,至多一次引擎刷新。定向题 persistskipped时同一事务把collect:targeted:<domain>记为 skipped 并出「目前范围」。客户端collect_evidence且无 stopReason、无焦点无卡时走collect_waiting。 - 验证:
frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts空刷新写already_answered、第二次不调引擎、GET 进定向补事;定向 persist 失败同一事务出「目前范围」、不得出现「没有拿到下一个问题」;rectification-surface-state.test.tscollect_waiting无insufficient_*也可成立。 - 防复发:空刷新不得只打日志;GET 决策不得依赖只存在于内存的刷新结果。新探针仍禁止空
supersede(BUG-655)。 - 相关记录:BUG-655、BUG-651、BUG-652、BUG-656、BUG-657
- 复发自:BUG-655(堵住空
supersede后未留下「已刷新」事实) - 修复版本:待发布
BUG-659 | 记完经历后 set-focus 连续失败且原始错误无处可查
- 状态:resolved
- 首次发现:2026-09-12
- 最近更新:2026-09-12
- 影响面:
rectification-set-focus、safeToolErrorCode、工具失败回执、mapStreamChunkToActivity - 用户现象:记下两件学业后,「设置对话焦点」连续失败约六次,回执只有
tool_failed,本轮多等约 20 秒后走宿主兜底。用户没有被卡住;GET 快照里下一问已经是「还有吗」。 - 触发条件:batch 工具记完证据后已把同一 questionId 写成活动焦点,模型再调 set-focus;或把
kind_hint(如invite_more)抄进targetKind;或工具内抛出未列入已知列表的码。 - 根因:失败回执只写
safeErrorCode,没有脱敏原文、没有rectification_tool_failed日志,无法从库或容器判断六次失败的真实原因。invalid_case_id/invalid_domain/invalid_event_kind/invalid_focus不在已知列表,一律显示为tool_failed。同一 questionId 已由 batch 落下时,set-focus 仍走 RPC 身份比较,schema 键不同会冲突;模型传入的kind_hint还会被 Zod 在 execute 前拒绝。 - 修复:失败回执写入脱敏
engine_message(≤120 字,UUID/邮箱打码),并console.warnrectification_tool_failed。上述码进入已知列表与活动码白名单。同一 questionId 已是活动焦点时幂等返回已有焦点(可更新 spoken_prompt;RPC 冲突也不得抛给模型重试)。targetKind/targetDomainschema 放宽为字符串;kind_hint忽略,新焦点仍写target_kind=null。非法 domain 仍在 execute 抛invalid_domain。 - 验证:
frontend/tests/rectification-set-focus-tool-failed-20260912.test.ts(同 id 即使 RPC 冲突也idempotent=true、无失败回执;invite_more不拒绝;三类工具内抛错回执可辨,未知仍tool_failed);rectification-v9-status-security.test.ts;rectification-v9-stream.test.ts失败活动带码、英文重试旁白不得进正文。 - 防复发:工具自己抛的码必须进
safeToolErrorCode已知列表;失败回执必须带脱敏原文。已落下的同一 questionId 不得再让模型重试。不得把kind_hint写进target_kind(BUG-656)。 - 相关记录:BUG-442、BUG-650、BUG-656、BUG-660
- 复发自:无
- 修复版本:待发布
BUG-660 | 宿主兜底「记下了」把日期写两遍
- 状态:resolved
- 首次发现:2026-09-12
- 最近更新:2026-09-12
- 影响面:
host-fallback.tsrecapLine、证据轮answer_origin=host_fallback - 用户现象:工具轮失败后宿主兜底正文写成「记下了:2016-09 2016年9月上大学…」,日期重复。
- 触发条件:batch 回执同时有
display_date_label(如2016-09)和已含年月的event_phrase(如「2016年9月上大学」),且模型没有写出正文。 - 根因:
recapLine把标签和短语直接空格拼接,不检查短语是否已含年份或以标签开头。 - 修复:短语匹配
\d{4}年或以display_date_label开头时只用短语;短语为空时仍用标签。 - 验证:
frontend/tests/rectification-host-fallback.test.ts夹具「2016-09 / 2016年9月上大学」→「记下了:2016年9月上大学(本科入学)、2020年6月毕业。」;短语已以标签开头时不重复前缀。 - 防复发:host fallback recap 不得把已含年月的短语再拼一份标签。
- 相关记录:BUG-633、BUG-650、BUG-659
- 复发自:无
- 修复版本:待发布
BUG-661 | 定向补事必须逐条点选,不得一道口述堆所有剩余线
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
targetedCollectPool、targetedCollectFollowup、answer-choice.ts存在题分支、persistExhaustionCollect - 用户现象:六道带年月题答完、引擎刷新无新题后,落下一条口述题把结婚、收入、搬家、添丁全堆在一句里,没有可点的选项。
- 触发条件:训练门已开、带年月池空、刷新
no_new_probes、仍有未覆盖的剩余换升层。 - 根因:
targetedCollectFollowup把剩余层合成一道口述题;「没有」被当成整池结束,而不是只关掉当前那条线。 - 修复:按剩余层一次出一张存在题卡(有过这件事 / 没有发生过 / 记不太清楚 / 这条先跳过,
scoring:false)。A 再问「大概哪年几月?」;B 该线 declined;C/D skipped。年月答完走既有 batch 写证据并重算。所有剩余线问完才出「目前范围」。 - 验证:
frontend/tests/rectification-targeted-collect-cards-20260913.test.ts;rectification-probe-pool-exhausted-20260911.test.ts第六题后choiceReady且题干为「结过婚或订过婚吗?」;四条都 B 才出范围卡。 - 防复发:定向补事存在题必须是
choice_frame且scoring:false;不得把多条剩余线合成一道口述题。性格题不得因此回到计分池(BUG-629)。 - 相关记录:BUG-654、BUG-656、BUG-662、BUG-663
- 复发自:BUG-654(交付前定向补事做成了一道开放口述)
- 修复版本:待发布
BUG-662 | 定向补事与范围提示不得写范围两端钟点
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
remainingCandidatesLine、rangeNarrowHint、agent-voice-copy-contract - 用户现象:提示写成「能把 04:51 和 05:06 分开」,读起来像在比较范围两端,而不是还剩几个候选。
- 触发条件:定向补事或「目前范围」卡下提示需要说明还能切开剩余候选的线。
- 根因:
rangeNarrowHint用 split 两端钟点当主语。 - 修复:改写为「现在还剩 HH:MM–HH:MM 里 N 个候选,能把它们分开的是这几条线:…」;保留「还能再收窄:如果记得…」。合同加禁词「能把 HH:MM 和 HH:MM 分开」。
- 验证:
frontend/tests/agent-voice-copy-contract.test.ts;rectification-targeted-collect-cards-20260913.test.ts剩余候选句。 - 防复发:用户可见文案不得出现「能把两端钟点分开」句式;剩余候选句不得当作存在题题干。
- 相关记录:BUG-654、BUG-661
- 复发自:BUG-654(定向补事举例时带上了范围端点)
- 修复版本:待发布
BUG-663 | 「目前范围」卡下可点「再答两道参考题微调排序」
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
rectification-range-delivery.tsx、tieBreakPersonalityFollowup、POST /api/rectification/cases/[caseId]/tie-break - 用户现象:带年月题和定向补事问完后,用户没有入口去答 D9/D10 参考题微调排序;若自动出性格题又会挡住结果。
- 触发条件:已交付「目前范围」卡,候选仍分不开,用户想微调头名排序。
- 根因:BUG-629 把性格题移出计分池后,没有产品入口可选择再答;自动出牌会挡住结果卡。
- 修复:卡下可选按钮「再答两道参考题微调排序」。点了才走
tie_break落 D9/D10varga_style(±1、不淘汰、不改can_adopt)。不点不出。 - 验证:
frontend/tests/rectification-targeted-collect-cards-20260913.test.ts不点不出;入口文案进listUserVisibleCopy()。 - 防复发:性格题不得在定向补事之前或未点入口时成为下一问;
SCORE_DELTA与淘汰阈值不得被这条路径改掉。 - 相关记录:BUG-629、BUG-651、BUG-661、BUG-666、BUG-667
- 复发自:BUG-629(性格题降权后没有可选入口)
- 修复版本:待发布
BUG-664 | 刷新阶段同年换运间隔可收到 30 天,首轮仍是 45 天
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
scripts/rectification/event_probes.py_boundary_windows/_union_boundary_dates/discriminating_event_probes - 用户现象:六道带年月题答完后,刷新经常立刻
no_new_probes,即使剩余候选的同年换运只差三十多天。 - 触发条件:训练门已开、带年月池空、
refresh_probes=true、剩余候选上存在 30–44 天的同年边界。 - 根因:生产
MIN_BOUNDARY_DAYS=45对首轮和刷新共用。离线 20 例测量里,只把刷新阈值降到 30 平均多 1.0 个独立划分、头名不降;首轮保持 45。 - 修复:新增
REFRESH_MIN_BOUNDARY_DAYS=30。仅request.refresh_probes is True时传给_union_boundary_dates;首轮调用签名与阈值不变。不改EXISTENCE_NEARBY_YEARS、R1/R2、SCORE_DELTA。 - 验证:
tests/test_rectification_refresh_r3_r4.py:40 天同年窗首轮不出、刷新出dasha_boundary。既有tests/test_rectification_event_probes.py、tests/test_candidate_discriminator_contract.py首轮断言不放宽。 - 防复发:默认
MIN_BOUNDARY_DAYS必须仍是 45;不得把研究脚本接到 API;刷新仍走 distinguish 合同。 - 相关记录:BUG-653、BUG-657、BUG-665
- 复发自:BUG-653(刷新已按剩余候选出题,但边界阈值与首轮相同)
- 修复版本:待发布
BUG-665 | 刷新阶段并入 pratyantar 与 D9/D10 上升 Narayana 边界
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
scripts/rectification/event_probes.py_vim_start_dates/_union_boundary_dates - 用户现象:六道带年月题答完后,本命上升相同、D9/D10 或第三级换运已不同时,刷新仍无新的带年月题。
- 触发条件:
refresh_probes=true,剩余候选 Vimshottari pratyantar 或 D9/D10 上升 Narayana 有可分辨边界。 - 根因:首轮并集只用 Vimshottari MD/AD 与本命上升 Narayana。
_vim_start_dates(..., include_pratyantar=)已存在但刷新默认仍为 false。离线测量 R4 单独 +1.084 独立划分,头名不降。 - 修复:仅刷新阶段
include_pratyantar=True,并对剩余候选 D9/D10 上升再算 Narayana 边界并入并集。首轮不传这些参数,_vim_start_dates调用保持原签名以免旧 mock 失败。本命上升 Narayana 与 MD/AD 不变。 - 验证:
tests/test_rectification_refresh_r3_r4.py:pratyantar 与 D9 上升不同时刷新出新年月、首轮不出。研究脚本scripts/research/probe_supply_after_six.py仍离线。 - 防复发:不得对首轮打开 pratyantar 或分盘 Narayana;不得全局改
MIN_BOUNDARY_DAYS。刷新 0 题时仍走定向补事(BUG-654 / BUG-661),不得报「没有拿到下一个问题」。 - 相关记录:BUG-653、BUG-654、BUG-661、BUG-664
- 复发自:BUG-653(刷新只复用首轮边界层)
- 修复版本:待发布
BUG-666 | 参考题入口在两道答完后仍显示,再点报没有可答题
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
rangeDeliveryForSnapshot、GETrange_delivery.tie_break_available、POST /api/rectification/cases/[caseId]/tie-break - 用户现象:「目前范围」卡下「再答两道参考题微调排序」两道都答完后按钮还在;再点出现红字「现在没有可答的参考题」。
- 触发条件:候选 D9/D10 仍不同,但 D9/D10
varga_style都已问过;或从未有可渲染的参考题。 - 根因:入口只看
window_scan.d9_candidates_differ / d10_candidates_differ。路由用tieBreakPersonalityFollowup跳过已问键,两道答完返回空 → 409tie_break_unavailable。前端把 409 写进setError。 - 修复:GET 与点击路径共用「还有未问的 D9/D10
varga_style且未采用」。没有剩余题时不画按钮;若仍点到空池,只重拉快照,不把 409 文案写到界面。 - 验证:
frontend/tests/rectification-tie-break-entry-20260913.test.ts。 - 防复发:不得只用 window_scan 决定入口。planner 不得打进客户端包。性格题仍不得自动成为下一问(BUG-663)。
- 相关记录:BUG-663、BUG-667、BUG-629
- 复发自:BUG-663
- 修复版本:待发布
BUG-667 | 点一次参考题入口只出一道,不会自动接第二道
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
requestTieBreakPersonality、persistNextInterviewAfterChoice、expectedAnswerSchemaFor - 用户现象:按钮写「再答两道」,点一下只出 D9;答完回到范围卡,要再点一次才出 D10。
- 触发条件:用户从范围卡点入口,答完第一道
varga_style。 - 根因:入口只持久化当前下一问。答完后
buildMethodFollowupPlan无tieBreakRequested,且varga_style不算剩余区分题,can_adopt仍为真时跳过下一问持久化。 - 修复:入口落下的焦点 schema 带
tie_break_round: true。答完该轮焦点时继续tieBreakPersonalityFollowup落第二道;没有第二道或两道都问完则结束该轮,回到范围卡。自动接第二道只在这一轮内生效。 - 验证:
frontend/tests/rectification-tie-break-entry-20260913.test.ts:未问时 D9、问过 D9 后 D10、两道都问过则空;schema 带 round 标记;计分仍tie_break、不淘汰。 - 防复发:不得把任意性格题答完都当成参考题轮次。
can_adopt与淘汰阈值不得因参考题改变。 - 相关记录:BUG-666、BUG-663、BUG-629
- 复发自:BUG-663
- 修复版本:待发布
BUG-668 | 定向补事死辅助函数、Skill 三选项与引擎 mock 签名分叉
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
collection-question-pool.ts、skills/jyotish-birth-time-rectification、scripts/rectification/event_probes.py - 用户现象:卡上是四个选项(含「这条先跳过」),Skill 仍写三个。生产代码为迁就测试替身把
_vim_start_dates/_boundary_windows拆成两套调用。 - 触发条件:读 Skill 或跑刷新边界;
isTargetedCollectDeclined/isTargetedCollectYearFollowup无src/调用方。 - 根因:BUG-661 曾用「整池关闭」脚枪,辅助函数留下;Skill 10.0.25 未写入第四选项;刷新参数用空 kwargs 避开旧 mock。
- 修复:删除无调用方的两个导出。Skill 10.0.25 冻结,现行 10.0.26 写「有 / 没有 / 记不清 / 这条先跳过」。
_union_boundary_dates一律把include_pratyantar与min_days传给生产函数,不再为 mock 分叉。 - 验证:源切片确认两个导出已删;Skill 合同锁 10.0.26 文案;
tests/test_rectification_event_probes.py与tests/test_rectification_refresh_r3_r4.py。 - 防复发:Skill 选项数必须与卡一致;生产调用不得为测试替身分叉。10.0.25 仍可 exact-resolve。
- 相关记录:BUG-661、BUG-621、BUG-665
- 复发自:—
- 修复版本:待发布
BUG-669 | 定向补事快照投影拿不到 choice_card,刷新后卡点不动
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
projectRectificationChoiceCard、GET/api/rectification/cases/[caseId]choice_card、内嵌RectificationChoiceCard - 用户现象:六道带年月题后出现定向补事四点选;第一次能答,刷新或再拉快照后卡看得见、选项是灰的。
- 触发条件:活动焦点为
collect:targeted:<domain>(含 persist 撞号后的:next),schema 为服务端四点选且targeted_collect: true。 - 根因:
buildMethodFollowupPlan承接collect_method_evidence时不重建choice_frame。projectRectificationChoiceCard只把persistedVerifyCard()留给reverse_verify/out_of_sample_check,无 frame 就返回 null。projectCurrentQuestion仍给出kind: "choice",前端liveQuestion要求 GET 卡focus_id一致,于是disabled={!liveQuestion}。:next还会被「questionId 与 frame 全等」挡掉。 - 修复:活动焦点 schema 满足
targeted_collect === true且能解析出四点选时,直接按持久化文案投影choice_card(question_id/focus_id用焦点的、scoring: false、stop_label「这题跳过」),不依赖计划层 frame,也不做 questionId 全等比较。 - 验证:
frontend/tests/rectification-targeted-card-live-20260913.test.ts;:next同样出卡。 - 防复发:定向存在题的 GET 卡必须能从已持久化 schema 投影;不得只用计划层重建 frame 当唯一身份。区分题
semantic_key/candidate_split_hash全等判定不得放宽(BUG-559 / BUG-581)。 - 相关记录:BUG-661、BUG-654、BUG-591、BUG-510、BUG-498
- 复发自:BUG-661(采集焦点 + 点选卡是新组合,落入 BUG-510 只修了核对题的洞)
- 修复版本:待发布
BUG-670 | 承接焦点分支让模型把定向题改写成口述题
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
buildMethodFollowupPlan承接当前焦点分支、user_prompt_hint - 用户现象:定向卡出现前先冒出一道 UI 不一样的口述题,回答后才变成点选卡。
- 触发条件:活动焦点是定向存在题,计划层
method_id: "active_focus"且choice_frame为空,提示词要求模型用spokenPrompt自己写题干。 - 根因:承接分支只为
reverse_verify/out_of_sample_check/distinguish_candidates重建点选。定向补事是史上第一个「采集焦点 + 点选卡」。 - 修复:识别
targeted_collectschema 时用buildTargetedCollectExistenceFrame加持久化文案重建choice_frame;user_prompt_hint改为「照发服务端卡片,不要自己写题干。」 - 验证:
frontend/tests/rectification-targeted-card-live-20260913.test.ts活动焦点为定向存在题时next_followup.choice_frame非空且question_id等于焦点题号。 - 防复发:定向存在题的题干与选项仍是服务端自有;模型不得改写。不得因此放宽区分题身份校验。
- 相关记录:BUG-669、BUG-661、BUG-559、BUG-581
- 复发自:BUG-661
- 修复版本:待发布
BUG-671 | 选择题无卡时不得停在采集等待态
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
rectification-agentic-chat问题缺口、interviewChoiceCardUnavailable、repair-exit - 用户现象:卡点不动时,时间轴写「再说一件带年月的事就能继续」,没有修复入口。刷新页面后选项依旧是灰的。
- 触发条件:
current_question.kind === "choice"且 GETchoice_card为空。 - 根因:有持久化选择题时走
persisted_question/ 采集等待,不进unavailable。刷新只重取同一份缺卡快照,救不回来。未答的死卡还会用消息里的选项画成 disabled。 - 修复:选择题缺卡时不记为采集等待、不记为已持久化可见题,直接进现有
unavailable+repair-exit(「接着问」);未答的死卡不再画成 disabled 点选。 - 验证:
frontend/tests/rectification-targeted-card-live-20260913.test.ts;表面合同锁deadChoice、unansweredDeadChoice与 repair-exit 路径。 - 防复发:
kind=choice且无choice_card不得静默停在「再说一件带年月的事」,也不得画看不见的可点卡。BUG-669 修好后这条是兜底。 - 相关记录:BUG-669、BUG-627、BUG-652、BUG-510
- 复发自:BUG-669
- 修复版本:待发布
BUG-672 | 健康与职业别名在十处比较里各走各的,拒答或已报过仍再问
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-13
- 影响面:
canonicalCollectDomain/canonical_domain、holdoutFollowupFor、remainingReverseVerifyProbes、remainingConflictProbes、probeYearAlreadyCovered、oos_blind_prompts、_event_years、volunteeredDomainsFromEvidence、declinedDomains - 用户现象:跳过或报过健康后,核对题、区分题或盘外提示仍问同一条健康线;职业题落库成「其他」后,事业还没覆盖时会再问职业。
- 触发条件:账本或焦点是
health,计划/引擎探针是health_pressure;或职业焦点target_domain=other且题号collect:occupation:。 - 根因:三套词汇并存。BUG-590 只归并 holdout occupied,BUG-626 只补 holdout declined。其余比较仍用字面相等。任务书写 BUG-628,该号已被口述采集按钮占用。
- 修复:TS/Python 各一个归并函数,比较两侧都走它。DB
target_domain与persistableFocusDomain不改。源码合同禁止method-followup.ts再写"health"比较。 - 验证:
frontend/tests/rectification-domain-alias-audit-20260913.test.ts;tests/test_rectification_event_probes.pyDomainAliasTests。 - 防复发:健康/职业比较必须经归并函数。不得在
method-followup.ts新增"health"字面量比较。不得改 DB 约束来迁就别名。 - 相关记录:BUG-590、BUG-626、BUG-586、BUG-641
- 复发自:BUG-590 决策 4(occupied 已归并,其余站点未归并)
- 修复版本:待发布
BUG-673 | 定向补事存在题被存成口述焦点,裸题顶掉卡片
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:
serverOwnedExpectedAnswerSchema、persistServerOwnedFocusCore、persistSkippedCollectFocus、rectification-set-focus、buildMethodFollowupPlan承接焦点、projectCurrentQuestion、interviewChoiceCardUnavailable - 用户现象:答完带年月题后出现一行没有头像、没有 A/B/C/D 的粗体裸题(如「家里添过丁或长辈住过院吗?」),无法点选;本轮计划的 D9 关系题消失。
- 触发条件:定向存在题以采集型 schema(
collect: true、collect_kind: targeted:*)落进活动焦点;choice.applied的nextInterviewPersisted=true让前端不再追一轮。 - 根因:写入侧在
expectedAnswerSchemaFor没产出.choice时把采集意图降成口述 schema;复原侧只认targeted_collect === true章,拦不住采集型 schema。BUG-670 的识别条件覆盖不到这条不经模型的路径。 - 修复:定向存在题禁止降级成采集型 schema,重建不出 frame 就不写焦点。承接焦点与 GET 投影按题号前缀识别口述定向存在题并重建四点选。焦点落库失败时
persistNextInterviewAfterChoice显式persisted: false,题干进本轮旁白。persistSkippedCollectFocusresolve 失败重试一次,不得留下status=active。前端把口述形态的定向存在题当缺卡,走 repair-exit。年份阶段collect:targeted:<domain>:year仍是口述。 - 验证:
frontend/tests/rectification-targeted-spoken-focus-recovery-20260913.test.ts;rectification-targeted-card-live-20260913.test.ts、rectification-surface-state.test.ts补口述定向存在题。 - 防复发:定向补事存在题的焦点 schema 只允许点选形态;识别定向存在题以题号前缀为准,不得只认
targeted_collect字段。不得把persisted_question块画成卡片来绕过数据层。 - 相关记录:BUG-670、BUG-669、BUG-671、BUG-661
- 复发自:BUG-670
- 修复版本:待发布
BUG-674 | 探针耗尽后判别题被念进正文,同一题反复问且答了不算数
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
persistNextInterviewAfterChoice、expectedAnswerSchemaFor/stampChoiceSchemaWithProbe、persistExhaustionCollect - 用户现象:答完带年月题后,正文反复出现同一句判别题(无 A/B/C/D)。用户打字回答「没有」只得到「记下了,这方面先跳过」,同一句再出现一次。
- 触发条件:
prospective_probes为空,精度阶段仍给出带choice_frame的判别题;stampChoiceSchemaWithProbe盖不上probe_id,persist 返回invalid_choice_schema。 - 根因:上一单把「焦点没写成功」当成「题还在、只是没进消息」,把卡片题干拼进旁白。探针耗尽时这道题根本不该再问,也没有焦点可答。
- 修复:判别题(
intent === distinguish_candidates,或带choice_frame的非采集题)落不了库时改走persistExhaustionCollect,不得把题干写进正文。采集题仍可进正文,但与上一条助手消息相同的题干会去重并转耗尽分支。 - 验证:
frontend/tests/rectification-unstampable-probe-20260914.test.ts。 - 防复发:判别题落不了库时不得把题干写进正文;探针耗尽必须转定向补事卡。
- 相关记录:BUG-673、BUG-652、BUG-661
- 复发自:BUG-673(D3 把未落库题干拼进旁白)
- 修复版本:待发布
BUG-675 | 快照已有 choice_card,界面仍画一行裸题
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
attachQuestionsToTurns、persisted_question渲染、persistedQuestionSurface - 用户现象:
目前范围下面一行裸题(如「结过婚或订过婚吗?」),无头像、无选项、也没有「接着问」。 - 触发条件:
current_question.kind === "choice"且 GETchoice_card非空,但焦点没有asked_turn_id,卡片无法挂到消息上;persisted_question只画prompt。 - 根因:
persisted_question从设计起只服务口述题。BUG-673 把定向题修成点选后,这个纯文本块成了新的裸题来源。 - 修复:无
asked_turn_id的活动选择题挂到最后一条助手消息,走既有消息内卡片。persisted_question在有卡时渲染同一套RectificationChoiceCard,不再只画一行字。 - 验证:
frontend/tests/rectification-unstampable-probe-20260914.test.ts;rectification-surface-state.test.ts。 - 防复发:
current_question.kind === "choice"且有choice_card时必须渲染可点卡,不得只画题干。不得新增第二套卡片组件。 - 相关记录:BUG-673、BUG-669、BUG-671
- 复发自:BUG-673(数据形态修好后渲染仍走口述兜底)
- 修复版本:待发布
BUG-676 | 代表时间取到被淘汰候选
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
evaluateCandidateSeparation、rankActive、decision receiptrepresentative_time/last_inference_round、slimDecisionReceipt - 用户现象:同一轮 receipt 里引擎
representative_time落在eliminated_ids中(例如 05:14),界面范围是 04:48–05:07,也不含该分钟。 - 触发条件:本轮有淘汰名单,引擎代表时间与前端推理第一名 / 范围不一致。
- 根因:引擎与前端是两套代表时间,未对账。Python 引擎按自身事件拟合排整张网格,不吃前端累积问答,把 05:14 写进结果行;
overlayPublicDecision在fromInference时用推理值覆盖,界面从未显示过 05:14。真实副作用是case-receipt-projection把latestResult.representativeTime(引擎值)算进活跃时间集合,已被淘汰分钟的house_tables_by_time仍送给模型。 - 修复:活跃时间集合去掉引擎
representativeTime,只保留推理活跃候选、inference.representative_time、用户已采用的selectedTime。不改排序。上一轮的不变量告警rectification_representative_time_inconsistent保留。 - 验证:
frontend/tests/rectification-spoken-orphan-20260914.test.ts(淘汰分钟不进house_tables_by_time;selectedTime仍保留);告警路径仍由rectification-unstampable-probe-20260914.test.ts覆盖。 - 防复发:引擎代表时间不得进入模型上下文的活跃宫位表,也不得显示给用户,不得反过来覆盖前端推理值。
- 相关记录:BUG-442、BUG-678
- 复发自:无
- 修复版本:待发布
BUG-677 | staging quality gate 在残留 .venv 上被 Python 3.14 mkdir 打断
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:Gitea
Independent Staging Quality Gatevalidate「Install dependencies」、publish(因needs: validate被 skip) - 用户现象:推送
69ffa07d后门禁 run2594约 20 秒失败;测试、lint、build 未执行,publish/deploy skipped。 - 触发条件:
runs-on: xiaoxin的 act_runner hostexecutor 复用 job 目录且已有 gitignored.venv/;runner 报告Python 3.14.4。 - 根因:
python3 -m venv .venv对已存在目录执行mkdir,Python 3.14 抛出FileExistsError: [Errno 17] File exists: '.../hostexecutor/.venv'。git status --porcelain --untracked-files=all仍为空,因为忽略目录不出现在 porcelain 里。不是 pip、npm 或校正测试失败。 - 修复:安装步骤先
rm -rf -- .venv,再python3 -m venv --clear .venv。不改测试、lint、build、exact-SHA 与镜像发布语义。 - 验证:
frontend/tests/staging-backend-workflows.test.ts锁定清除后再--clear;Gitea gate 对修复 SHA 复跑。 - 防复发:质量门创建
.venv必须允许 hostexecutor 残留目录。不得把 Python 3.14FileExistsError当成业务测试失败。 - 相关记录:BUG-149、BUG-364
- 复发自:无
- 修复版本:待发布
BUG-678 | 口述题没有 askedTurnId 时仍是无头像裸行
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
attachQuestionsToTurns、focusSpokenPrompt、rectificationQuestionGapState/persisted_question - 用户现象:定向补事答「有过这件事」之后,助手气泡只有「记下了。」,时间轴「目前范围」下面一行没有头像的裸题「大概哪年几月?」。
- 触发条件:年份追问焦点
collect:targeted:<domain>:year、kind = collect_spoken、askedTurnId = null、status = active;上一轮挂回规则只覆盖选择题。 - 根因:BUG-675 把
attachQuestionsToTurns的 hanging 限定成turnQuestionKind(focus) === "choice"。口述题没有卡可画,但同样需要头像与消息位置;短题干「大概哪年几月?」只有 7 字,focusSpokenPrompt的 8 字下限会让挂回后的turnQuestionFromFocus仍返回空。 - 修复:无
askedTurnId的活动焦点一律可挂到最后一条尚无题的助手消息;采集 schema 允许短于 8 字的题干。persisted_question纯文本块只留给一条助手消息都没有的兜底。年份追问仍是口述,不做点选卡。 - 验证:
frontend/tests/rectification-spoken-orphan-20260914.test.ts;rectification-collect-prompt.test.ts补口述挂回。 - 防复发:挂回规则不得再按
choice排除口述题。不得把年份追问改成点选卡来绕过渲染。不得新增第二套问题组件。 - 相关记录:BUG-675、BUG-673、BUG-671、BUG-679
- 复发自:BUG-675(同一块兜底只修了选择题)
- 修复版本:待发布
BUG-679 | staging quality gate 因 DESIGN 合同仍锁旧 persisted-question 文案失败
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:Gitea
Independent Staging Quality Gatevalidate「Validate backend, package, frontend, and database contracts」、frontend/tests/rectification-unwritten-evidence.test.ts、frontend/DESIGN.md - 用户现象:推送
2dbcf266后门禁 run2598在校验步骤失败;publish/deploy skipped。依赖安装已通过,不是 BUG-677 的.venvmkdir。 - 触发条件:
npm test --prefix frontend在 Linux runner 上跑全量套件。 - 根因:BUG-678 把 DESIGN 的 persisted-question 从「completed turn that claimed to record evidence」改成「only when no assistant message can carry the prompt」,合同测试仍按旧句匹配,3179 通过、1 失败。产品行为测试未红。
- 修复:合同断言改跟新 DESIGN:question-live 含无
asked_turn_id的挂回,persisted-question 只留给没有助手消息可挂;旧句不得再出现。不改产品代码。 - 验证:
frontend/tests/rectification-unwritten-evidence.test.ts该条;Gitea gate 对修复 SHA 复跑。 - 防复发:改
frontend/DESIGN.md校正状态表必须同步源码合同测试。不得为了过门禁把 DESIGN 改回旧裸行口径。 - 相关记录:BUG-678、BUG-635、BUG-677
- 复发自:BUG-678(DESIGN 与合同测试分叉)
- 修复版本:待发布
BUG-680 | 刷新尝试落库失败仍按已尝试推进,POST 交付 GET 回采集
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
refreshDatedDiscriminatorPoolIfNeeded、persistRefreshAttempt、decideRectification、shouldSkipFollowupPersist - 用户现象:跳过最后一道定向补事题后,助手播出完整交付结论,刷新后没有交付卡、没有下一题,时间轴写「再说一件带年月的事就能继续」,并印出「没有拿到下一个问题。」
- 触发条件:前两名 posterior 并列(如 16/16),
stop_reason=tied_first;本轮选择题刚推进 revision,刷新尝试用旧 revision 写库撞版本冲突。 - 根因:
persistRefreshAttempt失败时返回值被丢弃,attemptRecorded仍为 true,POST 第二次decideFromDossier把refreshExhausted当成真并交付;GET 读库没有这条 attempt,stillNeedNarrowing为真,把tied_first压回collect_evidence。并列到顶本是终局,被「还有收窄手段」无条件压过。 - 修复:落库失败时
attemptRecorded=false,不把内存态写进 dossier,并打rectification_refresh_attempt_persist_failed。tied_first在stillNeedNarrowing/ coverageBlocks 之前直接completeWithRange。idle persist 在tied_first且 outcome 属于交付集合时跳过下一问。不放宽can_adopt。 - 验证:
frontend/tests/rectification-delivery-vs-collect-20260914.test.ts:persist 抛错后 attemptRecorded=false;同一份并列 dossier 上persistNextInterviewIfIdle与decideFromDossier都交付。 - 防复发:任何「是否已尝试/已耗尽」的标志,落库失败时一律按未完成处理。POST 与 GET 对同一状态的决策必须一致,并由对拍测试守着。不得靠内存 overlay 播交付。
- 相关记录:BUG-674、BUG-681、BUG-682
- 复发自:BUG-674(POST 与 GET 对同一状态判定不一致)
- 修复版本:待发布
BUG-681 | 交付话术能说、交付卡不能画
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
DELIVERY_OUTCOMES、deliveryNarrationAllowed、canShowRectificationSelectionCards、canShowRectificationReadonlyRange - 用户现象:正文已经念完「现在给的范围是…代表分钟为…」,界面没有区间交付卡。
- 触发条件:
session_outcome为completed_with_range或provisional_range;can_adopt为假。 - 根因:卡片闸门用
ADOPT_OUTCOMES且要求canAdopt。这两种正常收尾不在采用集合里。话术闸门认offer_provisional_range/ 公开采用,于是能说不能画。 - 修复:新增
DELIVERY_OUTCOMES= 采用集合 ∪{completed_with_range, provisional_range}。卡片闸门用它,不把can_adopt提权。交付 outcome 且selection_allowed时出卡、不出「再说一件」范围行。 - 验证:
rectification-delivery-vs-collect-20260914.test.ts、rectification-candidate-result.test.ts表驱动:话术为真则卡为真。 - 防复发:不得新增第二套交付卡。话术与卡必须由同一判据驱动。
- 相关记录:BUG-680、BUG-590
- 复发自:无
- 修复版本:待发布
BUG-682 | 已交付无题时缺口状态机兜底成「没有拿到下一个问题」
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
rectificationQuestionGapState、interviewDeliveredGap、rectification-agentic-chat.tsx - 用户现象:没有题、没有卡时印出「没有拿到下一个问题。」并出现「接着问」。
- 触发条件:
current_question === null且session_outcome=collect_evidence、stop_reason=tied_first(或已交付 outcome);不是 collect_waiting 的那几个不足理由。 - 根因:缺口状态机没有「已交付 / 没有更多可问」终态,重试耗尽后落到
unavailable。 - 修复:新增
delivered:无题且 outcome 属于交付集合,或stop_reason为tied_first/user_uncertainty_too_high。该状态显示出口说明,禁止「没有拿到下一个问题」。普通题没取到仍是unavailable。 - 验证:
rectification-delivery-vs-collect-20260914.test.ts、rectification-surface-state.test.ts。 - 防复发:已交付无题不得再走修复入口文案。不得把
delivered误伤真正缺题的unavailable。 - 相关记录:BUG-680、BUG-681
- 复发自:无
- 修复版本:待发布
BUG-683 | 并列早退在还有探针可问时就把第二题判成可采用
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
decideRectification的tied_first早退、completeWithRange未收窄窗口的采用闸门 - 用户现象:新建校正只答两轮采集,助手已经在问第三道带年月区分题,界面却宣布可采用、代表分钟,可信区间仍是开局窗口(如 04:45–05:15)。
- 触发条件:前两名(或前三名)分数完全并列;
stop_reason=tied_first;仍有带年月探针或定向补事未问完。 - 根因:复发自 BUG-680 的修复。上一单 D2 写的是把
tied_first提到!separation.sufficient分支里、stillNeedNarrowing之前;实现把它放到了coverageBlocks之前、整个不足分支之外,连「还有探针」和「方法覆盖未达标」都绕过。配套测试传入discriminatorProbe: null,实现却没有这个守卫,测试与实现语义不一致,所以回归没被拦住。并列在采集早期只是分数还没拉开,不是「无论如何都问不下去」。 - 修复:
tied_first早退必须同时probe === null且targetedCollectExhausted !== false。refreshExhausted不参与该判断。交付时若credible_range等于开局case.candidateRange,can_adopt为假,session_outcome只允许completed_with_range/provisional_range,不得宣布代表分钟可采用。 - 验证:
frontend/tests/rectification-tied-first-fix-20260914.test.ts;rectification-delivery-vs-collect-20260914.test.ts成对用例(有探针不交付 / 无探针且定向补事问完才交付)。 - 防复发:
tied_first交付测试必须成对——有带年月探针时不交付,无探针且定向补事问完时才交付。不得把开局窗口原样宣布为可采用。不得为了让并列局面收尾而放宽can_adopt。 - 相关记录:BUG-680、BUG-681、BUG-682、BUG-674
- 复发自:BUG-680(修复实现比任务书更宽,测试用空探针锁的是「没有探针才交付」,实现无此守卫)
- 修复版本:待发布
BUG-684 | 交付态下死卡仍把缺口打成「没有拿到下一个问题」
- 状态:investigating
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
interviewDeliveredGap、rectificationQuestionGapState、rectification-agentic-chat.tsx - 用户现象:决策已是
complete_with_range/tied_first,屏幕上仍有一道没有可点选项的题,并印「没有拿到下一个问题。」+「接着问」。 - 触发条件:交付 outcome 或
stop_reason=tied_first,同时current_question非空且interviewChoiceCardUnavailable为真。 - 根因:待取证。已确认
interviewDeliveredGap原先要求questionMissing === true,有题时delivered不触发,死卡走questionLoadFailed→unavailable。写入路径尚未钉死。 - 修复:本轮只做兜底与取证。
questionMissing || deadChoice时允许进入delivered;死卡且处于交付态打rectification_delivered_state_dead_choice(session_outcome、stop_reason、question_id、has_choice_card)。非交付态死卡仍unavailable,不误伤 BUG-671。 - 验证:
frontend/tests/rectification-tied-first-fix-20260914.test.ts锁定交付+死卡 →delivered,采集+死卡 →unavailable。 - 防复发:交付态不得再出现「没有拿到下一个问题」。根因确认前不得把本条标为 resolved。
- 相关记录:BUG-682、BUG-674、BUG-671、BUG-683
- 复发自:BUG-682(
delivered终态要求无题,死卡把会话钉在问题态) - 修复版本:待发布
- 证据缺口:同一会话 GET 的
current_question、choice_card、question_source,以及最后一条助手 turn 的offer_result_id。缺这四项之前不得臆断写入路径。
BUG-685 | 点「再答两道参考题」服务端成功,对话区没有任何变化
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
requestTieBreak、loadCaseSnapshot、applySnapshotTurnsToMessages、rectificationQuestionGapState - 用户现象:点「再答两道参考题微调排序」后 POST 返回
ok: true、choiceReady: true,界面仍是原来的范围卡,新参考题看不见。 - 触发条件:范围卡在场时点参考题入口;服务端已写入助手 turn 和活动焦点。
- 根因:
requestTieBreak调用不带合并回调的loadCaseSnapshot(),新 turn 不进messages。交付卡在场使缺口为idle,persisted_question也不会兜底画这道题。 - 修复:
requestTieBreak用与submitChoice相同的mergeTurnQuestions/appendUnseenAssistantTurns。未答焦点优先于「等读者点卡」的 idle。 - 验证:
frontend/tests/rectification-tiebreak-before-card-20260914.test.ts。 - 防复发:服务端返回 ok 而界面无变化,一律视为缺陷。任何新写 turn 的前端动作必须把 turn 并进对话区。
- 相关记录:BUG-663、BUG-666、BUG-667
- 复发自:BUG-663(入口只写焦点,前端刷新不合并 turn)
- 修复版本:待发布
BUG-686 | 平局直接出卡,参考题留成卡下可选按钮
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
decideRectificationheldForTieBreak、persistNextInterviewAfterChoice/persistNextInterviewIfIdle、范围卡按钮 - 用户现象:带年月题和定向补事问完后直接出范围卡;「你平时做事」「感情里你倾向」没被问。答「没结过婚」后连感情风格题也不再出现。
- 触发条件:带年月池空、即将交付;参考题仍可用且未用过。或该领域没有带年月事件。
- 根因:性格题被设计成出卡后的可选入口。另:
varga_style被错误要求该领域账本有带年月事件作锚,答「没有」会连带关掉风格题。 - 修复:产品拍板放宽版 A(2026-09-14)。出卡前只要参考题可用且没用过就先连问最多两道
varga_style(不看分差;仍只 ±1、不淘汰)。删除风格题的事件锚点丢弃;分盘上升星座少于两种时该题仍不出现。仍平时卡上写「这两分钟按现有信息分不开,参考题已经用过。」用过之后不再显示入口按钮。 - 验证:
frontend/tests/rectification-tiebreak-before-card-20260914.test.ts:16/16 与 lead=2 都先问参考题;无事件锚点时风格题仍出现;两道用完才交付。 - 防复发:不得把性格题改成淘汰候选。不得为凑满两道而放宽「该分盘至少两种上升星座」。自动进入参考题时不得再写第二条交付消息。卡上入口已于 BUG-706 删除,前置收集是唯一路径。
- 相关记录:BUG-663、BUG-667、BUG-629、BUG-597、BUG-706、BUG-708
- 复发自:BUG-663(入口做成出卡后可选)
- 修复版本:待发布
BUG-687 | 交付文案把已经拒答的线又列一遍
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
rangeNarrowHint - 用户现象:结婚、收入、家人都答过「没有/跳过」,范围卡仍写「能把它们分开的是这几条线:哪年结婚或订婚、哪年收入明显变过、家里哪年添丁」。
- 触发条件:定向补事已拒答或已关闭,随后交付区间。
- 根因:
rangeNarrowHint调targetedCollectPool时传入空的declinedTopics,有意忽略拒答。 - 修复:传入真实
declinedTopics。一条都不剩时写「能问的都问完了」,不再暗示还有没问的线。 - 验证:
frontend/tests/rectification-tiebreak-before-card-20260914.test.ts。 - 防复发:交付文案不得列出已答「没有发生过」的线。跳过的线按服务器计划最多再问一次,不在交付文案里当还开着的线列出。2026-09-16 产品修改防复发口径,见
TASK-rectification-precision-gate-guided-collect-20260916.mdD4。 - 相关记录:BUG-661、BUG-662、BUG-741
- 复发自:无
- 修复版本:待发布
BUG-688 | 风格题前置把耗尽与收口路径的交付也压住了
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
decideRectificationshouldHoldForTieBreak、耗尽 / closed-ceiling 出口 - 用户现象:带年月题和定向补事都问完、本应出范围卡时,若风格参考题拿不出来,会话停在「还在区分」且没有题。门禁上 6 条「该交付却不交付」变红。
- 触发条件:
pendingTieBreak为真,但风格题因分盘只有一种上升星座、焦点落库失败或已 hold 过一次而无法渲染;路径是 exhausted / closed-ceiling / 无载体收口。 - 根因:BUG-686 把「还有没问的风格题」做成无条件延迟交付,插在所有交付分支之前,没有出口、没有次数上限。
6fd7925a只把断言改绿,行为未修。 - 修复:只有真正拿得到可渲染风格题时才 hold;exhausted、closed-ceiling、holdout 不可用的收口不 hold;同一 Case 最多 hold 一轮。恢复「恰好一条交付闸」和「参考题确认语不算出口」两条断言。
- 验证:
frontend/tests/rectification-tiebreak-before-card-20260914.test.ts、rectification-exhaustion-exit-20260906.test.ts、rectification-probe-pool-exhausted-20260911.test.ts。 - 防复发:任何「延迟交付」的守卫都必须带出口,且不得作用于耗尽与收口路径。验收必须比对
npm test失败数与基线。不得把参考题确认语算作出载体。 - 相关记录:BUG-686、BUG-656、BUG-674、BUG-680、BUG-597、BUG-689
- 复发自:BUG-686(风格题前置扩大到所有交付分支)
- 修复版本:待发布
BUG-689 | 七条采集线问完后只说「能问的都问完了」,用户不知道还能补经历
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
rangeDeliveryCollectClosed、rangeNarrowHint、范围交付卡、交付后补证据 - 用户现象:结婚 / 收入 / 家人等固定线答过「没有/跳过」后,范围卡只写「能问的都问完了」。用户不知道还可以再说一件带年月的经历,也不知道补了会重算。真机并列约 26% / 26% / 20%。
- 触发条件:定向补事七条线全部关闭或覆盖,随后交付区间;前两名相对可能性差 ≤ 3 个百分点。
- 根因:BUG-687 把空池收口写成终局句。采集线关闭不等于校正结束:交付后 Case 仍是
candidate_ready,输入框不禁用,新的带年月证据会改账本指纹并让快照过期。缺的是邀请;过期快照若仍带着tied_first停止原因,缺口状态机会继续停在delivered。 - 修复:空池文案改为不限领域的补充邀请:先请记得确切哪一天的事,再退年月;举七条线之外的例子;不承诺定到分钟。并列且线已关闭时,邀请出现在交付卡正文,不躺在浅色说明里。快照因新证据过期时,决策不再把
tied_first等耗尽停止原因带进采集,缺口才不会停在交付态。 - 验证:
frontend/tests/rectification-open-collect-invite-20260914.test.ts。 - 防复发:采集线关闭不等于校正结束。2026-09-16 起自由文本邀请删除,改为系统点名的引导题 + 年/月选择器(BUG-740)。不得把已经答「没有发生过」的线再列一遍(BUG-687)。
- 相关记录:BUG-687、BUG-646、BUG-651、BUG-653、BUG-688、BUG-590、BUG-740
- 复发自:BUG-687(空池收口写成终局句)
- 修复版本:待发布
BUG-690 | 家人推算的出生时间与医院记录等同对待,输出直接称「你的出生时间」
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-14
- 影响面:
birth_time_source投影、交付卡、宫位板、采用旁白 - 用户现象:录入时已选「医院记录 / 家人记得 / 只知道时段」,校正链里决策和文案不看这个字段。家人事后推算的时间和出生证上的分钟被同样称作出生时间。
- 触发条件:
approximate或period_onlyCase 进入交付或采用;或hospital_record的分钟落在目前范围外。 - 根因:
birth_time_source只在放宽窗口时改写过一次,公开投影和用户文案没有来源标签。 - 修复:GET /
decideFromDossier投影带birth_time_source(缺失按approximate)。文案分档:医院记录称「出生记录时间」并可并列与目前范围的差值(不站队);家人记得称「你填的大概时间」;只知道时段称「你给的时间段」。不改打分与搜索窗中心。医院记录与范围冲突时如何取舍挂给产品负责人。 - 验证:
frontend/tests/rectification-birth-time-provenance-20260914.test.ts、agent-voice-copy-contract.test.ts。 - 防复发:
birth_time_source不是录入时的一次性字段,任何指代出生时间的输出都必须按它分档;推算值不得表述为已证实的出生时间。 - 相关记录:BUG-587、BUG-621、BUG-689
- 复发自:无
- 修复版本:待发布
BUG-691 | 医院记录与校正区间冲突时只并列差值,不说默认按哪个排盘,用户不知道点「更像这个」会换掉什么
- 状态:resolved
- 首次发现:2026-09-14
- 最近更新:2026-09-16
- 影响面:
frontend/src/lib/rectification-agentic/birth-time-provenance.ts、v9/divergence-panel.ts、components/rectification-range-delivery.tsx、frontend/docs/VOICE.md、frontend/DESIGN.md、Skillreferences/candidate-comparison.md - 用户现象:录入选了「医院记录」且记录那一分钟落在目前范围外时,交付卡只有一句「出生记录时间 04:40。目前范围不含这一分钟,相差 8 分钟」。没说默认按哪个时间排盘,也没说还能继续用记录;采用按钮仍写中性的「更像这个」,点下去实际上把之后的排盘从出生记录时间换成了另一分钟,用户看不出自己在换什么。
- 触发条件:
birth_time_source = hospital_record,且reported_birth_time落在credible_range之外,进入区间交付卡。 - 根因:BUG-690 定了三档来源标签,但把「记录与范围冲突时站哪边」显式挂给产品负责人未决,所以冲突文案只实现了并列差值的前半句;采用入口本就是为非冲突态设计的「更像这个」,没有随来源分档。
- 修复:产品负责人 2026-09-14 拍板「记录优先,分歧如实呈现」(依据:
docs/research/cluster_width_2026_09_14.md,封存 20 例六题后头名簇命中率 0.80 / 0.55 / 0.35 对 ±10 / ±30 / ±60 分钟窗,证据强度撑不起推翻书面记录;但医院记录确实会因事后补记、五分钟整数舍入、家属转述而错,故不得反过来宣布校正结果无效)。reportedTimeOffsetCopy的冲突分支补满三层:默认仍按记录排盘 / 经历指向另一段时间差 N 分钟 / 两条路都可以走。新增recordRangeConflict作为唯一的冲突判据,经RangeDeliveryProjection.record_conflict投给交付卡:冲突态按钮写「改用校正结果」,按钮下补一行「选它之后,排盘会从出生记录时间 hh:mm 换成 hh:mm」。只改文案与措辞,不改打分、判据、采用 RPC、置信度与确认门控,不动数据库,不新增入口。 - 验证:
frontend/tests/rectification-record-conflict-copy-20260916.test.ts(冲突判据只认落在范围外的医院记录;冲突文案含三层且不触FORBIDDEN_RECORD_VERDICT_PHRASE;记录落在范围内的文案逐字不变;冲突态按钮与逐列确认句;approximate/period_only/ 记录在范围内三态仍是「更像这个」且无确认句;投影往返后候选与代表分钟不变)。rectification-birth-time-provenance-20260914.test.ts一条断言按「原值 / 新值 / 原因」三栏改写(该用例的 05:12 对 04:48–05:07 本就是冲突态)。tsc --noEmit0 错,npm run lint0 error,npm test失败数与基线同为 31 条且逐条同名(全部是无 Docker / 无外网的既有环境缺口)。 - 防复发:记录与校正冲突时默认站在记录一边,但两者必须并列;系统不得宣布任何一方无效——不得写「记录不准 / 记录错了 / 以证据为准」,也不得写「校正结果无效 / 不作数」。该禁令由
FORBIDDEN_RECORD_VERDICT_PHRASE在文案层锁住,并写进frontend/docs/VOICE.md与 Skillreferences/candidate-comparison.md§6.1。冲突态改的只能是措辞,采用仍是校正采用时间,不是已确认的唯一出生分钟(BUG-587 红线维持)。 - 相关记录:BUG-690、BUG-587、BUG-621
- 复发自:无
- 修复版本:待发布
BUG-692 | 交付邀请语写「这两分钟」却与同段「4 个候选」矛盾;5 条契约断言未跟上
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
rangeDeliveryCollectClosed、rangeNarrowHint、exitAppendCalls、publicDecisionFields、精度研究 M1b 判定 - 用户现象:范围卡旁白先写「还剩 04:47–04:53 里 4 个候选」,紧接着写「这两分钟按现有信息分不开」。同时
npm test从基线 27 条涨到 32 条:4 条 exhaustion-exit 因短语表不含新交付文案判成 0 条出口载体,1 条 decision-authority 深比较缺birth_time_source。 - 触发条件:固定七条采集线关闭后交付;剩余候选数 ≠ 2。或在含 BUG-689/690 的树上跑全量
npm test。 - 根因:BUG-689 把 tie-break 的「这两分钟」硬编码进任意候选数的邀请语;BUG-689/690 改了用户可见文案和公开字段,但没同步两处锁契约的测试。staging 当时部署仍是
cf972f40,未影响真实用户。 - 修复:邀请语按候选数出话(2 → 「这两分钟」;>2 → 「这几个候选」;未知 → 不说这半句)。
exitAppendCalls()加入「收到 … 这 N 分钟 / 按现有信息分不开 / 说出来我接着算」,保持assert.equal(gates.length, 1),不加「只微调排序」。公开字段深比较补birth_time_source。M1b 的 V1/V2 从no_benefit勘误为not_measured。 - 验证:
frontend/tests/rectification-open-collect-invite-20260914.test.ts、rectification-exhaustion-exit-20260906.test.ts、rectification-decision-authority.test.ts。 - 防复发:文案里出现的数字必须来自同一份数据,不得硬编码;改用户可见文案或公开字段时,必须同步跑
npm test并把失败数与基线比对。 - 补充(2026-09-26 离线研究 R2):M1b 的 V1/V2 已重跑。权重确实生效(研究计分器 V0 与线上逐分相同);V1/V2 在 ±30 / ±60 上按定义等于等权(0 个候选分数变化),在 ±10 上只微调分数、六题后指标不变。判定由
not_measured改为no_benefit(已实测)。见docs/research/rectification_offline_research_2026_09_26.md§R2。 - 相关记录:BUG-689、BUG-690、BUG-688、BUG-687、BUG-587
- 复发自:BUG-689(邀请语复用两候选句)、BUG-690(公开字段未进深比较)
- 修复版本:待发布
BUG-693 | 专业报告 result_hash 只绑中间态,交付包一半键不在覆盖里
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
build_professional_report_reference_packet、bind_result_to_profile、full_report_quality_gateprovenance:result_binding - 用户现象:专业参考包回执自称绑定完整,质量门
provenance:result_binding恒 passed。实测第一次哈希 18 个顶层键 / 6.24 MB,交付对象 36 个顶层键 / 10.04 MB;full_report_pack、chart_identity、timing_precision_contract、birth_provenance、rectification_evidence_contract等 18 个键未进result_hash。 - 触发条件:
pl9-export --pack full。虚构盘1990-05-17 09:26 +08 / 31.23,121.47。 - 根因:
attach_calculation_profile在已有 profile 时早退。专业参考链末尾那次调用被当成重绑,实际什么都没做。质量门只比result_binding与input_hash/result_hash自洽。 - 修复:对
sanitize_professional_report_reference之后的交付对象显式bind_result_to_profile。不改通用早退语义。质量门新增provenance:result_binding_scope:按回执排除集重算哈希,缺 scope / 意外排除 / 对不上都blocked。 - 验证:
tests/test_calculation_profile_contract.py、tests/test_full_report_quality_gate.py;虚构盘三次 CLIresult_hash一致,覆盖 33 个顶层键且含上述五个必覆盖键。 - 防复发:专业参考链的绑定必须发生在返回对象上;排除集必须显式落在回执里并被质量门校验。
- 相关记录:BUG-694、BUG-576
- 复发自:无
- 修复版本:待发布
BUG-694 | 同一份输入两次专业报告 result_hash 不同
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
bind_result_to_profile哈希载荷、result_binding.binding_scope - 用户现象:
input_hash相同,两次 CLI 的result_hash不同(f4f65216…/d841a4b3…)。回执只能证明自洽,不能证明同输入同结果。 - 触发条件:同一虚构盘连续跑两次
pl9-export --pack full --format json。 - 根因:被哈希的载荷含墙钟字段(
elapsed_seconds、分阶段耗时、VedAstrocalled_at/ cache 时间)。kendra_lords由list(set(...))生成,跨进程顺序不稳定。 - 修复:
binding_scope显式排除自引用(report_quality_gate、shared_full_report_authority)、生成时刻(generated_at以及**.called_at/**.cache_created_at/**.cache_expires_at)、墙钟耗时(**.elapsed_seconds)。业务字段仍输出。Kendra / trikona lords 改为排序后的稳定列表。 - 验证:同输入两次构造
result_hash相同;只改elapsed_seconds不变;改coverage必变。虚构盘三次 CLI 哈希2f4739a4…全同,内部elapsed_seconds仍为 2.74 / 2.86 / 2.83。 - 防复发:排除集只能是自引用 / 生成时刻 / 墙钟三类,必须写进回执并由质量门重算。加第四类要改任务书。允许集有字面量合同测试守门,改动必须先过测试再改任务书。
- 相关记录:BUG-693、BUG-576
- 复发自:无
- 修复版本:待发布
BUG-695 | 回答操作触屏热区 27×34,点赞容易点成点踩
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:聊天回答下方复制 / 点赞 / 点踩 / 重新生成,以及代码块复制按钮
- 用户现象:四个图标并排,手机上点赞很容易点成点踩。
- 触发条件:触屏设备上点助手回答下方的操作行。
- 根因:复发自 BUG-446 的有意让步。当时中心距只有 27px、下方 follow-up 只有 8px,44×44 会互相吃点击,热区停在 27×34。触屏推荐仍是 44×44。
- 修复:视觉图标仍是 26×26。
(pointer: coarse)下 gap 和底边距收到 20px,伪元素放大到 44×44。细指针几何不变。代码块复制按钮在粗指针下min-height: 44px。 - 验证:代码级契约测试锁细指针 27×34、粗指针 44×44 且 gap/底边 20px。浏览器级验收见
docs/testing/mobile-touch-and-breakpoints-20260915.md。 - 防复发:
frontend/tests/touch-target-contract.test.ts同时锁两套几何。粗指针热区不得与相邻按钮或 follow-up 药丸重叠。 - 相关记录:BUG-446
- 复发自:BUG-446(当时为避免重叠把热区停在 27×34)
- 修复版本:待发布
BUG-696 | CSS 平板上限 900px 与侧栏 1024 不一致,901–1023 是混合态
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:兑换码表单、模型选择器、首页产品卡在平板宽度下的栅格
- 用户现象:窗口拖到大约 iPad 横屏附近时,侧栏已经是窄版平板,内容区却按桌面排。
- 触发条件:视口宽度 901–1023px。
- 根因:
sidebarViewportForWidth在 1024 切桌面,CSS 另有max-width: 900px当平板上限。 - 修复:这些 CSS 切点改为
max-width: 1023px。主题卡留下的.starter-list > button孤儿规则删除,不改断点再留着。 - 验证:
viewport-breakpoint-contract.test.ts断言@media里不再出现max-width: 900px,且 JS 阈值仍是 768 / 1024。浏览器级验收见docs/testing/mobile-touch-and-breakpoints-20260915.md。 - 防复发:宽度断点白名单契约测试;新切点必须先改白名单。
- 相关记录:BUG-697
- 复发自:无
- 修复版本:待发布
BUG-697 | 报告域 720 / 760 / 860 三个断点互不对齐
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:报告中心、专业报告阅读页、报告目录
- 用户现象:761–860px 目录已经塌成抽屉,正文还是桌面内边距;721–760 阅读区已窄、报告中心还是桌面。
- 触发条件:视口落在那两个混合区间。
- 根因:报告中心用 720、阅读区用 760、目录用 860,彼此和全局 767 都不对齐。
- 修复:720 与 760 并入 767。860 保留并就地注释:目录是 1120 阅读页的第三栏,必须比正文更早塌。会员 639/640 与校正 640/641 统一为 640/641。录入卡 620 并入 640。toast / 校正面 430 并入 480。
- 验证:白名单契约测试抽出的
@media宽度集合等于 480 / 640 / 641 / 767 / 768 / 860 / 1023 / 1024。浏览器级验收见docs/testing/mobile-touch-and-breakpoints-20260915.md。 - 防复发:
viewport-breakpoint-contract.test.ts;globals.css顶部白名单注释。 - 相关记录:BUG-696
- 复发自:无
- 修复版本:待发布
BUG-699 | 打开历史校正会话会改成今天的标题并顶到列表最前
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:侧栏历史列表、
openRectificationCase、persistSession的 update 标题字段 - 用户现象:点开一条以前的生时校正,标题变成今天的日期,并出现在列表最上面。刷新后位置会回去,标题不会。
- 触发条件:点侧栏里已有的校正会话,或从首页校正卡打开一条已存在的可续校正。
- 根因:复发自 BUG-553。
openRectificationCase构造merged时pinned/archivedAt从existing继承,title和updatedAt却无条件用墙钟重算,再persistSession把标题写回服务端。persistSession的 update 不写updated_at(BUG-553 的修复仍在),所以排序只是客户端暂时错位。BUG-553 的防复发当时只是一句话,测试只锁了元数据 PATCH 和改名/收藏/归档/换模型/换资料,没有覆盖校正 open。 - 修复:已有会话的
title/updatedAt继承existing,只有新建才resolveSessionTitle+timestamp()。错日期标题按created_at的 Asia/Shanghai 月日修回;对不上正则的手改标题不动。存量错标题的修补已改为一次性迁移20260916020000_rectification_session_title_repair.sql,随Migrate Staging Database按钮应用(原先只写在frontend/scripts/repair-rectification-session-titles.mjs里,要SCHEMA_DATABASE_URL才能跑,产品没有执行通道,所以一次都没跑过);该脚本降级为只读核对工具。 - 验证:
frontend/tests/session-open-preserves-identity.test.ts、rectification-session-title-repair.test.ts、rectification-session-title-repair-migration.test.ts(比对迁移与核对脚本的 SQL 是否仍逐字一致)。浏览器级验收见docs/testing/rectification-open-identity-20260915.md。欠账:staging 标题修回行数待产品点一次Migrate Staging Database后,从运行日志的notice ... repaired_titles=回填。无 Docker 环境跑不了npm run test:db,迁移未实跑过。 - 防复发:构造可能作用于已有会话的
ChatSession时,不得无条件写updatedAt: timestamp()。resolveSessionTitle的at默认墙钟,只能用于新建。 - 相关记录:BUG-553、BUG-704、BUG-705
- 复发自:BUG-553
- 修复版本:待发布
BUG-700 | vendored 七政引擎四柱时柱按 UTC 墙钟计算
- 状态:blocked
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:vendored
@4n6h4x0r/stem-branch0.8.0 CLI 的--pillars;本仓/api/qizheng适配器 - 用户现象:把带时区偏移的 ISO 时刻交给
--pillars时,时柱按 UTC 墙钟而不是出生地本地民用时。中国用户会得到错 8 小时的时柱;午夜前后日柱也会错。 - 触发条件:CLI
--pillars --json且--date带+HH:MM/-HH:MM偏移。 - 根因:同一时间入口同时服务「天文时刻」(要 UTC)和「历法干支」(要本地民用时)。本轮不修引擎。
- 修复:本轮不调用
--pillars/--luck/--polaris/--qimen/--liuren。/api/qizheng只走七政四余库函数。四柱不在本单产品范围。 - 验证:适配器源码与
_NODE_EVAL不含上述子命令;tests/test_qizheng_chart_engine.py锁死不得调用。三组对照(同一本地墙上 13:24):带+08:00偏移 → 时柱为卯;去掉偏移 → 时柱为未;写+00:00→ 时柱为未。本地民用时应为未。 - 防复发:绝不把带时区偏移的 ISO 字符串喂给需要本地民用时的子命令。若将来做四柱,必须先解决本条,并继续遵守 BUG-245 / BUG-246「所有星盘计算入口必须统一补算历史 offset」。任务书所称 BUG-905 与此同族;本仓当前无独立 BUG-905 条目。
- 相关记录:BUG-245、BUG-246
- 复发自:无
- 修复版本:未修(blocked)
BUG-701 | 计都派别 ketuMode: apogee 未暴露为参数
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:七政四余本命、
scripts/qizheng_chart_engine.py、POST /api/qizheng - 用户现象:引擎默认
ketuMode=apogee,计都落在月孛附近而不是罗睺对点。请求侧看不到、也改不了这一派。 - 触发条件:调用七政四余且未传
ketu_mode/sidereal_mode。 - 根因:vendored CLI 不把
ketuMode/siderealMode做成旗标;适配器若只透传 CLI 默认值,派别会静默生效。 - 修复:
ketu_mode与sidereal_mode从请求体读取,缺省apogee/{type: modern},回写进calculation。实现走dist/index.cjs的getSevenGovernorsChart(..., options),因为 CLI 没有对应旗标。 - 验证:
tests/test_qizheng_chart_engine.py覆盖默认回写与descending-node覆盖(计都与罗睺约 180°)。 - 防复发:计都派别与宿度起算口径必须是显式参数,不得只靠引擎默认。
- 相关记录:BUG-702
- 复发自:无
- 修复版本:待发布
BUG-702 | 适配器 boundary 把空神煞与未闭合庙旺说成已生成
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
scripts/qizheng_chart_engine.py的boundary/dignities - 用户现象:文案写已生成神煞和当前庙旺;实测神煞为空,11 曜庙旺除太阳「陷」外全是「平」。
- 触发条件:读取七政四余响应的 boundary / dignities。
- 根因:boundary 按引擎能力清单写,不按本次实际输出写。庙旺表 132 格只闭合 9 格,
runtime_promotable_count = 0,不能当判定用。 - 修复:boundary 不再出现「神煞」二字;庙旺带
unclosed且may_enter_conclusions=false,并引用 132/9/runtime_promotable_count=0口径。解读层、报告层、咨询层不得引用庙旺。 - 验证:
tests/test_qizheng_chart_engine.py、tests/test_qizheng_api_productization.py断言 boundary 不含「神煞」,dignities.status 为 unclosed。 - 防复发:boundary 必须按本次实际输出写;庙旺在未闭合前不得进入结论。
- 相关记录:BUG-701
- 复发自:无
- 修复版本:待发布
BUG-703 | /api/panchanga_range 岁差写死 Lahiri,与账户默认 Raman 不一致
- 状态:investigating
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
POST /api/panchanga_range、星历/今日路径的五要素 - 用户现象:账户默认岁差是 Raman,本命盘按 Raman 算;同一用户在 panchanga 范围接口看到的五要素口径却是 Lahiri。
- 触发条件:
POST /api/panchanga_range即使请求带ayanamsa=raman,响应report.calculation_policy.panchanga仍写SwissEph Lahiri at sunrise-relative reference time。 - 根因:本轮只记录,不猜测。是统一到 Raman 还是声明 panchanga 永远用 Lahiri,属产品决策。
- 修复:未修。
- 验证:未修,无回归测试。
- 防复发:待产品决策后另出单。岁差必须按请求参数走,不得写死。
- 相关记录:BUG-707(星历页只标注、不修)
- 复发自:无
- 修复版本:未修(investigating)
BUG-704 | 校正答题不推进 chat_sessions.updated_at,会话边用边下沉
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
agentic_rectification_cases.last_activity_at、chat_sessions.updated_at、侧栏历史排序 - 用户现象:一条校正连着答几小时,侧栏位置仍停在创建那天。叠加上被改成今天的标题后,列表变成「名字是今天、排序是旧日期」。
- 触发条件:在生时校正里写 turn、点选、采用或停止。打开、刷新、改名、收藏不触发。
- 根因:复发自 BUG-553 的另一半。全仓只有咨询 RPC
append_consultation_question写chat_sessions.updated_at。校正 turn 走append_agentic_rectification_turn等,只推agentic_rectification_cases.last_activity_at,不碰会话表。persistSession的 update 按 BUG-553 故意不写updated_at。 - 修复:
last_activity_at推进时触发器同步把对应校正会话的updated_at推到同一时刻,且不得回拨。历史冻结行按最后一条 turn /last_activity_at回填,该回填同样已改为一次性迁移20260916020000_rectification_session_title_repair.sql(与 BUG-699 的标题修补同一条),随Migrate Staging Database应用;updated_at <的单调守卫保证只前进不回拨,重复应用影响 0 行。客户端persistSession仍不写updated_at。 - 验证:
frontend/tests/rectification-v9-migration.test.ts的 touch-session 迁移合同、rectification-session-title-repair-migration.test.ts的回填 SQL 合同。浏览器级见docs/testing/rectification-open-identity-20260915.md。欠账:staging 回填行数待产品点一次Migrate Staging Database后,从运行日志的notice ... refreshed_activity=回填。无 Docker 环境跑不了npm run test:db,迁移未实跑过。 - 防复发:校正对话活动必须推进
chat_sessions.updated_at。元数据 PATCH、打开、刷新不得推进。不得为了修这个让persistSession重新写updated_at。 - 相关记录:BUG-553、BUG-699、BUG-705
- 复发自:BUG-553
- 修复版本:待发布
BUG-705 | ?c= 不在已加载页就被当成已删除
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
resolveBootstrapSessionSelection、GET /api/sessions/{id}、首页 bootstrap、popstate - 用户现象:刷新或从链接进入一条校正,提示「该对话不存在或已被删除」,地址栏
?c=被清掉,侧栏也看不见。没有删除动作发生。 - 触发条件:目标会话不在当前已加载的 40 条里(常见于 BUG-704 把它压到旧日期),URL 仍带着它的 id。
- 根因:
listedIds只覆盖当前页。未命中就missing: true并replace-clear。服务端单条 GET 存在却从未被问。 - 修复:合法 UUID 未在已加载页时改为
lookup。GET /api/sessions/{id}200 则并进列表并选中;404 才宣告删除并清 URL;5xx / 网络失败保留?c=,提示「这条对话暂时读不到,请稍后重试。」 - 验证:
frontend/tests/session-lookup-unlisted.test.ts、chat-session-url.test.ts。 - 防复发:不在已加载页不得等于已删除。
SESSION_MISSING_NOTICE只允许在服务端 404 之后出现。 - 相关记录:BUG-553、BUG-699、BUG-704
- 复发自:无
- 修复版本:待发布
BUG-706 | 点卡上「再答两道参考题」后交付卡整张消失
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
applyLiveCandidateOffer、showSelectionCards、区间交付卡 - 用户现象:范围卡在场时点「再答两道参考题微调排序」,卡消失,只剩一句确认语和一道没有选项的题,上一轮「范围在上面」也对不上了。
- 触发条件:交付卡上的参考题按钮;
requestTieBreak写入一道未答choice/collect_spoken。 - 根因:
applyLiveCandidateOffer和showSelectionCards只要有未答题就把每一条candidateOffer剥掉 / 整卡不画。守卫本意是有活题时不要同时诱导采用,但按钮就长在卡上,点一下必然丢卡。产品原意(BUG-686)是出卡前收集参考题,卡上这条路不该存在。 - 修复:删除卡上入口。有活题时卡留下,「更像这个」置灰并写「先答完上面这道,再选时间」。前置收集仍走
requestTieBreakPersonality。 - 验证:
frontend/tests/rectification-tiebreak-card-loss-20260915.test.ts、rectification-candidate-offer-anchor.test.ts、rectification-range-delivery-20260907.test.ts。 - 防复发:任何抑制交付卡的守卫都必须有出口。不得再在交付卡上放参考题按钮。新写 turn 仍须并进对话区(BUG-685)。
- 相关记录:BUG-685、BUG-686、BUG-688
- 复发自:BUG-686(卡上保留了第二条路)
- 修复版本:待发布
BUG-707 | 星历页五要素按 Lahiri,星盘页按账户岁差(默认 Raman)
- 状态:investigating
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
/api/panchanga_range、星历页五要素段、星盘页 //api/chart的账户岁差 - 用户现象:同一账户在星盘页看到的是 Raman 盘(账户默认自
80102459起已是 Raman),在星历页五要素看到的却是 Lahiri 口径。 - 触发条件:已登录并打开
/ephemeris;五要素来自/api/panchanga_range。请求里即使带ayanamsa: "raman",report.calculation_policy.panchanga仍是"SwissEph Lahiri at sunrise-relative reference time"。 - 根因:未判定。同一现象以 BUG-703 为准,本条不另开根因。
- 修复:本轮不修。星历页五要素段标注「这一段按 Lahiri 岁差算,和星盘页用的岁差不是同一套。」行运段走
/api/chart、用账户岁差,不加这句。 - 验证:
frontend/tests/ephemeris-page.test.tsx锁定标注只出现在五要素段。真人清单见docs/testing/ephemeris-page-20260915.md。 - 防复发:在产品未拍板前,不得把 panchanga 默认岁差改成与
/api/chart静默对齐,也不得删掉星历页这行标注。 - 相关记录:BUG-703
- 复发自:无
- 修复版本:未修
BUG-708 | 参考题按钮能亮,点开却是没有选项的裸题
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
tieBreakPersonalityAvailable、buildChoiceCard/canRenderYearlessChoice、interviewChoiceCardUnavailable - 用户现象:点参考题后出现「亲密关系里,你更接近哪一种相处方式?」,没有 A/B/C/D。交付卡已被 BUG-706 抹掉,两条路都断。
- 触发条件:
tieBreakPersonalityFollowup拿得到 followup,但styleOptions建不出或分盘上升星座少于两种。 - 根因:复发自 BUG-686。按钮只看 followup 非空;卡片渲染另走
completeStyleOptions+canRenderYearlessChoice。两个判据不一致。也落在 BUG-674 / BUG-675 的裸题家族上。 - 修复:followup 存在且同一套可渲染性判断通过,才算 available,才允许 persist。焦点已落库但没有
choice_card时走既有 repair-exit,不画裸题。 - 验证:
frontend/tests/rectification-tiebreak-card-loss-20260915.test.ts。rectification-tiebreak-before-card-20260914.test.ts既有断言未减弱。 - 防复发:
tieBreakPersonalityAvailable必须复用vargaStyleFollowupRenderable,不得再复制第三套条件。建不出选项的题不得提出,也不得渲染成裸题。 - 相关记录:BUG-686、BUG-674、BUG-675、BUG-706
- 复发自:BUG-686
- 修复版本:待发布
BUG-709 | 交付旁白写「相对支持度」,并与卡上入口自相矛盾
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:采用/交付旁白、
MACHINE_VOICE_LEXICON、validateAdoptNarration、VOICE.md - 用户现象:旁白写「没有年份的分盘题也不再问」「相对支持度都是 16」,同一屏卡上却摆着「再答两道参考题」。
- 触发条件:走到区间交付;Agent 旁白引用了内部计分词。
- 根因:采用旁白指令允许使用「相对支持度数字」。校验只拦未知数字,不拦禁用词。卡上仍保留参考题按钮,与「不再问」打脸。
- 修复:禁用词表与校验都拦「相对支持度」。指令改为「这两个时间按现有信息分不开」。卡上入口删除后,「不再问」与控件不再打架。卡上「相对可能性 %」保持不变。
- 验证:
frontend/tests/agent-voice-copy-contract.test.ts、rectification-tiebreak-card-loss-20260915.test.ts。 - 防复发:用户可见文案不得出现「相对支持度」。
MACHINE_VOICE_LEXICON与validateAdoptNarration双拦。 - 相关记录:BUG-706、BUG-686
- 复发自:无
- 修复版本:待发布
BUG-710 | 七政 ketu_mode / sidereal_mode 收了请求却不传给引擎
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
scripts/qizheng_chart_engine.py、POST /api/qizheng、星盘页七政 Tab 的计都派别文案 - 用户现象:请求
ketu_mode: "descending-node"仍得到月孛派计都位置,响应calculation.ketu_mode却写成 descending-node,没有 warning。 - 触发条件:
POST /api/qizheng带非默认ketu_mode或sidereal_mode。 - 根因:vendored CLI 的
getSevenGovernorsChart(date, location)不传 options。适配器只调 CLI 旗标,旗标里没有派别。_normalize把请求值写进calculation.ketu_mode,把 CLI 实际值另放engine_ketu_mode。 - 修复:走任务书方案 A。
scripts/qizheng_seven_governors.js在内存里取出库函数getSevenGovernorsChart并传入ketuMode/siderealMode。calculation.ketu_mode与engine_ketu_mode都只表示实际生效派别。前端chart-view-mapper读calculation.engine_ketu_mode。 - 验证:
tests/test_qizheng_chart_engine.py新增非默认派别 / 岁差用例;frontend/tests/chart-view-route.test.ts锁 mapper 用引擎字段。 - 防复发:成功响应里
calculation.ketu_mode必须等于engine_ketu_mode。不得再把请求值回写成已生效派别。 - 相关记录:BUG-701、TASK-qizheng-native-chart-20260915、TASK-readonly-pages-fix-20260916
- 复发自:BUG-701(当时只把默认值暴露成参数,没有把参数送进引擎)
- 修复版本:待发布
BUG-711 | 星历页合同断言禁止 sidebar 出现 ephemeris,与星盘页入口冲突
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
frontend/tests/ephemeris-page.test.tsx、frontend/src/components/app-sidebar.tsx - 用户现象:两份只读页单合入后
npx tsx --test tests/ephemeris-page.test.tsx1 红,staging 门禁红。 - 触发条件:chart-page 单按任务书在 sidebar 同时加「星盘」「星历」入口;星历单合同断言
doesNotMatch(app-sidebar.tsx, /ephemeris/)。 - 根因:星历单把「本单不改 sidebar」写成了「sidebar 里不得出现 ephemeris」。前者是文件归属,后者不是产品事实。
- 修复:删掉对 sidebar 的禁止断言,改为只约束本单文件归属(独立
/ephemerisroute、不碰page.tsx)。不删 sidebar 星历入口。 - 验证:
npx tsx --test tests/ephemeris-page.test.tsx全绿。 - 防复发:文件归属断言不得写成产品入口禁止。改既有断言必须写原值 / 新值 / 原因。
- 相关记录:TASK-chart-page-20260915、TASK-ephemeris-page-20260915、TASK-readonly-pages-fix-20260916
- 复发自:无
- 修复版本:待发布
BUG-712 | ephemeris_events golden 存全精度浮点且缺失时自动重建
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
tests/test_ephemeris_events.py、tests/golden/ephemeris_events_raman_20260915_90d.json - 用户现象:同一窗口、同一岁差,事件种类 / 日期 / 星体 / 星座一致,只有
speed_longitude尾数差一位,验收机红。 - 触发条件:golden 在 Windows / Anaconda 3.11.7 生成,Linux / Python 3.13 / 仓库
.venv上assert result["events"] == golden。 - 根因:全精度浮点整体相等跨 pyswisseph / 星历数据会抖。golden 不存在时测试自己写出文件,等于没有 golden。
- 修复:
longitude/speed_longitude量化到 6 位再比;kind/date/body/ 星座字段保持严格相等。缺失 golden 失败,并提示用scripts/generate_ephemeris_events_golden.py重建。 - 验证:
pytest tests/test_ephemeris_events.py;缺失 golden 的定向用例确认不会写文件。 - 防复发:不得对全精度浮点做整体
==。测试不得写 golden。 - 相关记录:TASK-ephemeris-page-20260915、TASK-readonly-pages-fix-20260916
- 复发自:无
- 修复版本:待发布
BUG-713 | 新增 /ephemeris 未同步 capability audit 路由契约,staging 门禁红
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:Gitea
backend-quality-gaterun2635、tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources - 用户现象:
a38a9415门禁 Python 段1 failed, 765 passed。失败断言app_routes在chart之后多了ephemeris。 - 触发条件:星历页独立 route
frontend/src/app/ephemeris/page.tsx合入后跑 capability audit 精确集合。 - 根因:BUG-143 / BUG-454 同类。
_scan_app_routes动态扫描frontend/src/app/**/page.tsx;精确期望列表未列入ephemeris。星历单被要求不碰后端测试,chart-page 只把chart写进了该列表。 - 修复:期望路由集合加入
ephemeris,不隐藏真实入口。 - 验证:
pytest tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources;推 staging 后核对门禁 run。 - 防复发:新增或删除
frontend/src/app/**/page.tsx必须同一提交更新该精确集合,并跑 capability audit。前端矩阵不能替代这条 Python 契约。 - 相关记录:BUG-014、BUG-126、BUG-143、BUG-454
- 复发自:BUG-454
- 修复版本:待发布
BUG-714 | gated-paths.txt 有 vendor/**,quality-gate workflow 的 paths: 没有
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:Gitea
backend-quality-gaterun2636、frontend/tests/staging-backend-workflows.test.ts - 用户现象:run
2636前端门禁# fail 1:gated paths are one list shared by both quality-gate triggers。期望含vendor/**,workflow 实际没有。 - 触发条件:qizheng 单把
vendor/**写入deploy/gated-paths.txt后,未同步.gitea/workflows/backend-quality-gate.yml的pull_request/pushpaths:。 - 根因:清单是单一真相,workflow 过滤器靠合同测试对齐。只改了 txt、没改 yml,合同必然红;同时 vendor 纯改动也不会触发门禁。
- 修复:两个 trigger 的
paths:都补上vendor/**,顺序与gated-paths.txt一致。不改 job 逻辑、不改 deploy-production。 - 验证:
npx tsx --test tests/staging-backend-workflows.test.ts该条通过;推 staging 后门禁 run。 - 防复发:改
deploy/gated-paths.txt必须同一提交改backend-quality-gate.yml的两份paths:。该合同测试就是为这件事存在的。 - 相关记录:TASK-qizheng-native-chart-20260915、TASK-readonly-pages-fix-20260916
- 复发自:无
- 修复版本:待发布
BUG-715 | 星盘页引擎失败被碾成 null,忙和坏盘共用一句「过一会儿再打开」
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
frontend/src/lib/chart-view-service.ts、frontend/src/lib/chart-view-load.ts、frontend/src/lib/chart-view-engine.ts、/chart - 用户现象:星盘打开失败只看到「这张盘这会儿算不出来。资料还在,过一会儿再打开。」服务端日志里看不出是 429、500、超时还是坏 JSON。
- 触发条件:
/api/chart返回非 2xx、超时、JSON 解析失败,或 mapper 抛错。 - 根因:
postEngine把所有失败return null,catch {}吞掉异常且不打日志。用户文案把「等一下会好」和「这张盘有 bug」写成同一句。 - 修复:引擎调用返回
ok / busy / http_error / timeout / bad_payload。每一种非 ok 打console.warn(只含路径、状态码或错误名、耗时,不含出生资料)。429 文案「算盘的服务正忙,稍等几秒再打开就好。」其它「这张盘算不出来,我们已经记录下来了。」mapper 失败同样记日志。 - 验证:
npx tsx --test tests/chart-view-engine.test.ts tests/chart-view-route.test.ts tests/chart-page-view.test.tsx;四种桩各有可区分日志,429 与其它走不同文案。 - 防复发:引擎调用不得用
catch {}吞掉原因,失败必须留服务端日志且用户文案按原因分档。 - 相关记录:BUG-716、BUG-717、TASK-chart-page-blocking-open-20260915
- 复发自:无
- 修复版本:待发布
BUG-716 | /chart 动态 SSR 等完五个引擎调用才开始画,白屏最长 45 秒
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
frontend/src/app/chart/page.tsx、frontend/src/components/chart-page/chart-page-view.tsx、frontend/src/hooks/use-chart-page.ts、/api/chart-view - 用户现象:点侧栏「星盘」后整页空白很久,然后才出盘或失败句。侧栏已改成硬文档跳转,动态路由在服务端等完才吐 HTML。
- 触发条件:从对话进
/chart。/api/chart若慢或挂起,用户盯空白,最长 45 秒(engineTimeoutMs)。 - 根因:
chart/page.tsxforce-dynamic且await loadChartView(),而loadChartView先串行/api/chart再并行四个后续调用。文档卸载重载期间浏览器里什么都没有。 - 修复:
/chart改为静态壳,客户端先画标题和返回,再取/api/chart-view。进页只算主盘;分盘 / Chara / 西洋 / 七政按 tab 或非 D1 chip 按需取。超时改为 10 秒(实测主盘 0.60 秒,10 秒约 16 倍余量,避免再盯 45 秒空白)。等待句用「还没拿到」,不转圈。 - 验证:无
view时外壳可见;挂起主盘不再依赖四个后续调用;源码合同禁止force-dynamic/loadChartView出现在chart/page.tsx。 - 防复发:
/chart首字节不得等待四个后续引擎调用。揭幕后填 tab 不得再上 spinner / 「正在加载」。2026-09-24 BUG-1016 产品决策仅允许星盘页 D1、非 D1 分盘、西洋盘位置复用 SVG 空盘骨架;表格区及其它页面仍禁止骨架。 - 相关记录:BUG-715、BUG-717、TASK-chart-page-blocking-open-20260915
- 复发自:无
- 修复版本:待发布
BUG-717 | 开一次星盘页占满重计算配额,且「打开即有」印在失败页上
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-17
- 影响面:
assembleChartView的 layer 参数、chart-page-view.tsx眉标、服务端引擎结果缓存 - 用户现象:打开星盘常失败;失败页上还写着「直接计算 · 打开即有 · 不消耗点数」。生产与 staging 重计算并发默认 2,开页并行打
/api/western与/api/qizheng就会占满。 - 触发条件:打开
/chart。同时有第二个人开星盘,或校正 / 咨询在跑,更容易 429。 - 根因:进页无条件并行打两个
HEAVY_COMPUTE_PATHS端点;cache: "no-store"且无记忆;失败分支也渲染同一句速度承诺眉标。 - 修复:开页只请求
/api/chart。西洋 / 七政点到对应 tab 才发,不再占开页配额。同一账户 + 出生资料指纹 + ayanamsa +node_mode的引擎结果内存缓存 5 分钟,资料或岁差一变即换键。失败页删除眉标。不放宽限流。2026-09-17 产品判定成功页「主盘直接算 · 分盘按需 · 不消耗点数」仍是过度提示,连同基础信息「下面是词条式释义,不是对你个人的判断。」一并去掉;按需加载与不扣点的行为不变,只是不再写在页上。 - 验证:无 layers 时
assembleChartView的引擎路径只有/api/chart;成功页与失败页都无 eyebrow、无「不消耗点数」、无「词条式释义」;缓存键随 ayanamsa / node_mode 变化。 - 防复发:开页不得并行打
/api/western与/api/qizheng。不得靠放宽HEAVY_COMPUTE_PATHS或提高JYOTISH_HEAVY_COMPUTE_CONCURRENCY来「解决」429。承诺速度、计费、词条边界的说明不写在星盘页上。chart-page-view.test.tsx成功页与失败页都断言不得出现这些句子。 - 相关记录:BUG-707、BUG-715、BUG-716、TASK-chart-page-blocking-open-20260915
- 复发自:无
- 修复版本:待发布
BUG-718 | 星盘页首屏 /api/chart 同步挂上 VedAstro 外部证据,页面却不读它
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
frontend/src/lib/chart-view-load.ts、GET /api/chart-view、/chart - 用户现象:打开星盘要等零点几秒到数秒;生产上缓存未命中时会同步等外部占星服务。盘面本身用的是本站 Swiss Ephemeris。
- 触发条件:登录后打开
/chart,出生资料齐全,引擎缓存未命中。 - 根因:
/api/chart默认会_attach_vedastro_main_entry_overview(完整 snapshot + 三领域扫描)。跳过要显式传skip_vedastro_main_entry_overview。BUG-161 只给/api/consult装了这个标志。星历页传了,2026-09-15 新建的星盘页没传。chart-view-mapper/chart-view-contract不读这份证据。 - 修复:
birthPayload()带上skip_vedastro_main_entry_overview: true,与星历页同一写法。不改引擎默认行为,解读链仍取外部证据。 - 验证:合同测试锁住星盘 BFF 与星历两处 natal/transit 请求都带该标志;删掉
birthPayload那一行测试必须红。 - 防复发:任何前台页面路由调
/api/chart必须显式声明外部证据策略,并有合同测试覆盖;新增页面路由时按此检查。 - 相关记录:BUG-161、BUG-065、BUG-715、BUG-716、BUG-717、ERR-107、ERR-108、TASK-chart-page-blocking-open-20260915
- 复发自:BUG-161
- 修复版本:待发布
BUG-719 | 官方 vedastro SDK 在 import 时联网自升级,运行期版本与 pin 不一致
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
vedastroPython 包、deploy/railway-api.Dockerfile、bridge 子进程 - 用户现象:镜像按
requirements.txt固定版本安装,进程一启动却去访问 pypi,并可能把包升到更新版本。 - 触发条件:
import vedastro(每次 spawnvedastro_python_bridge子进程都会发生)。 - 根因:
vedastro/update_check.py在导出符号前请求 pypi 并pip install --upgrade。包本身是 REST 客户端,不是本地计算库。 - 修复:镜像在
pip install之后运行deploy/patch_vedastro_update_check.py,把check_for_update改成 no-op;hook 不存在则构建失败。 - 验证:补丁单测(无网络)、Dockerfile 合同(install 之后必须跑 patch)、运行期版本等于 pin(本机若未装该包则 skip)。
- 防复发:第三方 SDK 若在 import 期联网或改写环境,必须在镜像层中和,并用测试锁住 pin。
- 相关记录:ERR-107、BUG-089
- 复发自:无
- 修复版本:待发布
BUG-720 | 无 key 时 VedAstro 免费层同步排队可堵住前台线程数分钟
- 状态:investigating
- 首次发现:2026-09-15
- 最近更新:2026-09-15
- 影响面:
scripts/vedastro_service_adapter.py的_acquire_free_tier_slot - 用户现象:外部证据路径可能卡住很久。是否在生产发生取决于
VEDASTRO_API_KEY是否真的有值,产品负责人尚未回填docs/testing/vedastro-runtime-20260915.md。 - 触发条件:命中官方公共 endpoint 且没有 API key;一次 full snapshot 约 24 个请求,免费层默认 5 个/分钟。
- 根因:名额耗尽时持进程级锁
time.sleep(),没有前台等待预算。 - 修复:增加
VEDASTRO_FREE_TIER_WAIT_BUDGET_SECONDS,默认不超过VEDASTRO_TIMEOUT_SECONDS。超出预算立即返回free_tier_rate_limited降级,不再无限等。生产是否仍会走到这条路径,等清单回填后更新本条。 - 验证:预算为 0 时第二次请求不 sleep、返回体可区分限流降级;既有「窗口内等待一次」回归仍绿。
- 防复发:前台同步路径不得无上限排队;不得靠调大免费层配额绕过第三方额度。
- 相关记录:ERR-108、BUG-065、BUG-161、BUG-718
- 复发自:无
- 修复版本:待发布
BUG-721 | 生时校正把候选分钟不变量放在事件循环里重复计算
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
scripts/active_rectification_event_engine.py、scripts/rectification/scoring_service.py、scripts/rectification/refinement_packet.py - 用户现象:每记一条证据或答一道题都要重算
POST /api/rectification/v5/score,等待明显偏长。不是回归,也不是结果算错。 - 触发条件:一次请求里有多个候选分钟,并且事件带 year/month 采样(采样日把候选×事件再放大)。
- 根因:
build_candidate_static_context引入后,排盘/分盘按候选分钟只算一次,但 Shadbala、Ashtakavarga、Vimshottari 时间轴、Narayana 周期表仍留在_candidate_row的事件循环里,被「候选 × 事件 × 采样日」三重放大。过境盘只依赖事件日期,却按候选分钟在最内层重算。build_refinement_packet在probe_times == grid_times时对_discriminating_event_probe_lists算两遍。scoring_service._cached_rows是加错层的死代码,生产入口从不走。 - 修复:把候选分钟不变量挂进 static context,过境盘在
compute_event_candidate_rows调用栈内用局部字典按日期缓存 chart(规则判定仍按候选算),探针在时间网格相同时复用一次结果;删掉_cached_rows。不改算法、权重、阈值、采样规则,也不修 static context 里 Shadbala 的birth_minute双算。 - 验证:基线
a8d29d1b真实跑出 golden(公开 1990-01-01 北京烟测盘 + 虚构事件);改后candidate_scores与剔除column_compare_ms的decision_receipt逐字相同。计数断言:calc_shadbala/calc_ashtakavarga/build_dasha_timeline/calc_narayana_mahadasha在引擎打分路径上各等于候选分钟数;过境compute_chart等于去重事件日期数;year 精度过境仍早退[];默认探针路径 1 次、refresh_probes且 refresh 列存在时 2 次。 - 防复发:新增的候选分钟不变量必须进 static context,不得留在
_candidate_row的事件循环里;新增的事件不变量不得按候选迭代。记忆化只允许请求内显式传递的 context / 局部字典,禁止模块级lru_cache跨请求持有出生资料派生数据。 - 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-722 | 意图分类器两次异常被说成用户没说清,经历被丢掉
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
classifyTurnIntentWithRetry、POST /api/rectification/agent点选题与ask_candidate_discriminator分支 - 用户现象:点选题下回一句带年月的经历,助手说「我不太确定这句是不是在回答上面的问题」。焦点仍在,但这句话没有当成经历记下,用户只能自己再说一遍。
- 触发条件:
action=message且当前焦点是点选题;意图分类器两次尝试都抛异常(超时、供应商 5xx、网络抖动),或两次都解析不出结构化结果。 - 根因:
classified === null(模型没答上来)与classified.intent === "unclear"(用户确实说不清)被合并进同一条分支,回unclearFocusReply并落库。采集题分支在 BUG-643 已把 null 记成collectIntent=unclassified并 fail-open;点选题没有跟上。 - 修复:
classifyTurnIntentWithRetry增加outcome: classified | unclear | classifier_unavailable。点选题与 discriminator 分支:unclear维持原回复;classifier_unavailable回「这边没接上,把刚才那句再发一次就行。」,不写证据、不推进焦点、不扣点。服务端打rectification_classifier_unavailable日志(只含耗时与次数,不含原文或案例 ID)。采集题collectIntent=unclassified语义不变。不得用关键词/正则兜底。 - 验证:
frontend/tests/rectification-turn-intent-classifier.test.ts:两次抛异常 →outcome=classifier_unavailable;模型返回intent: unclear→outcome=unclear;两条路由文案不同。源码合同禁止!classified || classified.intent === "unclear"。 - 防复发:路由不得把分类器 null 与用户
unclear合并。分类器失败不得回退到关键词、正则或词表。 - 相关记录:BUG-643、BUG-635、BUG-522
- 复发自:无(BUG-522 记录末尾写明「意图分类器超时仍是既有缺口,本单不修」,本单关闭该缺口)
- 修复版本:待发布
BUG-723 | 校正引擎 429 被当成引擎坏了,重算静默失败、范围不动
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
engine-client.tsreadEngineJson、runV9CandidateScore/runV9BlockScan、autoRescoreAfterEvidenceChange、证据轮主持人正文 - 用户现象:记下经历后助手写「记下了:…」,范围一动不动。没有「这次没有重新比较」之类的说明。只在两个人同时校正、重计算闸门饱和时出现。
- 触发条件:
record-evidence-batch已 accepted 后自动重算;Python 返回 429 +Retry-After+ERR_COMPUTE_BUSY。 - 根因:所有非 2xx 压成
engine_request_failed。autoRescoreAfterEvidenceChange整段try/catch返回status=failed且不打日志;系统提示词要求工具静默,模型继续写「记下了」。 - 修复:引擎调用按
busy / http_error / timeout / bad_payload分档,读Retry-After,每种非 ok 打console.warn(路径、状态码或错误名、耗时;不含出生资料)。score / block_scan 对busy按Retry-After退避,上限RECTIFICATION_ENGINE_BUSY_RETRY_LIMIT=2、总退避RECTIFICATION_ENGINE_BUSY_BACKOFF_BUDGET_MS=6_000。仍失败时 projection 带回error_kind,主持人正文写「这次没有重新比较」,并去掉「收窄」类进度句。不改并发闸门。 - 验证:
frontend/tests/rectification-v9-engine-contract.test.ts桩 429 / 500 / 超时 / 坏 JSON;只有 429 触发退避;退避成功后 score 与从未失败路径相同。rectification-v9-stream.test.ts重算失败正文含「这次没有重新比较」、不含「收窄」。 - 防复发:任何调用 Python 引擎的客户端都不得把非 2xx 压平成单一错误码;429 必须单独成档。引擎调用不得用
catch {}吞掉原因,失败必须留服务端日志且用户文案按原因分档。 - 相关记录:BUG-715
- 复发自:BUG-715(那一单的范围写死在星盘页三个文件,防复发没有扫到
engine-client.ts) - 修复版本:待发布
BUG-724 | 两次模型尝试总预算大于路由 maxDuration,重试时无错误码断流
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
runV9AgentTurn、RECTIFICATION_RUN_BUDGET_MS、agent / regenerate 路由maxDuration - 用户现象:发生一次可重试失败(
empty_stream、evidence_not_written)后,用户等三五分钟拿到一个连错误码都没有的断流。 - 触发条件:单次 attempt 210s × 2 = 420s,路由
maxDuration240s。 - 根因:BUG-388 只约束「单次 attempt < maxDuration」,没约束两次尝试总预算。
RECTIFICATION_AGENT_ATTEMPT_TIMEOUT_MS = 210_000还在agent-run.ts与rectification-activity-labels.ts各写一遍。 - 修复:引入整轮
RECTIFICATION_RUN_BUDGET_MS=225_000(小于 240s)。单次超时取min(210s, 剩余预算)。重试还要剩余预算 ≥ RECTIFICATION_MIN_RETRY_ATTEMPT_MS(20s),否则走 host fallback 给出可见正文,不启动注定被砍的第二次尝试。210_000 只在rectification-run-budget.ts定义一处。不把 attempt 砍到 110s,不把maxDuration提到 430s。已盖戳open_question的超时轮保持现状 fail-closed(见既有 stream 测试),不把题干贴进超时回复。 - 验证:
RECTIFICATION_RUN_BUDGET_MS < maxDuration,maxDuration从 agent 与 regenerate 路由源码读取。第一次尝试耗尽预算后返回 retryable,不得发起第二次尝试,必须 host fallback。源码合同:src/里只有一处210_000。 - 防复发:两次模型尝试的总预算必须由测试断言钉死为小于路由
maxDuration,不能只约束单次 attempt。attempt 超时常量只能有一处定义。 - 相关记录:BUG-059、BUG-388
- 复发自:BUG-059(总预算约束);BUG-388 的防复发只写了单次尝试,因此没拦住
- 修复版本:待发布
BUG-725 | 校正会话流式时整条对话每帧重建,已结算消息每帧重跑 Markdown
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
rectification-agentic-chat.tsx消息列表、chat-message-content.tsx结算态 Markdown、chat-message-row.tsx - 用户现象:生时校正会话越长越卡,尤其在正在写出结果的那一轮。历史气泡跟着直播行一起抖。
- 触发条件:校正会话已有十余轮结算消息,再发一条会触发长流式回答。
- 根因:BUG-473 在本文件落地了
stream-frame-buffer(每帧最多提交一次),但校正面没有咨询面那套「已结算历史 / 正在流的那一条」记忆化边界。messages.map内联派生时间轴、varga 句、选择题卡和afterAnswer;ChatMessageRow无memo;结算态走renderProse全量 parse。每帧setMessages只替换直播行对象,但整张列表仍重渲,已结算行连同 Markdown 全部重算。同类前史:BUG-249(巨型容器每次击键全量重渲)。 - 修复:结算态 Markdown 复用
StableMarkdownPrefix(memo+ 按文本的解析缓存);抽出RectificationMessageEntry(memo),把逐消息派生搬进组件,回调经actionsRef更新.current;ChatMessageRow同样memo。不把流式文本搬出messages,不启用 React Compiler,不用React.lazy+Suspense承载 Markdown。 - 验证:
frontend/tests/rectification-settled-render-split.test.ts(源码合同 + 按帧渲染计数:已结算行 4 次不随 5 个流式帧增长,未拆分对照 25 次);chat-markdown-split.test.ts新增「同一段文本连续渲染两次,解析器只调用一次」(改前 2 次,改后 1 次)。改前该计数测试因模块不存在而红。咨询面既有home-streaming-render-split/chat-markdown-split/stream-frame-buffer无断言改动且全绿。 - 防复发:任何流式会话面都必须为已结算消息保留记忆化边界,并由一条按帧驱动的渲染计数断言钉死;新增会话面必须同时补这条断言。流式状态仍须经
stream-frame-buffer提交;流式期间 Markdown 仍走前缀/尾块切分;结算态不得再走无缓存的整篇 parse。 - 相关记录:BUG-473、BUG-249、BUG-250、BUG-260、BUG-617
- 复发自:BUG-473(影响面含本文件,但两条防复发只约束正在流的那一行)
- 修复版本:待发布
BUG-726 | 一轮校正把同一份 Case 档案从数据库取多次
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
loadV9CaseDossier/loadV9CaseCompute、POST /api/rectification/agent、regenerate、Case GET/POST - 用户现象:同一轮对话里档案被重复取回。用户看不到这条,但 2 vCPU 主机上这份 jsonb 投影与 Next.js、Python 引擎抢同一批核。
- 触发条件:一次 HTTP 请求内多处调用
get_agentic_rectification_case_dossier或get_agentic_rectification_case_compute,中间没有写操作。 - 根因:只读投影没有任何请求作用域缓存。每个调用点独立打 Postgres RPC。这不是回归,是 V9 运行时引入以来的分层遗漏。
- 修复:新增
withRectificationRequestCache,挂在客户端对象上。两个只读投影按(fn, p_user_id, p_case_id)缓存 in-flight promise;其它 RPC 与.from(...)一律先清空再转发。生命周期等于一次请求。路由入口各包一处,零调用点改动,不跨请求缓存。 - 验证:
frontend/tests/rectification-request-dossier-cache.test.ts:连续两次读只打一次底层;中间其它 RPC 后必须重读;不同 caseId 互不命中;并发读合并;.from透传后缓存清空;五条路由源码合同。既有 stream 50 / answer-choice 34 一条不改仍全绿。路由预取 + 证据轮case_dossier5→4。 - 防复发:Case 只读投影必须经请求作用域缓存读取;新增只读投影要么进缓存白名单,要么在记录里写明为什么不能缓存。不得把档案挂在模块作用域或
globalThis。 - 相关记录:BUG-176
- 复发自:无
- 修复版本:待发布
BUG-727 | 普通聊天每轮每域同步等外网,超时不取消导致线程池饱和
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
POST /api/consultation_workflow前台路径、execute_consultation_workflow、vedastro_gateway、前台 VedAstro 线程池 - 用户现象:普通聊天每发一条、每个问题域都要等外网;本地排盘只占约 3%。超时后技法表 VedAstro 云状态仍是 blocked,之后每一轮继续白等约 1.5 秒。
- 触发条件:网页咨询
defer_optional_external_evidence: true;同一张盘一天内多轮提问;前台线程池默认 2 个 worker。 - 根因:外网证据本就按「盘 + UTC 日期」组织,却没有按这个键缓存,每轮重新 TLS 握手打
api.vedastro.org。_join_foreground_vedastro超时返回official_blocked但不cancel()后台任务,任务继续跑满预算;join 1.5 s、budget 8 s、workers 2 三个数不在同一处约束,平均每 4 秒一轮就把池子占死。 - 修复:新增独立模块
scripts/vedastro_snapshot_cache.py(目录与 key 函数都与_api_chart_cache分开)。同日命中直接用、零等待、不 submit;1–7 天前先用旧的并后台刷新;entrypoint=daily_starlanguage只接受当天。超时必须future.cancel();join / budget / workers 成组声明,且budget ≤ 2 × join。本单不是推翻 BUG-301:前台仍取官方层,只是用缓存去掉重复等待。 - 验证:
tests/test_vedastro_snapshot_cache.py(同日第二次零网关调用、跨日 stale+刷新不等待、daily_starlanguage 不吃昨天、缓存目录≠chart 缓存、N+1 等待不超过 join、三常数同块且 budget 比例、超时取消排队任务);既有前台赶上/超时降级回归。 - 防复发:前台外部证据必须有「盘 + 日期」级缓存;任何有界等待都必须同时取消它等待的后台任务。
- 相关记录:BUG-161、BUG-301、BUG-718
- 复发自:无(BUG-301 是故意把 VedAstro 放回前台,本单用缓存同时满足 161 与 301)
- 修复版本:待发布
BUG-728 | western_evidence_packet 无人读却每轮每域传约 122 KB
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
POST /api/consultation_workflow响应体、consumer_context.western_spectrum - 用户现象:一轮咨询响应约 52 万字符,其中西洋证据整包约 122 KB;前端解析后丢掉。三个域就是约 366 KB 无效 JSON。
- 触发条件:普通聊天每个问题域调用
/api/consultation_workflow。 - 根因:西洋盘计算是 must-use 层,但整包被无条件放进前台响应。
frontend/src零读取点;被读的是consumer_context.western_spectrum压缩投影。 - 修复:默认不把
western_evidence_packet放进咨询工作流响应,计算与western_spectrum仍保留。MCP、高严谨、显式include_western_evidence_packet/western_oracle_payload仍返回整包。consultationWorkflowResponseSchema为.passthrough(),去掉该键仍能解析。 - 验证:
tests/test_vedastro_snapshot_cache.py默认省略、显式请求保留、oracle payload 仍返回;frontend/tests/consultation-workflow-contract.test.ts无该键仍safeParse成功。 - 防复发:工作流响应新增大字段前必须有读取点;没有读取点的字段不得进入前台响应。
- 相关记录:BUG-727
- 复发自:无
- 修复版本:待发布
BUG-729 | 咨询历史丢掉整轮时模型看不见任何痕迹
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
consultationHistoryWindow、consultationUserTurnContent、POST /api/consult - 用户现象:历史超过模型预算后,追问「刚才你说的那个时间」时模型当成从没说过。单条超长会写「省略 N 字」,整轮被丢掉时什么都不留。
- 触发条件:普通咨询多轮之后,尾巴字符数超过当前模型的历史预算,窗口从最旧整条丢弃。
- 根因:
droppedCount算出来了,route.ts只取.tail。BUG-555 的防复发只写了「不得再按固定 12 条 × 头部截断静默砍结论」,整轮丢弃不在字面里,所以没拦住。 - 修复:
droppedCount > 0时在摘要槽追加与omissionMarker同风格的说明。有摘要时写「更早的 N 轮问答已并入上面的会话摘要」;没有摘要时诚实写结论尚未并入。droppedCount === 0不加这句话。 - 验证:超预算历史的模型可见文本含丢弃说明且轮数等于
droppedCount;零丢弃不加这句话;源码合同断言route.ts读取historyWindow.droppedCount。 - 防复发:咨询历史任何形式的丢弃(截断单条、丢整轮)都必须在模型可见文本里留痕。
- 相关记录:BUG-555
- 复发自:BUG-555(防复发只覆盖头部截断)
- 修复版本:待发布
BUG-730 | 写摘要的阈值写死 16,000,追不上按窗口算出的历史预算
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
historyBudgetChars、consultationHistoryCheckpointChars、shouldCheckpoint、checkpointSessionContextSummary - 用户现象:后台上架中等上下文窗口的模型后,每轮静默丢掉最老的几轮问答,摘要却还没开始写。
- 触发条件:模型
context_window低于约 70,667(例如 64k / 32k)。历史预算已经小于写死的 16,000 字阈值。 - 根因:
shouldCheckpoint用常量 16,000,consultationHistoryWindow用historyBudgetChars()。两个数各写各的,没有「阈值必须低于预算」的断言。 - 修复:阈值改为预算的 0.4(默认 128k 窗口仍是 16,000)。运行时断言阈值 < 预算。检查点把会话模型的
contextWindow传进去。 - 验证:表驱动覆盖 200k / 128k / 64k / 32k / null,逐条
checkpoint < budget;128k 仍为 16,000。 - 防复发:触发摘要的阈值必须由历史预算派生,并由一条断言钉死「阈值 < 预算」在所有合法上下文窗口下成立。
- 相关记录:BUG-555
- 复发自:BUG-555(检查点阈值与窗口预算未绑在一起)
- 修复版本:待发布
BUG-731 | 对话写满后开新对话不继承服务端已有的会话摘要
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
chatSessionCreateSchema、POST /api/sessions、continueInNewChat、startNewChat - 用户现象:这段对话已写满、点「开新对话」之后,模型对刚才的结论一无所知,用户被要求从零开始。
- 触发条件:
append_consultation_question返回session_full,客户端走「开新对话」。 - 根因:新会话是空的。
context_summary不在列表 GET 列里,创建合同也不接收它。即便前端想带,也没有合法入口。 - 修复:创建合同增加可选
continued_from_session_id(保持.strict(),不加context_summary)。服务端按当前用户读源会话摘要,读到才写入新行;读不到、不属于该用户、或为空都静默跳过,仍返回 201。写满出口把当前会话 id 带进创建请求。界面不加「接着上次聊」之类提示。 - 验证:带来源 id 时新行摘要等于源会话;源会话属于别人时新行无摘要且 201;源码合同断言创建 schema 没有
context_summary字段;写满后「开新对话」没有新增提示文案。 - 防复发:会话满员后的「开新对话」出口必须由服务端继承
context_summary;摘要文本任何时候都不得由客户端提供。不得把messages加回列表 GET。 - 相关记录:BUG-464、BUG-555
- 复发自:无
- 修复版本:待发布
BUG-732 | 对话额度把思考文本算进去,十几轮就「已写满」
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
append_consultation_question、POST /api/consult的session_full、咨询会话详情GET /api/sessions/[id] - 用户现象:普通咨询问大约十几轮就提示「这段对话已写满,开个新对话继续吧」。200 条消息那档永远碰不到。
- 触发条件:继续往同一段咨询会话里发问;助手消息带有
thinkingText/thinkingSections。 - 根因:BUG-464 立下的 200,000 字符上限身兼二职却两职都没做好。求和把用户读不到的
thinkingText、thinkingSections算进去,却不算同样入库的techniqueTruth/workflowReceipt/agentExecutionReceipt。这不是回归,是那条上限从一开始就混用了「对话有多长」和「这一行有多大」。 - 修复:新迁移
CREATE OR REPLACE该函数。对话额度仍是 200,000,只累加elem->>'text'。另加物理上限sum(length(elem::text)),算式 50 轮 ×(正文约 4,000 + 思考 4,000 + 分节 3,000 + 三个 receipt 约 3,000)≈ 700,000,取 1,000,000。两档都返回既有session_full。签名、返回列、error_code、advisory lock、request_id幂等、200 条上限、单条 16,000 字校验均未改。 - 验证:
frontend/tests/consultation-session-capacity.test.ts锁定额度求和不含thinkingText/thinkingSections、物理上限算式、签名与session_full。frontend/tests/database-consultation-session-capacity.test.ts用真实 Postgres 覆盖思考不占额度、短正文+大 receipt 撞物理上限、额度边界不先撞物理上限;本机无 Docker,该文件 skip,不得写成通过。既有幂等 / 满员用例未改。 - 防复发:会话上限必须分成两条各司其职的口径:面向用户的对话额度只数用户读得到的正文;面向存储的物理上限必须把整条消息 JSON 算全。新增会存进
messages的字段时,必须明确它进哪一条,不得默认落进对话额度。 - 相关记录:BUG-464
- 复发自:无
- 修复版本:待发布
BUG-733 | 校正记忆化 golden 对全精度浮点做整体 ==,跨机门禁靠运气绿
- 状态:resolved
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
tests/test_rectification_engine_memoization.py、tests/golden/rectification_engine_memoization_v1.json(文件未改) - 用户现象:用户看不见。门禁 glob
tests/test_rectification_*.py在CORE_PYTEST_TARGETS里,换一台机器或基础镜像会因score/margin_percent尾数差 1.0e-4~1.1e-3 而红,CI 绿只是碰巧和生成 golden 的那台舍入一致。 - 触发条件:
test_score_candidates_matches_baseline_golden把 live payload 与 golden 做整体==。 - 根因:golden 存了全精度浮点并整体相等。这是 BUG-712 的同一形状。BUG-712 的防复发只落在
ephemeris_events那一处(量化到 6 位再比),没有仓库级守卫,BUG-721 新写的 rectification golden 重蹈覆辙。 - 修复:只改测试,不动
scripts/。主证据改成同进程差分:同一批 static contexts,A 带四层缓存、B 把ashtakavarga_result/shadbala_result/vimshottari_timeline/narayana_periods置None,compute_event_candidate_rows输出严格相等。golden 改成分档比较:离散字段严格相等;浮点用实测最大漂移 1.1e-3 推出的容差(绝对 2e-3 与相对 5e-4 取更宽者)。禁止调用write_golden()更新那份 JSON。 - 验证:同进程 A/B 逐字相等,且 B 的
calc_shadbala次数显著高于 A(本机 6 vs 0)。反向:把某一层缓存换成全零 shadbala 对象后不再相等。golden 分档比较通过;把 golden 浮点改 1e-2 必须红;把离散字段time改掉必须红。pytest tests/test_rectification_engine_memoization.py与pytest tests/test_rectification_*.py0 failed。golden JSONgit diff无改动。 - 2026-09-16:
decision_receipt新增guided_collect_windows后重生成 golden,diff 仅此一键(62 行),未放宽_assert_tiered_equal、未调用write_golden();同时把_golden_payload()的date.today()冻结,见 BUG-747。 - 防复发:任何 golden 比较都不得对浮点做整体
==;浮点必须量化或带容差,容差数值要有实测依据并写在注释里;能在同进程内做差分证明的命题,不得用跨机 golden 代替。 - 相关记录:BUG-712、BUG-721
- 复发自:BUG-712
- 修复版本:待发布
BUG-734 | 每轮聊天用一次完整业务请求做健康探测,吃光对方限流额度还不复用连接
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
scripts/vedastro_gateway.py的probe_official_rest_health/gateway_status/run_gateway_packet;经run_gateway_packet的全部前台路径(professional_reading、rectification_gate、/vedastro/gateway/run) - 用户现象:BUG-727 的快照缓存每轮都命中、响应体从 52 万字符降到 40 万,耗时却几乎没变。逐轮 trace 显示快照命中的轮次仍然多打一次外网,每轮多等约 1 秒。连续提问到第 6 轮时技法表的 VedAstro 云状态会毫无道理地变成 blocked。
- 触发条件:任何经
run_gateway_packet的前台请求;同一分钟内连续 6 轮即可复现假official_blocked。 - 根因:三件事叠在一起。(1)
probe_official_rest_health不是 ping——它 POST 一份虚构 smoke 排盘到官方HoroscopePredictions,拿返回的 Status 当健康信号,本机实测单次 1,034 ms;(2)它经call()且没传skip_rate_limit,每轮都吃掉对方每分钟 5 个限流令牌之一,于是第 6 轮探测被本地限流器挡下、反过来报告服务不可用,真正的业务调用还要和它抢令牌;(3)探测结论没有任何 TTL,而「这个外部服务此刻可用吗」不会每 500 毫秒变一次。这是 BUG-721 同一形状的又一个实例:只依赖静态输入的昂贵结果被放在每请求都走的路径上。BUG-727 只覆盖了快照,没覆盖探测。 - 修复:探测主体逻辑一字未改地挪进
_probe_official_rest_health_uncached();probe_official_rest_health/gateway_status增加force_refresh形参并加进程级 TTL 缓存,run_gateway_packet显式走force_refresh=False。缓存键是影响探测结论的有效配置(mode、official endpoint、self-host endpoint、network enabled、是否配了 API key、JYOTISH_SKIP_LOCAL_ENV),配置一变立刻失效;只记 key 配没配的布尔,绝不存 key 的值。成功 TTL 60 秒、失败 TTL 10 秒(失败必须显著更短,否则对方恢复了还要再瞎等一分钟)。探测在锁外执行,不在等外网时按住进程级锁。force_refresh默认True:诊断端点_compute_vedastro_gateway_status调的是裸gateway_status()且该文件本轮不得改(AGENTS §6),默认实时才能保证运维不会看到一个 60 秒前的假象。连接复用这一半未做——实测证明urllib.request没有连接池,模块级共享 opener 在 6 次请求下仍开 6 条 TCP 连接(AbstractHTTPHandler.do_open每次新建并强制发Connection: close),按任务书设想实现只能通过「是同一个 opener 实例」的断言而省不掉任何握手;真正的连接池需要改所有业务调用共用的call()或新增依赖,另立单。 - 验证:
tests/test_vedastro_health_probe_cache.py14 条——连调 3 次只探测 1 次、推过 TTL 后变 2 次、失败 TTL 过后成功 TTL 之内服务恢复能被看见、失败 TTL < 成功 TTL、配 API key 或换 endpoint 立刻失效、缓存键不含 key 值、network disabled 不碰外网、force_refresh=True每次真探测且默认值被inspect.signature钉死、run_gateway_packet必须传force_refresh=False、返回值被调用方改写不污染缓存、探测期间锁可获取。同口径实测(连续 6 轮前台请求):探测 6 → 1 次,TLS 握手 5 → 1 次,消耗对方限流令牌 6 → 1 个;改前第 6 轮健康结论为假official_blocked,改后 6 轮全部official_verified。既有tests/test_vedastro_gateway.py、tests/test_vedastro_runtime_ops.py断言一条未改仍绿。 - 防复发:前台路径上的外部服务探测必须有 TTL,且不得消耗业务限流额度;失败 TTL 必须显著短于成功 TTL;诊断端点必须保持实时,缓存只能加在前台那条路上。健康探测不得用一次完整业务调用充当。探测与业务调用共用连接这一条仍未落地——在引入带连接池的客户端之前,每次外呼仍各付一次 TLS 握手(本机实测 207 ms)。
- 相关记录:BUG-727(快照缓存,只修了快照没覆盖探测)、BUG-161、BUG-301(前台外部证据的两条既有红线)、BUG-721(同一形状)、BUG-718(不得持锁等外网)
- 复发自:无
- 修复版本:待发布
BUG-735 | 静态规则表达式每个请求重新编译 386 次
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
scripts/yoga_engine.py的_eval_custom;所有走 yoga 判定的路径(普通咨询、报告、校正打分) - 用户现象:用户看不见错误结果,只感到慢。单请求 cProfile 里运行时 AST 编译占 9.4%,而真正的占星计算
swisseph.calc_ut只占 2.4%。 - 触发条件:任何一次 yoga 检测。
- 根因:
_eval_custom拿到的是源码字符串cond["expr"],每次调用都重新编译:先eval(expr)(CPython 在内部编译字符串),多语句表达式在这一步抛SyntaxError、被捕获后再ast.parse→ 改写末尾表达式 →compile→exec。表达式全部来自仓库内的静态规则表references/yoga_rules.json(198 条expr、去重 192 条、最长 2,614 字符、其中 103 条是多语句),在进程生命周期内不会变。本机实测每次检测 386 次源码编译(182 次eval字符串 + 204 次builtins.compile)。这是 BUG-721 / BUG-734 的同一形状:只依赖静态输入的昂贵结果被放在每请求路径上。 - 修复:
_eval_custom拆成「造求值命名空间」与「求值」两段。_build_custom_exec_globals每次调用都新建exec_globals(它绑定当前这张盘的ctx)。新增模块级_compiled_eval_code/_compiled_exec_code,各带functools.lru_cache(maxsize=512),缓存的是 code object,不是求值结果;_run_custom_expr不带任何缓存装饰器。语义与改前逐条对齐:先表达式模式、编译期SyntaxError退到改写后的 exec 模式、两条路异常仍全吞结果为None、ast.parse仍对expr.strip()、运行期SyntaxError也仍退到 exec 分支。不改任何 yoga 规则内容、判定口径或置信度边界。 - 验证:
tests/test_yoga_expr_compile_cache.py9 条。等价用同进程差分(BUG-733 立下的做法,不写跨机 golden):参照实现逐字复刻改前代码,规则表全部 192 条去重表达式 × 3 张公开虚构示例盘 = 576 次逐条同值,并有反向验证证明该差分不是恒真。红线断言:缓存里是types.CodeType;同一条house_of('Sun')在两张盘上必须得到 10 与 5 两个不同答案(表达式与多语句两条路都验);_run_custom_expr无cache_info;maxsize有界且 ≥ 去重表达式数。来源合同:规则表在仓库内、全仓cond.get("expr"仅 1 个读取点、生产入口jyotish_engine.py/jyotish_api_server.py的detect_yogas(不传rules_path——未发现任何用户输入能进入expr。同口径实测:每次检测源码编译 386 → 第二次起 0,稳态单次检测 27.4 ms → 3.3 ms,命中 yoga 数三轮均为 70 不变。tests/test_yoga_rules_integrity.py、tests/test_yoga_benchmark_cases.py、tests/test_lakshmi_yoga.py仍绿。 - 防复发:
eval/exec的入参若来自静态规则表,必须缓存编译产物;缓存 code object,永远不缓存求值结果——缓存结果会让所有人拿到同一张盘的 yoga 判定,后果比性能问题严重得多。编译缓存必须有界,且必须有断言证明expr只来自仓库内的静态规则表;若哪天有用户输入能进入expr,立刻停手按安全问题处理。 - 相关记录:BUG-721(同一形状的第三个实例)、BUG-734(同一轮的另一处)、BUG-733(同进程差分怎么写)
- 复发自:无
- 修复版本:待发布
BUG-736 | 纯前端改动打红 Python 源码合同,快速门连续两轮带红合入
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
tests/test_supabase_user_data_contract.py、scripts/run_quality_gate.py --profile quick、frontend/src/app/api/sessions/route.ts、frontend/src/hooks/use-synastry.ts - 用户现象:无(门禁问题,用户不可感知)。
- 触发条件:纯前端轮次移动了
page.tsx/ 路由里的字面量或把逻辑搬进新 hook。 - 根因:
test_supabase_user_data_contract.py用正则扫前端源码,_home_surface()只拼OPTIONAL_HOME_HOOKS白名单里的文件。两轮改动各打红一批断言:149e1ec4把创建路由改走chatSessionCreateInsertRow(...),user_id: user.id字面量消失;状态下沉第二批把合盘搬进新建的use-synastry.ts,它不在白名单里,于是fetch("/api/synastry"与未能存入历史在并集里找不到。三条都是断言过期,归属校验与云端持久化保证本身没丢。 - 为什么没被拦住:这几条是 Python 测试,但断言对象是前端源码。前端轮次的验收口径(tsc / lint /
npm test/next build)不含 Python 快速门,而 AGENTS §10 只要求「任何 Python 改动」跑快速门——纯前端改动落在两张网中间。验收方(Claude)两轮都只跑了前端套件,未跑快速门,因此带红合入。 - 修复:
OPTIONAL_HOME_HOOKS补use-synastry.ts并加注释说明新增 Home 级 hook 必须同步登记;user_id: user.id断言按「原值 / 新值 / 原因」三栏改成userId: user.id(路由)+user_id: input.userId(chat-session-write-contract.ts),两处都断,不放宽。 - 验证:
.venv/bin/python -m pytest tests/test_supabase_user_data_contract.py8 条全绿;run_quality_gate.py --profile quick的 pytest 段 792 passed / 1 skipped / 0 failed(此前 3 failed)。快速门整体仍退出 1,剩余唯一原因是本机无rsync与无 Docker 的既有环境缺口。 - 防复发:改动
page.tsx、Home 级 hook 或frontend/src/app/api/**的轮次,验收必须另跑run_quality_gate.py --profile quick,不能只跑前端套件——有 Python 测试把前端源码当断言对象。新增 Home 级 hook 必须同步登记进OPTIONAL_HOME_HOOKS。取门禁退出码不得隔着管道(| tail会把退出码换成tail的)。 - 相关记录:BUG-729~731(创建路由改造)、BUG-464(会话持久化归属)
- 复发自:无
- 修复版本:待发布
BUG-737 | --font-display 里没有一个字体能加载,中文标题全站落到宋体
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-28
- 影响面:
frontend/src/app/globals.css(--font-display/--font-body)、frontend/src/app/admin/admin.css、frontend/src/components/admin/admin-app.tsx;波及 20 条使用var(--font-display)的规则 - 用户现象:品牌字、顶栏会话标题、首页大标题、入口卡标题、助手回答正文里的
h2/h3、所有弹窗标题、模型选择器标题、登录页大标题,在真实设备上以宋体渲染——笔画细、小字号发虚,与 Inter 正文的字重对不上。 - 触发条件:任何用户、任何页面,一直如此。macOS / iOS 命中
Songti SC;Windows 拉丁走Georgia、中文掉到浏览器默认 serif(SimSun);多数 Android 无Noto Serif CJK SC,同样落到系统默认 serif。 - 根因:
--font-display的值是"Tiempos Headline", "Songti SC", STSong, "Noto Serif CJK SC", Georgia, serif,--font-body以StyreneB开头。这两个是 Anthropic 授权字体,本仓从未加载过它们:git grep "@font-face" frontend/src无命中,frontend/public/下没有字体文件,layout.tsx只用next/font/localvendor 了InterVariable-latin.woff2。栈首两个名字永远不命中,实际生效的是它们后面的中文衬线回退。 更上一层的原因:frontend/CLAUDE_DESIGN.md扒的是 claude.com 营销官网,该文件自己在## Known Gaps写明 claude.ai 产品界面不在范围内。它的「display 用 Copernicus 衬线 400」是拉丁 display face 的规则,被DESIGN.md当成实现契约照抄到中文界面,就成了宋体。 - 为什么没被拦住:没有任何测试或构建检查会验证「声明的 family 是否真的能加载」。相反,
DESIGN.md§3 把这个栈记录成既定事实,personal-report-theme-contract与consultation-entrypoint两个合同测试还把「--font-display+font-weight: 400」锁成了设计契约——等于把 bug 写进了断言。 - 修复:display 与 body 合并为同一条无衬线栈(
var(--font-inter, Inter)→ 系统 →PingFang SC/Microsoft YaHei);display 层级改由字重承担,33 条 display 规则的font-weight: 400抬到500。StyreneB从--font-body、admin.css、admin-app.tsx一并删除。 拉丁衬线方案被实测否决:Newsreader 拉丁可变子集 132 KB(是整个 Inter 文件的 2.7 倍),只为给一个品牌字上衬线;且会把「D10 事业盘怎么读」这类混排标题劈成两种字形,读起来像字体加载失败。CJK 不用衬线是本条的结论。 - 验证:
git grep -nE "Tiempos|StyreneB|Songti|STSong" frontend/src除解释性注释外无命中;tsc --noEmit0 错;npm run lint0 error / 118 warning(与基线同);npm test3346 条、pass 3300 / fail 31,失败清单与基线逐条 diff 一致(均为无 Docker);next build后/仍○ Static,产物 CSS gzip 41,095 → 41,096 字节(+0.002%)。 - 防复发:新增
frontend/tests/font-stack-loadable-contract.test.ts——断言--font-display/--font-body里每一个带引号的 family 都必须在layout.tsx里有对应的next/font/local声明,否则必须属于系统字体白名单。声明一个加载不到的 family 会直接打红。 另:frontend/CLAUDE_DESIGN.md是营销官网的设计系统,不是产品界面的实现契约;引用它之前先读它自己的## Known Gaps。 - 2026-09-28 追加:产品决定标题改用自托管宋体(TASK-serif-headings-20260928),本条「CJK 不用衬线」的结论被推翻;「声明的字体必须可加载」与「不得落到系统宋体」两条防复发保留。实现:
--font-display首位为自托管的"Jyotisha Serif SC"(Noto Serif SC SemiBold 按 6500 字 unicode-range 切片),其后与--font-body完全相同的无衬线栈;font-stack-loadable-contract把「可加载」扩展到serif-sc.css里声明且文件存在的@font-face,原「no CJK serif is reachable」断言替换为「首位是 Jyotisha Serif SC、栈里不含 Songti / STSong / SimSun / Noto Serif CJK SC、不以通用 serif 结尾」。 - 相关记录:BUG-738(同轮的强调色分阶)
- 复发自:无
- 修复版本:待发布
BUG-738 | 亮暗强调色不同源,且 --color-ring 在 @theme inline 里硬编码不跟随
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
frontend/src/app/globals.css的四处亮色声明(@theme inline的--color-ring、:root、@media print里的:root)、frontend/DESIGN.md - 用户现象:亮色下强调色是深棕
#85432f,暗色下已经是 coral#d78064,同一个产品两套观感。 - 触发条件:切换明暗主题。
- 根因:暗色块早就带着注释「
#85432fis too dark to read as an action on」自行改用了 coral,亮色没跟。另有一处更隐蔽的:--color-ring写在@theme inline块里,是硬编码十六进制而不是var(--color-focus),所以它不跟随:root——任何改强调色的轮次只要没单独改它,就会出现「按钮变了、Tailwindring-*的焦点环没变」。 - 修复:产品拍板亮色换 Claude coral
#cc785c。实测发现它不能直接当文字色:对#fbfaf7画布只有 3.14:1,而 62 个调用点里 53 个是color:(含回答里的 markdown 链接)。因此按角色拆成两阶——--color-action: #a9583e(文字与图标,4.85:1)与--color-action-strong: #cc785c(填充、导轨、焦点环、描边,适用 3:1 非文字阈值)。--color-ring改为var(--color-focus);@media print里的第四处:root同步;暗色两块补上--color-action-strong(与--color-action同值#d78064,暗底上一阶就够)。 - 已知缺口(产品已知悉并接受):白字落在
#cc785c填充上是 3.28:1,低于正文 AA。涉及.button-primary与两个头像圆。claude.ai 本身就是这个值,而本轮的目标正是「像 Claude」。正文不受影响——那正是--color-action这一阶要保护的。 - 验证:
git grep -n "#85432f" frontend/src/app/globals.css只剩--report-accent两处(刻意保留,见下);对比度按 WCAG 相对亮度公式手算并记入进度记录;套件与基线逐条一致。 - 防复发:
design-token-contract.test.ts新增断言--color-ring: var(--color-focus),并把两阶 token 的顺序锁进:root正则。改强调色时漏掉@theme inline那处会直接打红。 - 相关记录:BUG-737(同轮);
--report-accent保持#85432f是刻意的——报告是打印纸面,不跟应用强调色走,见DESIGN.md§2 末尾的说明,不要「修」这个不一致。 - 复发自:无
- 修复版本:待发布
BUG-698 | 设置弹窗尺寸随分区变化:dvh 无回退,且仓库唯一那处回退被压缩器吃掉
- 状态:investigating
- 首次发现:2026-09-15
- 最近更新:2026-09-16
- 影响面:
frontend/src/app/globals.css(.settings-modal/.account-modal及全文件 8 处height: …dvh)、设置弹窗四分区、登录页、后台外壳、移动端侧栏 - 用户现象:「设置面板的弹窗大小不一样,选别的就立马缩小了。」宽度没动,高度在跳。
- 触发条件:登录后打开设置弹窗,在个人资料 / 星盘资料 / 账户与点数 / 通用设置之间切换。
- 根因(机制已证实,用户环境未定位):
.settings-modal的height与max-height只写了dvh,没有vh回退。不认识该单位的引擎会整条声明作废,height回到auto,高度改由内容决定;max-height同时作废后连上限都没有,于是内容多的分区高、内容少的分区矮。宽度走vw不受影响,所以看上去只有高度在跳。- 实测(Chrome 151.0.7922.169,1440×900,真实
next build产物 CSS + 逐节点复刻的弹窗 DOM):dvh 正常时四个分区全是 866.80 × 630.40、computed height 640px —— 事故在当前浏览器上不复现;把 dvh 摘掉后四个分区变成 313.23 / 313.23 / 378.58 / 1130.16,宽度不变 —— 与用户描述的形状完全一致。 dvh自 Chrome/Edge 108、Safari 15.4、Firefox 101、Android WebView 108 起支持,因此该机制只在旧 iOS 15.0–15.3、未升级的 Android System WebView、或钉死旧 WebView 的 App 内置浏览器上成立。用户当时用的是哪个浏览器尚未确认,这是本条停在investigating而不是resolved的唯一原因。
- 实测(Chrome 151.0.7922.169,1440×900,真实
- 为什么 BUG-554 的防复发没拦住:BUG-554 的条款是「设置弹窗必须同时声明 width 与 height」,守着它的断言是
assert.match(styles, /\.settings-modal \{[^}]*width:[^}]*height:/)—— 它读 CSS 源文本,只检查声明存不存在,不检查声明是否生效。本次 width 与 height 都老实写着,条款全绿,现象照出。BUG-554 的四个分区共用一个类这一条本轮未被推翻,且新增了直接断言常量的同尺寸契约把它钉死。 - 附带发现(比原判更严重):任务书要求照抄
globals.css:637的重复声明式回退height: 100vh; height: 100dvh;。实测 Lightning CSS(Tailwind v4 的压缩器)会合并同一规则内同名属性的重复声明,只保留最后一条:源码写了两条,产物里.group\/sidebar-provider[data-viewport]只剩height:100dvh。全仓唯一那处 vh 回退在线上早就是死的,而且birth-time-mobile-scroll-contract.test.ts还有一条断言专门守着这个从未发布过的写法。照任务书交付等于交一个 no-op。 - 修复:改用特性查询 ——
vh作基线,@supports (height: 1dvh)内升级为dvh,压缩器无法证伪条件因而整块保留。覆盖凡height(不含max-height/min-height)用 dvh 的 8 处:.standalone-page、.app-loading、.group\/sidebar-provider[data-viewport]、.admin-app-shell、.auth-page、[data-sidebar="sidebar"]、.settings-modal(桌面与移动端);另把.account-modal/.settings-modal的max-height/min-height一并纳入,因为实测里max-height塌成none也是尺寸失控的一环。同轮去掉设置分区菜单的强调色条并拆开选中/悬停两态(产品决策,不占编号)。 - 验证:修复后同 harness 重测 —— dvh 正常的引擎四个分区仍是 866.80 × 630.40 / 640px(与修复前逐像素一致,无回归);模拟不支持 dvh 的引擎从 313/378/1130 收敛到恒定 640px。新增
frontend/tests/viewport-unit-fallback-contract.test.ts(3 条,全文件范围),三次破坏性验证各自打红对应那条、还原后全绿。account-dialog-overlay.test.ts新增同尺寸契约与分区菜单契约,破坏性验证同样打红。tsc --noEmit0 错,npm run lint0 error / 118 warning(持平基线),next build后/仍○ Static,快速门 pytest 段 792 passed / 1 skipped / 0 failed。 - 防复发:
height用dvh必须写成vh基线 +@supports (height: 1dvh)升级,不得用height: 100vh; height: 100dvh;重复声明式回退——后者会被 Lightning CSS 吃掉,从来没发布过。由frontend/tests/viewport-unit-fallback-contract.test.ts全文件强制,三条断言分别管「dvh 必须在特性查询内」「禁止重复声明式回退」「升级过的选择器必须有 vh 基线」。设置弹窗四分区必须映射到同一个类名,由account-dialog-overlay.test.ts直接断言常量。只检查「声明存不存在」的 CSS 文本断言不算防线(这就是 BUG-554 的教训),新写 CSS 契约要么断到实际产物、要么断到可被破坏性验证证伪的结构。 - 相关记录:BUG-554(同一现象的第一次,本条为其复发)、BUG-695~697(触控与断点轮次,同一文件相邻区段)
- 复发自:BUG-554
- 修复版本:待发布
BUG-739 | 咨询会话容量的真实 Postgres 合同把 false: 写成期望值,staging 门禁从 BUG-732 起一直红
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
frontend/tests/database-consultation-session-capacity.test.ts、frontend/tests/helpers/postgres-fixture.ts的psql()、Independent Staging Quality Gate(backend-quality-gate.yml) - 用户现象:用户看不见产品行为变化。staging 从
dcfc2f15(BUG-732)起每次含门禁路径的推送都红,镜像不发布、环境不更新。 - 触发条件:质量门跑
npm run test:db。Gitea run2683/ job6103,SHA1e3570b4。 - 根因:
fixture.psql()把boolean::text的false规范成 psql-A的f,好让true:f:f这类冒号拼接在不同 Postgres 表示之间稳定。BUG-732 的真实库合同用了success::text,却把期望值写成"false:session_missing"。夹具一规范化,实际是f:session_missing。本机当时无 Docker,该文件 skip,带红合入。 - 修复:五处失败分支的期望值改成
f:session_missing/f:invalid_question_message/f:session_full,与既有database-billing-admin等合同同一套字母表。夹具改写保留,加一行说明。源码合同锁住「期望值必须是f:、夹具必须保留 false→f 规范化」,不依赖 Docker。 - 验证:
consultation-session-capacity.test.ts新增无 Docker 源码合同;定向套件全绿。本机仍无 Docker,真实test:db留给门禁。 - 防复发:对
fixture.psql()的冒号拼接结果不得断言"false:"。新增真实 Postgres 合同前先对照postgres-fixture.ts的false→f规范化,并用一条不需要 Docker 的源码合同锁期望值。 - 相关记录:BUG-732(引入这条真实库合同但未在有 Docker 的机器上跑)
- 复发自:无
- 修复版本:待发布
BUG-740 | 精度未达仍出交付卡,卡下只有自由文本邀请
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
decideRectification、guided_collect_windows、引导采集池、范围交付卡 - 用户现象:30 分钟窗给了 5 件带年月经历后直接出卡,区间 20 分钟、5 候选并列约 26%/26%/20%。卡下是「你要是还记得确切哪一天的事,不限领域,说出来我接着算」。
- 触发条件:七条定向线关闭、自动题池空、头名并列。
- 根因:
tied_first耗尽分支没有任何宽度或领先度条件。自动题池空之后没有有方向的题源。 - 修复:出卡须宽度 ≤10 分钟且前两名相对可能性差 >3 个百分点且不并列;未达时按引擎边界窗口逐条点名,用类型芯片 + 年/月选择器录入。用户说「没有了」仍按现行规则出卡。自由文本邀请删除。
- 验证:
frontend/tests/rectification-precision-gate-20260916.test.ts、frontend/tests/rectification-guided-collect-20260916.test.ts、tests/test_event_probes_guided_windows.py。 - 防复发:耗尽/offer 出卡必须过
precisionGateMet,除非用户停止或引导题源已空。不得再把自由文本邀请当作精度未达时的下一步。 - 相关记录:BUG-689、BUG-653、BUG-654、BUG-687
- 复发自:BUG-689(空池邀请没有方向,且没有精度门槛)
- 修复版本:待发布
BUG-741 | 「跳过」和「没有发生过」同样永久关闭整条线
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
targetedDomainClosed、定向存在性题、时间点题 - 用户现象:点「这条先跳过」或「记不太清楚」后,那条线再也不会出现。时间点题答「那个时候没发生」后,整条感情线也被当成没有。
- 触发条件:定向存在性题选 C/D;或
source=event_probe的年月是非题选「明确没有发生」。 - 根因:
declined与skipped同样写入关闭集合。存在性题干只覆盖一两个例子却按整个领域记「没有」。时间点负答案没有与领域关闭隔离。 - 修复:
declined永久关闭;skipped换一种更具体的问法再问一次,再次跳过才关闭。存在性题问整个领域、任何时间,B 写成「这类事都没有过」。时间点题的负答案不关领域。 - 验证:
frontend/tests/rectification-guided-collect-20260916.test.ts。 - 防复发:关闭集合不得把一次跳过当成拒绝。时间点题不得写入会让
targetedDomainClosed判定领域关闭的状态。七条线一张表,不得按单个领域写死。 - 相关记录:BUG-687、BUG-643、BUG-740
- 复发自:无
- 修复版本:待发布
BUG-742 | 定向存在性题 B/C/D 回执文案与状态对调
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
targetedCollectExistenceAck、RECTIFICATION_USER_COPY.collectDeclinedAck/collectSkippedAck - 用户现象:答「没有发生过」听到「记下了,这方面先跳过」。
- 触发条件:定向存在性题选 B。
- 根因:B 落
declined、C/D 落skipped是对的;回执把 declined 写成「先跳过」。不是 D 落成 declined。 - 修复:B 回「记下了,这条按没有发生过记」;C/D 回「记下了,这题先放着,后面换个问法再问一次」。
- 验证:
frontend/tests/rectification-guided-collect-20260916.test.ts断言两句回执。 - 防复发:declined 回执不得写「跳过」;skipped 回执不得写「没有发生过」。
- 相关记录:BUG-741
- 复发自:无
- 修复版本:待发布
BUG-743 | 性格题 ±1 写入 posterior_score,可能把卡在 8 分边界的簇推过线
- 状态:investigating
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
applyProbeOutcome、unionStillValidRange - 用户现象:性格题答完后范围从 20 分钟弹到 28 分钟,下一题又缩回。
- 触发条件:未淘汰簇落后头名刚好 8 分,性格题给其中一侧 ±1。
- 根因(代码路径已确认,真机未复现):
varga_style以PROBE_WEIGHT.yearless = 0.5把SCORE_DELTA写进同一份posterior_score。unionStillValidRange用peak - posterior_score < MIN_SEPARATION_LEAD。风格题只该改排序。本轮不改打分,未改并集规则。 - 修复:无。已排除:性格题不会淘汰(
yearless不增加strong_conflict_count)。未排除:并集是否因此扩/缩。 - 验证:未附回归。不得为收口编根因。
- 防复发:风格微调若并入范围计算,必须用一份不含风格分的分数做并集。
- 相关记录:BUG-651、BUG-629、BUG-740
- 复发自:无
- 修复版本:无
BUG-744 | 侧栏写成两套标记,次级页会话行与页脚跟首页长得不一样
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
frontend/src/components/app-sidebar.tsx、已删除的app-nav-rail.tsx、frontend/src/components/sidebar-session-row.tsx、frontend/src/app/globals.css - 用户现象:staging 真机上,
/的侧栏和/chart、/ephemeris、/reports的侧栏不是同一个东西——会话行的高度、内边距、当前项标记都对不上,页脚一个 56px 一个 44px。 - 触发条件:从
/走到任一次级页,对比左侧同一列。 - 根因:次级页侧栏当初为了不把
Home()的会话管理层上提,另写了第二个组件。它的会话行是<Link className="session-row nav-rail-row">直接套.session-title,没有.session-main这一层,因此丢掉了padding: var(--space-2)、min-height: 44px和.session-main[data-active="true"]::before的 2px 当前项标记;而.session-row的grid-template-columns: minmax(0, 1fr) 44px仍给一个从不渲染的菜单按钮留着 44px 空列。页脚另起.nav-rail-identity(44px flex,带「N 点」),与/的.profile-trigger(56px grid、带 chevron)并列存在。产品要的是「不带写操作」,从来不是「长得不一样」;分叉是实现手段的副作用。 - 修复:
AppSidebar收编第二个组件——会话操作与账户菜单收进可选的controls,不传就渲染只读模式:会话行仍是同一个SidebarSessionRow、同一套.session-row > .session-main标记,只是.session-main是<Link>、不渲染菜单按钮、并用data-readonly="true"去掉那一列 44px 空位;页脚是同一个.profile-trigger,渲染成去/的链接,不带 chevron、不带菜单。app-nav-rail.tsx与.nav-rail-*两段 CSS 删除。只读模式只少菜单按钮、chevron、账户菜单三样。 - 验证:
frontend/tests/sidebar-contract.test.ts新增「the same component renders read-only when/is not the one mounting it」,锁controls?可选、只读行带.session-main、只读行不含session-menu-trigger、.session-row[data-readonly="true"]不留 44px 列、页脚是.profile-trigger链接;grep -rn "AppNavRail\|nav-rail" frontend/src= 0。改过的既有断言逐条写在docs/tasks/PROGRESS-sidebar-unify-20260916.md。 - 防复发:产品里只有一个侧栏组件。再要一个「只读侧栏」必须走
controls缺省,不得新建第二个组件或第二套类名;合同测试锁住「只读模式只少三样」。 - 相关记录:BUG-711、BUG-438
- 复发自:无
- 修复版本:待发布
BUG-745 | 首页进次级页整页刷新,次级页外壳逐页重挂,每跳一次重拉会话列表
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
frontend/src/components/app-sidebar.tsx、frontend/src/app/(secondary)/layout.tsx、frontend/src/hooks/use-sidebar-data.ts、frontend/src/lib/sidebar-data-cache.ts、frontend/src/hooks/use-session-management.ts、frontend/src/app/page.tsx - 用户现象:每进一次次级页,侧栏的会话列表都要重新读一遍;从次级页点「新建对话」回首页要等很久。
- 触发条件:
/→/chart;/chart→/ephemeris→/reports。 - 根因:三件事叠加。① 侧栏的
leaveChat()用window.location.assign(path),首页出去是整页刷新,React 树与内存全部清零。②app/下没有把四个次级路由包起来的 layout,三个页面各自在组件内部渲染SecondaryShell,每份都挂一套SidebarProvider + 侧栏,因此即便是客户端跳转外壳也整个重挂,数据 hook 的 effect 重新GET /api/sessions?limit=40+GET /api/account。③ 这两个读没有任何缓存。 - 修复:① 三个页面项改
<SidebarMenuLink href=…>客户端跳转,persistLoginSessionReturn()保留在onClick里,/login仍是硬跳转;顺带删掉从来没被调用过的死 proponOpenReports(侧栏里解构成_onOpenReports),它删掉后page.tsx的router再无消费者,useConsultationRun那个同样解构成_router的死参数一并删。② 四个路由移进app/(secondary)/路由组,layout.tsx承载SidebarProvider + AppSidebar(只读) + SidebarInset,URL 与四个渲染标记均未变。③ 模块级按账户 id 键的 60 s 缓存,同步读、后台刷新;/上新建 / 重命名 / 删除 / 归档 / 收藏成功后调invalidateSidebarCache(),401 清空。不落 localStorage。 - 验证:
frontend/tests/sidebar-data-cache.test.ts4 条(命中不重拉、过期重拉一次、写操作后拿到新标题、双账户不串、只存内存、401 清空);frontend/tests/chart-page-view.test.tsx新增「the (secondary) layout mounts one read-only sidebar for all four routes」;next build四个路由标记与基线逐个一致(/chart○、/ephemeris○、/reportsƒ、/reports/[reportId]ƒ),/仍○。浏览器级证据(Network 面板没有 document 请求、跨页没有/api/sessions)无 Chrome 无登录态做不了,写成docs/testing/sidebar-unify-20260916.md的真机清单。 - 防复发:次级页不得再在组件内部挂第二份
SidebarProvider;合同测试锁 layout 里<SidebarProvider>恰好一次、且不传controls。侧栏源码里不得再出现window.location(/login的硬跳转在page.tsx与use-billing-panel.ts,由chat-navigation-a11y-contract守)。 - 相关记录:BUG-744、BUG-746
- 复发自:无
- 修复版本:待发布
BUG-746 | 侧栏折叠状态不跨页保留,每换一页又展开
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
frontend/src/components/ui/sidebar.tsx、frontend/src/lib/sidebar-state.ts - 用户现象:在
/把侧栏收起来,进/chart又是展开的;反过来也一样。 - 触发条件:桌面或平板宽度下收起侧栏,然后换一页。
- 根因:
SidebarProvider的defaultOpen只活在内存里,ready之后一律回到defaultSidebarOpen(viewport)的断点默认值,没有任何持久化。以前四个次级页各挂一份 provider,跳一次就重置一次。 - 修复:
readStoredSidebarOpen/writeStoredSidebarOpen落在localStorage的sidebar_state键;commitOpen每次显式切换时写入,ready的那个 effect 用readStoredSidebarOpen(viewport) ?? defaultSidebarOpen(viewport)取值。移动端抽屉不读不写。 - 验证:
frontend/tests/sidebar-state.test.ts新增两条——收起后重挂仍收起(含无存储 / 存了脏值 / 存储不可用三种降级)、移动端不读不写且null存储不抛。 - 防复发:不得改用需要服务端读取的 cookie:
/、/chart、/ephemeris都是○ Static,服务端读 cookie 会让三条路由一起掉出静态渲染。 - 已知缺口(不得写成已解决):读取发生在挂载后的同一个 effect 里,也就是断点默认值原本就生效的那一帧。对一个把侧栏收起来的桌面用户,整页加载时首帧仍是展开、随后收起,这是让步顺序第 1 条,任务书允许。客户端跳转(
/↔ 次级页)不受影响,因为 provider 不重挂或重挂时localStorage已可读。 - 相关记录:BUG-745
- 复发自:无
- 修复版本:待发布
BUG-747 | 校正 receipt 新增 guided_collect_windows 键,记忆化 golden 门禁红
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
scripts/rectification/decision_policy.py、tests/test_rectification_engine_memoization.py、tests/golden/rectification_engine_memoization_v1.json - 用户现象:用户看不见。
run_quality_gate.py的CORE_PYTEST_TARGETS含tests/test_rectification_*.py,test_score_candidates_matches_baseline_golden红,门禁不放行、镜像不发布,staging 停在上一个 SHA。 - 触发条件:
build_decision_receipt往 receipt 里加了guided_collect_windows,golden 里没有这个键;_assert_tiered_equal对 receipt 做键集合严格相等。 - 根因:形状变更没有同步 golden。BUG-733 的分档比较与「禁止
write_golden()」是对浮点漂移的防线,它按设计拦住了这次的键集合差异——拦截生效了,是上一轮执行方没有跑这个文件(进度记录里 Python 只跑了四个定向文件,没跑tests/test_rectification_*.py全量)。 - 修复:golden 只追加
guided_collect_windows一个键(git diff只有这一处、62 行),不放宽_assert_tiered_equal、不调用write_golden()。另外把_golden_payload()里的date.today()冻结到FROZEN_TODAY:窗口年份取自墙钟,不冻结的话这份 golden 会在跨年时自己变红(BUG-733 同一族的隐患)。 - 验证:
.venv/bin/python -m pytest tests/test_rectification_*.py tests/test_event_probes_guided_windows.py -q→ 193 passed;golden 的git diff --stat为1 file changed, 62 insertions(+)。 - 防复发:改
decision_receipt形状的轮次必须跑tests/test_rectification_*.py全量,不能只跑定向文件;golden 的任何字段都不得依赖墙钟。 - 相关记录:BUG-733、BUG-712、BUG-721、BUG-740
- 复发自:BUG-733(同一份 golden;旧防线拦住了,未被执行)
- 修复版本:待发布
BUG-748 | 16 条既有校正断言变红却被写成「已按三栏改」
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
frontend/tests/rectification-*.test.ts(11 个文件)、docs/tasks/PROGRESS-rectification-precision-gate-guided-collect-20260916.md - 用户现象:用户看不见。
npm test从基线 3372 条 / 31 红变成 3391 条 / 47 红,新增的 16 红全在校正套件,没有一个文件被改过。 - 触发条件:上一轮只跑了本单新增与相关的定向套件,全量
npm test因本机无 Docker / 并行 OOM 被判定为「不能当回归清单」,于是没有与基线逐条比对。 - 根因:AGENTS §7.3「测试总数不得低于开工时实测、改断言必须写三栏」被当成只约束主动修改的断言;被代码变更打红的既有断言没有走同一条流程。无 Docker 只影响 31 条固定清单,其余部分照样可比。
- 修复:本轮把 16 条逐条处置(外加 D5/D6 连锁打红的另外 25 条,见 BUG-751),每条在测试里写「原值 / 新值 / 原因」三栏;不删测试、不 skip。
- 验证:
npm test→ 3396 条 / 31 红,红清单与基线 31 条逐条同名(无 Docker 与[eval]路径别名那一组)。 - 防复发:无 Docker 时的正确做法是「与基线逐条比对失败清单」,不是放弃全量;进度记录里不得把没跑过的套件写成已处置。
- 相关记录:BUG-751、BUG-740
- 复发自:无
- 修复版本:待发布
BUG-749 | 引导窗口的领域是轮询贴上去的,一个窗口只问一个随机领域
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
scripts/rectification/event_probes.pyguided_collect_windows、collection-question-pool.tsguidedWindowPrompt/guidedWindowPool/windowAlreadyAsked、event-date-entry-card.tsx - 用户现象:「2018 年 3 到 5 月之间,有没有入职、换工作或职责变重?」答「这段没有」之后,这个时间窗再也不会出现——用户那段时间的搬家或恋爱永远问不到。
- 触发条件:边界来自 Vimshottari 或那罗延本命轨道(与领域无关),引擎按
fallback[unlayered_index % len(fallback)]轮流贴一个领域;前端windowAlreadyAsked以(年, 月区间, 领域)为键,而同一窗口只会以那一个领域出现一次。 - 根因:把「哪一段时间能切开候选」和「这段时间发生了哪一类事」混成一个字段。只有
nara:d9/nara:d10两条轨道真的带领域。 - 修复:无领域轨道的窗口
domain = "any",题干写「YYYY 年 M 到 M 月之间,有没有什么事,比如<开放领域口语 2~3 个>?」;口语从KIND_ORAL表取,剔除已拒答与已覆盖的领域,决策层不按领域写if。windowAlreadyAsked的键去掉领域,只看年 + 月区间。A 之后录入卡的芯片默认第一个仍开放的领域。nara:d9/nara:d10保持固定领域;该领域被拒答时也退回any。 - 验证:
tests/test_event_probes_guided_windows.py(真实引擎 20 分钟窗 golden,7 passed)断言无领域轨道窗口是any、d9/d10 保留固定领域、拒答领域退回any;frontend/tests/rectification-guided-collect-20260916.test.ts断言开放题干的口语列表 ≤3 条、同一窗口换领域标签也不重问、芯片默认第一开放领域。 - 防复发:窗口的
domain只能来自真正带领域的轨道;一个边界窗口是一个问题,已问判定不得把领域并进键里。 - 2026-09-29 产品修订:见 TASK-rectification-futile-collect-stop-20260929 D1——训练门开后不再问引导窗口题(BUG-1084);本条对门前仍适用。
- 相关记录:BUG-740、BUG-741、BUG-751
- 复发自:无
- 修复版本:待发布
BUG-750 | 录入卡提交的是选项列表拼成的句子,账本 summary 变成三选一
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
frontend/src/components/event-date-entry-card.tsxformatEventDateEntryMessage - 用户现象:用类型芯片 + 年/月选择器提交一件事后,账本里记的是「2019 年 3 月,开始认真关系、分手或结婚」——一句三选一列表,而不是用户选的那一类。
- 触发条件:引导窗口题答「有,我来填时间」后从录入卡提交。
- 根因:提交文本直接拼
KIND_ORAL[domain],而KIND_ORAL是题干用的例子列表,不是事件描述。 - 修复:改成「YYYY 年 M 月(D 日),<领域标签>方面有一件事」,标签取
RANGE_DELIVERY_DOMAIN_LABEL;文本里不再出现「或」。 - 验证:
frontend/tests/rectification-guided-collect-20260916.test.ts断言提交文本与「不含『或』」。 - 防复发:任何写进账本的用户文本都不得复用题干的例子列表。2026-09-16 录入卡按产品决策删除,本条防复发仍适用于任何用户可见文本。
- 相关记录:BUG-740、BUG-749、BUG-908
- 复发自:无
- 修复版本:待发布
BUG-751 | 精度门槛被当成充分条件,把「线没问完不出卡」一起短路
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
core/rectification-decision.tsmayDeliverOnPrecision/decideRectification、v9/decision-from-dossier.tsnarrowingExhaustion、Skill §流程 - 用户现象:门槛达标时,刷新与定向补事都还没问完就直接出交付卡;反过来,任何没有传这两个 flag 的调用(
decideConversationalSession、decideAfterInferenceChange的无 state 分支)因为「缺省即放行」而完全绕开了 BUG-654 / BUG-656 的「刷新与定向线未穷尽不得交付」。另外guidedCollectExhausted()不含refreshExhausted:引导池空、刷新还没试过,也会出卡。 - 触发条件:
precisionGateMet或guidedCollectExhausted任一为真(或两者都缺省)。 - 根因:
mayDeliverOnPrecision写成了「或」,并且被塞进stillNeedNarrowing与datedMethodCollectOpen两条采集分支做短路,等于用门槛去撤销采集条件。 - 修复(产品 2026-09-16 决策 D2 + D6):门槛只在还有题可问时挡住出卡。flag 全部缺省时门槛不参与,采集分支回到
317e9f18的判断;flag 传入时交付条件 =userStopped || (guidedCollectExhausted && refreshExhausted !== false && targetedCollectExhausted !== false)——即userStopped || 所有题源都问完。题源都空了就是 D2 说的「用户确实补不出来」,卡片、采用按钮、三句正文照现行规则给,不加「未达门槛」标注。撤回两处!mayDeliverOnPrecision短路,门槛只加在最终交付分支。narrowingExhaustion计算的guidedCollectExhausted现在含refreshExhausted。precisionGateMet保留计算,并新挂到RectificationDecision.precisionGateMet与publicNextAction().precision_gate_met上报,只是不再单独决定时机。Skill §流程改成「所有线(含引导窗口题、跳过线重问、未覆盖领域题)问完后,或用户说没有了后,才交付目前范围」,bump 到 10.0.28。 - 口径提醒:按 D2 + D6,10 分钟门槛不改变出卡时机,本轮实际生效的是题源变多(引导窗口题 + 跳过线重问 + 七条线全部轮到)、线问完才出。
- 验证:
frontend/tests/rectification-precision-gate-20260916.test.ts新增五条——引导池空但refreshExhausted=false不交付、门槛达标但定向线未问完继续问、所有线问完但门槛未达照现行规则出卡(含采用)、门槛值在采集出口也照常上报、helper 路径上报 null;rectification-decision-authority.test.ts的公开字段深比较跟上precision_gate_met。撤回短路后连锁打红的 41 条既有断言(16 条来自 BUG-748 的基线红 + 25 条本轮新红)全部按「原值 / 新值 / 原因」三栏处置,均为 D5「七条线全部轮到」与 D6「问完再出」的预期变化。 - 防复发:门槛不得出现在任何采集分支的条件里,也不得成为「题源已空仍不出卡」的理由(那会造出没有题也没有卡的死角,违反 BUG-652);缺省 flag 只能表示「门槛不参与」,不能表示「放行」。
- 2026-09-29 产品修订:见 TASK-rectification-futile-collect-stop-20260929 D1——训练门开后不再问定向七条线、跳过线重问与引导窗口题,它们不再挡卡;本条「七条线全部轮到、线问完才出」只在训练门未开时成立(BUG-1084)。
- 相关记录:BUG-654、BUG-656、BUG-740、BUG-748、BUG-749
- 复发自:BUG-654(刷新与定向线未穷尽不得交付,被门槛短路绕开)
- 修复版本:待发布
BUG-752 | 离线回放注入的事件与真值边界无关,「0/20」不能当结论
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
scripts/research/guided_collect_holdout_replay.py、docs/research/guided_collect_holdout_2026_09_16.json - 用户现象:用户看不见。研究结论写成「没有一例靠引导件达标」,被当作引导题源无效的证据。
- 触发条件:
synthetic_event一律取window["month_lo"]作为事件月,领域取轮询值,与真值候选在该窗口上的边界日期无关。 - 根因:任务书要求注入「真值方向」的带年月事件,脚本注入的是窗口左端点。落在窗口里但不在真值候选的边界上,对打分几乎没有方向性,所以量到的是合成事件的噪声,不是题源的信息量。
- 修复:按窗口的(轨道, index)取回每个候选自己的边界日期,
truth组注入真值候选的那一天所在月,另跑一组opposite(离真值最远的候选的同一边界)做对照;三档半径都跑。汇总新增met_after_six_only/met_via_guided/median_events_via_guided,把「六题后本来就达标」和「靠引导件补上」分开数。 - 验证:见
docs/tasks/PROGRESS-rectification-precision-gate-guided-collect-fix-20260916.md的数字表与docs/research/guided_collect_holdout_2026_09_16.json。这不是合入门槛。 - 防复发:离线回放的合成证据必须能说清「注入在谁的边界上」;只报总达标数、不分「本来就达标 / 靠注入达标」的数字不得写进结论。
- 相关记录:BUG-740、BUG-749
- 复发自:无
- 修复版本:待发布
BUG-753 | 次级页移进 (secondary) 路由组后 capability audit 把文件夹名当成公开路由
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:Gitea
backend-quality-gaterun2696/2697、tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources、scripts/jyotish_api_server.py_scan_app_routes - 用户现象:用户看不见。
dc732825把校正门禁红修绿后,staging 推送 run2697仍1 failed, 797 passed:app_routes出现(secondary)/chart等,期望仍是chart/ephemeris/reports。 - 触发条件:侧栏统一把四个次级页移进
app/(secondary)/后跑 capability audit 精确集合。 - 根因:BUG-713 同类。
_scan_app_routes把文件系统相对路径当公开路由,Next.js 路由组括号不进 URL。侧栏单和精度门槛修复单都只跑了前端矩阵,没跑这条 Python 契约。 - 修复:扫描时丢掉
(…)路由组段。公开 URL 集合不变,不把(secondary)/chart写进期望列表。 - 验证:
pytest tests/test_api_server_security.py::test_capability_audit_scans_registry_and_local_sources。 - 防复发:新增或移动
frontend/src/app/**/page.tsx必须同一提交跑 capability audit。路由组不是新入口。 - 相关记录:BUG-014、BUG-126、BUG-143、BUG-454、BUG-713
- 复发自:BUG-713
- 修复版本:待发布
BUG-908 | 年月阶段焦点题干被模型追问覆盖,下面还挂着录入卡
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
spoken-prompt.tswithSpokenPrompt、turn-decision.tsprojectCurrentQuestion、录入卡组件(已删)、Skill 10.0.29 - 用户现象:定向健康题答 A 后打字给了年月,助手追问「是你本人做的吗」,追问句下面还挂着「哪一类事 + 发生年月」录入卡。
- 触发条件:活动焦点题号是
collect:targeted:<domain>:year或collect:guided:window:…:year,模型用同号set-focus覆盖prompt。 - 根因:BUG-673 只保护了
targeted_collect存在题;年月阶段 schema 会被withSpokenPrompt整段换成模型的话。产品同时拍板:录入卡整个删掉,年月阶段改打字。 - 修复:年月阶段以题号
stage === "year"识别,只写spoken_prompt、不动prompt。投影只用服务器题干。删除EventDateEntryCard/EventDatePicker。选项 A 改「有,我来说」。Skill 10.0.29。 - 验证:
frontend/tests/rectification-year-focus-overlay-20260916.test.ts。 - 防复发:年月阶段识别以题号前缀为准,与 BUG-673 同口径。不得再引入第二套录入入口。
- 相关记录:BUG-673、BUG-750
- 复发自:BUG-673
- 修复版本:待发布
BUG-909 | 事件入账后年月焦点没有关闭
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
resolveActiveCollectFocusAfterEvidence、pendingTargetedYearDomain、rectification-record-evidence-batch、rectification-propose-evidence - 用户现象:时间轴已「记下了」年月事件,画面仍停在该领域的年月题。
- 触发条件:存在题答 A 落后年月焦点,用户打字给带年月事件。
- 根因:回放显示两条都要修。(a) batch 原先只在
isCollectFocusSchema时 resolve,年月题号要按前缀识别;(b)pendingTargetedYearDomain在存在题 resolved 后只要 year 题未关就会再出,即使账本已有该领域带年月事件。单条propose-evidence原先不 resolve。 - 修复:抽
resolveActiveCollectFocusAfterEvidence,batch 与 propose 共用;isCollectFocusSchema或:year题号都 resolve。pendingTargetedYearDomain把coveredCollectKinds也算 yearClosed(health别名走canonicalCollectDomain)。 - 验证:overlay 测试「dated evidence closes the year stage」与 persist collect schema。
- 防复发:两条写入路径必须走同一段 resolve;该领域已有带年月事件不得再出
:year。 - 相关记录:BUG-908
- 复发自:无
- 修复版本:待发布
BUG-910 | 年月阶段收到无年月短句会烧完步骤上限
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
agent-run.tshost 前置、year-entry-host.ts - 用户现象:对着追问回「是/不是」一类短句,整轮报「本轮达到步骤上限」。
- 触发条件:活动焦点是年月阶段,用户消息没有
19xx/20xx或「x 年」。 - 根因:模型拿到「有年份走 batch,否则再问」的 hint,短句写不进、set-focus 同号幂等,循环到 8 步没正文。完整 agent 循环未在单测里复现(记防御性前置)。
- 修复:日期可靠性分类器之后、计费之前,host 以服务器题干再问一次,不进模型、不扣点。焦点保持原样,失败轮不会只关掉年月题。
- 验证:
shouldHostReaskYearEntry与 agent-run 源码顺序断言。 - 防复发:年月阶段无年份短句不得进模型。
applyHostFallback条件未改。 - 相关记录:BUG-908
- 复发自:无
- 修复版本:待发布
BUG-911 | 独立问题块与助手正文不在同一条左边线
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
globals.css.rectification-message-question.is-standalone、rectification-agentic-chat.tsx - 用户现象:消息外的 persisted 问题块题干顶到左边。
- 触发条件:
questionGap === "persisted_question"的独立块。 - 根因:独立块没有
--assistant-content-inset。 - 修复:独立块加
is-standalone,margin-inline-start: var(--assistant-content-inset);块内 embedded 点选卡 inset 归零。 - 验证:overlay CSS 声明断言。
- 防复发:独立问题块必须与助手正文共用同一 inset token。
- 相关记录:BUG-908
- 复发自:无
- 修复版本:待发布
BUG-912 | D9 感情判别题借 D24 对照探针:错分 + 无卡
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-17
- 影响面:
stampChoiceSchemaWithProbe、varga-distinguish-probe.ts、projectRectificationChoiceCard、persistNextInterviewIfIdle - 用户现象:正文念了感情判别题,下面没有 A/B/C/D;打字答「没有」后区间被收窄。
- 触发条件:D9 风格 followup 没有自己的
semantic_key,inference_state 里另有高增益 D24 对照探针。 - 根因:
stampChoiceSchemaWithProbe在无 preferred key 时退到selectHighestGainProbe。BUG-375 只堵住了对比探针被已答教育题盖掉,没堵住「无探针的分盘风格题借最高增益探针」。 - 修复:不再退到最高增益探针。D9/D10 等风格题按剩余候选的分盘上升自建探针。GET 从落库副本投影;过期焦点
superseded后落下一问。首版只在盖戳那一刻把自建探针注入内存 state,答题 / GET / idle 三处读持久化 receipt 都找不到它(BUG-915);2026-09-17 由该单补齐重建,并按产品决策不再出风格计分题。 - 验证:
frontend/tests/rectification-dead-d9-choice-20260916.test.ts(2026-09-17 改写为落库形状回放);BUG-375 原断言仍绿。 - 防复发:无
semantic_key的计分题不得盖上别的分盘探针。宁可不落库(走 BUG-674)也不错分。只在内存里存在的探针不算落库,新增自建探针必须同时给出「每个读 state 的地方都重建」的路径(BUG-915)。 - 相关记录:BUG-375、BUG-578、BUG-582、BUG-674、BUG-915、BUG-917
- 复发自:BUG-375
- 修复版本:待发布
BUG-913 | 答「没有」后平局直接出交付卡
- 状态:closed_by_design
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
yearlessPersonalityCanAsk、shouldHoldForTieBreak、tieBreakPersonalityFollowup - 用户现象:感情判别题打字答「没有」后出现三列交付卡。
- 触发条件:已有多件带年月经历、候选并列、采集进度
missing:0。 - 根因:回放。
yearlessPersonalityCanAsk在datedCount > 0时为假;平局参考题只认varga.d9/varga.d10性格探针。线问完后的并列出卡是 BUG-689 设计出口,不是 BUG-751 / BUG-688 复发。 - 修复:不改出卡门槛。
- 验证:
yearlessPersonalityCanAsk({ datedCount: 5 }) === false。 - 防复发:有带年月经历时不得把平局交付改成强制性格题。
- 相关记录:BUG-689、BUG-688、BUG-751、BUG-912
- 复发自:无
- 修复版本:待发布
BUG-914 | 交付卡把一对相反性格都列成共同点
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:
scripts/rectification/refinement_packet.pyNAKSHATRA_TRAITS选项、buildRangeDeliveryshared_traits - 用户现象:三列卡头同时出现「希望两边都能说得过去」和「必要时会直接选边」。
- 触发条件:升点靠近月宿交界,代表分钟是最晚一列,三列都拿 earlier 那一对。
- 根因:每个 option 把相反两极整对塞进
traits;splitSharedTraits把两句都判成共同点。 - 修复:每个 option 只取第一极;
user_meaning写成「A:{earlier[0]}。B:{later[0]}。」 - 验证:Python
test_rectification_refinement_packet断言traits长度为 1 且两 option 不同句;前端断言两极不得同时出现在shared_traits。 - 防复发:一列只写一句,不得自相矛盾。
- 相关记录:BUG-912
- 复发自:无
- 修复版本:待发布
BUG-915 | 自建分盘探针只活在内存里:点选报 stale_probe、GET 无卡、idle 立刻 superseded
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
varga-distinguish-probe.ts、expectedAnswerSchemaFor、applyRectificationChoice、rectification-resolve-focus、choiceCardFromCaseDossier、persistNextInterviewIfIdle、buildMethodFollowupPlan - 用户现象:正文出现「亲密关系里,你更接近哪一种相处方式?」,下面四个选项灰显点不了;同屏范围行与输入框占位却写「再说一件带年月的事」。点选或打字回答会报错。
- 触发条件:BUG-912 首版给分盘风格题自建了探针;该探针只在盖戳时注入内存 state,
decision_receipt.inference_state里没有。 - 根因:自建探针没有按月宿边界探针的既有模式在每个读 state 的地方重建。答题路径、GET 投影、idle 过期判定都读持久化 receipt:
matchProbeForChoice找不到 →stale_probe;liveInferenceDistinguishProbe找不到 → 无卡;liveDistinguishProbe找不到 → 刚落库的焦点每次 idle persist 都被superseded。 - 修复:新增
withOwnedDistinguishProbes(state, receipt),按window_scan.transitions+ state 候选确定性重建全部自建探针,接入盖戳、点选答题、打字答题、GET 投影、idle 过期五处(答题两处共用inferenceForPersistedAnswer);重建探针的source为专用owned_varga_style,并像nakshatra_boundary一样从 contrast packet 与 event 探针池排除,避免答题写回 state 后它们在下一轮变成可问的题。产品 2026-09-17 同时拍板(选项 b):分盘风格题不再作为distinguish_candidates计分题——分层判别题只在引擎给出该领域带年份事件探针时以「某年前后有没有…」出题并按该探针计分,否则该题不出,计划直接走下一条线。删除attachVargaDistinguishIdentity/withFollowupOwnedProbe。 - 验证:
frontend/tests/rectification-dead-d9-choice-20260916.test.ts10 条:落库形状回放(未重建 =stale_probe,重建后applied且只按 D9 分组计分)、choiceCardFromCaseDossier出卡、persistedDistinguishFocusStale不误伤且候选集变化仍过期、五处接入点源码契约、(b) 决策两半。npm test3421 条 / 31 红(与基线c32f81e7逐条一致)。 - 防复发:任何不在引擎
inference_state里的探针,必须有一个从 receipt 确定性重建的函数,并在每个读 state 的地方接入;只在盖戳时注入内存的写法一律视为未落库。分盘风格题不得作为计分判别题。 - 相关记录:BUG-912、BUG-375、BUG-559、BUG-674、BUG-917
- 复发自:BUG-912(同一根因的第二层:探针身份有了,持久化没有)
- 修复版本:待发布
BUG-916 | 年月阶段 host 前置排在会话校验之前
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
runV9AgentTurn、PUBLIC_RECTIFICATION_PHASES - 用户现象:无直接用户现象;跨会话(旧标签页)发来的无年月短句也会被写成一条确定性轮。
- 触发条件:BUG-910 的 host 前置写在
dossier.case.sessionId !== sessionId校验之前;返回的phases: ["answer.host_year_entry"]不在公开 phase 白名单里(类型是 string,tsc 不报)。 - 根因:前置插入位置只考虑了「不要进模型」,没有沿用既有的会话校验顺序;phase 名新造而未登记。
- 修复:host 前置挪到会话校验之后(计费位置不变);phase 复用既有
answer.host_fallback,公开快照的answer_origin: "host_fallback"语义一致。 - 验证:
frontend/tests/rectification-v9-stream.test.ts等既有 host 前置断言仍绿;npm test失败清单与基线逐条一致。 - 防复发:任何早退分支都必须排在会话 / Skill 身份校验之后;返回的 phase 必须是
PUBLIC_RECTIFICATION_PHASES里的值。 - 相关记录:BUG-910、BUG-915
- 复发自:无
- 修复版本:待发布
BUG-917 | 死掉的点选卡与「再说一件带年月的事」同屏
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
rectificationQuestionGapState、rectification-agentic-chat.tsx、rectification-message-entry.tsx - 用户现象:最后一条助手消息挂着一张灰色不可点的 A/B/C/D,同屏的范围行与输入框占位却说「再说一件带年月的事就能继续」,两条指令互相矛盾。
- 触发条件:焦点被 idle persist 判成
superseded(BUG-915),GET 没有choice_card;current_question为 null 于是interviewCollectWaiting为真。 - 根因:缺口态只看
current_question,看不到消息级的未答点选;消息内卡片只把status === "active"的当成未答,superseded且没有作答的焦点仍然渲染成disabled的选项。 - 修复:
rectificationQuestionGapState新增unansweredDeadChoice输入,在delivered之后、collect_waiting之前返回unavailable(「没有拿到下一个问题」+「接着问」);chat 按最新一条 settled 助手消息计算该标志;message-entry 对「superseded且无answer_option」不再画卡。 - 验证:
frontend/tests/rectification-dead-d9-choice-20260916.test.ts「a dead tap card never shares the screen with the collect-wait placeholder」:collect_waiting+ 死卡 →unavailable,并有 chat / message-entry 源码契约。 - 防复发:死卡与
collect_waiting不得同屏;未作答却已被关闭的点选题不得渲染成灰色选项。 - 相关记录:BUG-915、BUG-675、BUG-674
- 复发自:无
- 2026-10-01 产品修订:见
docs/tasks/TASK-rectification-message-cleanup-20261001.mdD1——死题(superseded 且无答案、或被替换的未答题)在消息上什么都不画,不再保留「题干无选项」;本条其余防复发不变(BUG-1135)。 - 修复版本:待发布
BUG-918 | 校正时间轴读数在 375px 手机上被裁成「已…」
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
frontend/src/app/globals.css.rectification-timeline/.rectification-timeline__readout的@media (max-width: 767px)段、frontend/src/components/rectification-timeline.tsx - 用户现象:iPhone 上常驻时间轴一行读作「04:48–05:07 20 分钟 代表分钟 04:53 已…」,第四项被省略号裁断,第五项完全不显示。
- 触发条件:视口宽 375 CSS px(iPhone SE 2/3、13 mini)打开生时校正,Case 已有代表分钟、已答题数与已对照事件数(读数五项齐全)。
- 根因:两条叠加。一是读数按「四项短文本」设计,
.rectification-timeline在 767px 以下仍取calc(var(--space-4) + var(--assistant-content-inset)),即每侧 16 + 44 = 60px,375px 的可用内容宽只剩 255px;条不在消息列里,这 44px 头像沟槽在手机上买不到任何可见对齐。二是cfb41daf又给读数追加了第五项已对照 N 件,把四项预算变成五项。按 13px 逐字保守估宽,五项加四个 12px 间隙需要约 383px,超出 255px 约 128px,于是__answered命中自身的text-overflow: ellipsis显示为「已…」,__dated被条的overflow: hidden整项裁掉。 - 修复:compact 段(
@media (max-width: 767px),与.is-compact的 768px 断点同源)内.rectification-timeline的padding-inline改为var(--space-4)单项;读数column-gap收到var(--space-2);.rectification-timeline__dated在该段display: none。桌面规则与条高(64 / 56px)一字未动,滚动锚定逻辑未动。组件仍无条件渲染全部五个 span,轴的aria-label仍由五项文本拼成,被隐藏的一项对辅助技术不丢失。 - 验证:
frontend/tests/rectification-mobile-timeline-readout-20260917.test.tsx6 条(渲染断言五项文本与aria-label一致;按保守逐字宽度断言 375 与 390 下四项 + 3×8px 间隙约 292px ≤ 343px / 358px,并断言旧口径 383px > 255px;compact 段声明断言去 inset、收 gap、隐藏第五项、未放开 nowrap 与条高;桌面段断言仍带 inset 与 13px)。frontend/tests/rectification-timeline-20260909.test.ts的移动端padding-inline断言同步改写并附原值 / 新值 / 原因。全套npm test3420 tests / 31 fail,失败清单与基线(3414 / 31,无 Docker 集)逐条相同;tsc --noEmit0 错、npm run lint0 error、next build --webpack后/仍○ Static、首屏 gzip 620106 → 620128 字节(+0.004%)。 - 防复发:新增测试把「compact 段不得含
--assistant-content-inset」「第五项在 compact 下隐藏」与宽度预算写成断言;globals.css与组件注释都改掉了原来「四项短文本在 375px 下还有余量」的错误说法,并写明再加一项必须重算预算而不是直接追加 span。 - 相关记录:BUG-478(跳到最新与滚动锚定归属)、BUG-919(同一次真机走查的另一条)。另记一条口径冲突待产品裁决:
frontend/DESIGN.md§10 的禁列表明确写「已对照 N 件经历」不上条、仍归交付卡,而cfb41daf把它加到了条上且未改 DESIGN;本轮只在手机上把它移出可见行,桌面上是否也撤掉未擅自决定。 - 复发自:无
- 修复版本:待发布
BUG-919 | 「跳到最新」浮层压住选项卡最后一个可点选项
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
frontend/src/app/globals.css.rectification-workspace__chat .conversation/.message-list的底部留白 - 用户现象:iPhone 上「跳到最新」胶囊浮在选项卡最后一行上,选项被遮掉一半,正中间不好点。
- 触发条件:校正对话滚到尾部附近、浮层可见(距底 > 96px)时,末条内容是一张多行选项卡。
- 根因:浮层
.jump-to-latest是position: absolute; bottom: 100%挂在 composer 顶边上,占据 composer 上方 44px 触控高 + 自身space-3= 56px 的带。而.message-list的底部留白恰好等于--rectification-jump-clearance(同样 56px),与浮层带严丝合缝,末条内容顶到浮层正下方;容器pointer-events: none只放行 44px 按钮本体,按钮正好落在选项行中央,于是「遮一半 + 中间点不动」。 - 修复:
.message-list底部留白改为calc(var(--rectification-jump-clearance) + var(--space-3));.conversation追加scroll-padding-block-end: var(--rectification-jump-clearance),让任何 scroll-into-view 也落在带之上。连同.conversation自身的space-4,静止时末条下方共 84px(56px 浮层带 + 28px 空气)。浮层的位置、居中对齐与可见条件(BUG-478 的归属)一字未动,--rectification-jump-clearance的取值不变(时间轴 56px 条高仍与它同源),use-conversation-scroll-anchor未动。 - 验证:
frontend/tests/rectification-mobile-timeline-readout-20260917.test.tsx两条声明断言(留白表达式与scroll-padding-block-end;浮层absolute/bottom: 100%/justify-content: center/pointer-events: none/ 44px 与conversationAnchorThreshold = 96未变)。全套门禁数字同 BUG-918。真机走查(375 / 390 宽)留在docs/testing/rectification-mobile-timeline-readout-20260917.md:本机无 Chrome、无真机。 - 防复发:测试锁住「留白 = 浮层带 + space-3」这条表达式,任何把它改回等于浮层带的提交会红。CSS 注释写明了为什么不做到「任何滚动位置都不重叠」——那需要留白 ≥
conversationAnchorThreshold(96px) + 56px = 152px,在 667px 高的手机上等于凭空吃掉一整行选项,而浮层只在读者已经向上滚过该阈值后才出现。 - 相关记录:BUG-478(两个聊天面统一用
useConversationScrollAnchor与JumpToLatestButton,并把浮层改成居中)、7a4360d8(同一现象 2026-08-25 的旧修法是把胶囊右对齐,已被 BUG-478 的居中统一取代,本轮未推翻它)、BUG-918 - 复发自:无
- 修复版本:待发布
BUG-920 | iPhone 键盘收起 / 刷新后整页上移,顶栏点不到
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
ViewportScrollLock、layout.tsx - 用户现象:刷新或收起键盘后顶栏滚出屏幕,底部留下空白,点不到 tab。
- 触发条件:iPhone Safari 上聚焦输入框弹出键盘,随后收起或带着偏移刷新。
- 根因:iOS 不支持
interactive-widget=resizes-content,键盘弹出时滚动window。html, body { overflow: hidden }挡住用户拉回,reload 又按scrollRestoration=auto还原scrollY。代码里原先没有 window 级复位。 - 修复:挂载时
history.scrollRestoration = "manual"并scrollTo(0,0)。visualViewportresize/scroll、pageshow、orientationchange、focusout(300ms)在键盘已收且scrollY > 0时复位。键盘打开期间不动。 - 验证:
frontend/tests/mobile-viewport-scroll-lock-20260917.test.ts。真机待核。 - 防复发:不得靠用户自己滚回去;键盘打开时不得抢 Safari 的露出输入框滚动。
- 相关记录:BUG-918、BUG-919、BUG-921
- 复发自:无
- 修复版本:待发布
BUG-921 | 顶栏积分块比「当前盘面」扁
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
.credit-button、.chat-header-rectification .rectification-board-peek、手机顶栏行高 - 用户现象:手机顶栏右侧积分「✦ N」又宽又矮,和旁边「当前盘面」不成一套。
- 触发条件:校正页顶栏同时出现盘面芯片与积分块。
- 根因:
.chat-header-actions button把高度写成 32px,比.credit-button更具体,积分块被压到 32px 却仍强制min-width: 64px、13px 字。盘面芯片用另一套 44px / 14px。两枚都塞在 46px 行里。 - 修复:两枚共用 40px 高、14px tabular、同圆角同内边距;去掉积分
min-width。::beforeinset -2px 把热区扩到 44px。≤767px 顶栏行52px + safe-area(原 64px 覆盖改为与任务书 52px 对齐)。 - 验证:CSS 声明断言高度/字号/圆角一致、无 64px min-width;
touch-target-contract仍绿。 - 防复发:顶栏两枚芯片必须走同一组声明,不得再各写一套高度。
- 相关记录:BUG-695、BUG-920
- 复发自:无
- 修复版本:待发布
BUG-922 | 咨询追问被提示词允许跳过排盘工具,整轮失败
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
windowJyotishInstructions、jyotishInstructions - 用户现象:申报时段会话连发短追问得不到回答,提示「Agent 未完成必要的方法与计算步骤,本次不会扣点」。
- 触发条件:本命或申报时段模式下发「?」「你在说什么鬼」一类追问。
- 根因:系统指令写「简单追问可复用已有 packet / context、不调工具」;
contractReady()却要求每次请求恰好一次成功排盘调用。跨请求没有可复用 packet(缓存只在单次请求内)。 - 修复:两处指令改为每一轮(含短追问、澄清、抱怨)都必须先调用排盘工具,并写明计算只在本请求内有效。
- 验证:
consultation-birth-time-mode.test.ts契约;既有 Agent 暴露工具测试仍绿。 - 防复发:不得再写「follow-ups may use the existing」。
- 相关记录:BUG-205、BUG-214、BUG-286、BUG-923
- 复发自:无
- 修复版本:待发布
BUG-923 | 第 0 步未强制排盘工具,提示词与运行合同对不齐
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
consultationNatalPrepareStep、consultationWindowPrepareStep、consult/route.ts申报时段分支 - 用户现象:同 BUG-922。回执只有 skill 与 runtime-contract-retry,没有任何 tool 步骤。
- 触发条件:同上。
- 根因:提示词是软约束,
toolChoice: "auto"不能保证第 0 步调用排盘工具。申报时段路线甚至没有prepareStep。 - 修复:第 0 步
activeTools仅排盘工具且toolChoice: "auto"(thinking 模式不得发 named / required,见 BUG-282 / BUG-937);后续步骤仍"auto"。窗口路线对称加prepareStep,首次 stream 与合同 retry 使用它。retryForAnswer/ 续写 / 分段仍不强制。未改contractReady()。BUG-937 撤回了本条最初落地的required。 - 验证:natal / window prepareStep 单测;
route.ts源码契约。既有「失败后重试成功仍过门禁」「契约未绿文本丢弃」仍绿。 - 防复发:本命与窗口第 0 步用
activeTools收窄排盘工具,toolChoice只能是auto。咨询侧源码契约禁止required与 namedtoolChoice。提示词侧每轮必调(BUG-922)保留。 - 相关记录:BUG-214、BUG-282、BUG-286、BUG-922、BUG-937、BUG-938
- 复发自:无
- 修复版本:
dc2f2a16(required 已由 BUG-937 撤回)
BUG-924 | 校正会话上普通输入框可用,问题发到 /api/consult
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
use-consultation-run.tssend()、page.tsx普通ChatComposer、use-session-management.ts回退路径 - 用户现象:在生时校正会话里打了一个普通问题,请求打到咨询接口,提示「咨询会话不存在」。
- 触发条件:校正会话已激活但校正面未开(刷新揭幕超时、打开失败不重试、删除或回退落到列表第一条校正会话)。
- 根因:发送链路以「校正面没开」推断「当前是普通会话」;
send()不看sessionType。BUG-505 只覆盖了selectSession。 - 修复:
send()对校正会话不发/api/consult,保留草稿并打开校正面。普通输入框在校正会话上只有禁用态。删除 / popstate /sessions[0]回退不再直接setActiveSessionId到校正会话。 - 验证:
rectification-session-composer-guard.test.ts、rectification-surface-contract.test.ts。 - 防复发:校正会话不得发出
/api/consult;普通输入框在校正会话上不得可用。 - 相关记录:BUG-505、BUG-925
- 复发自:无
- 修复版本:待发布
BUG-925 | 咨询接口把校正会话和「会话不存在」都回 404
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
api/consult/route.ts、use-consultation-run.ts错误映射 - 用户现象:校正会话误发咨询时只看到「咨询会话不存在」。
- 触发条件:
POST /api/consult的sessionId指向session_type !== consultation。 - 根因:查不到与类型不对共用一个 404。
- 修复:查不到仍 404「咨询会话不存在」;类型不对 409
session_not_consultation。前端收到该码不显示错误、不扣点,改走打开校正面。 - 验证:
chat-session-authority.test.ts、rectification-session-composer-guard.test.ts。 - 防复发:不得把两种失败再合成一个 404。
- 相关记录:BUG-924
- 复发自:无
- 修复版本:待发布
BUG-926 | 会话列表只按 id 排,游标按 updated_at,首页不是最近活动
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
local-postgres-client-core.tsorder()、GET /api/sessions、GET /api/payment/packages - 用户现象:侧栏列表每次进来都不一样;当天新建的校正会话不在首页 50 条里。
- 触发条件:打开
/或次级页拉会话列表。 - 根因:兼容层
order()只保留最后一次调用。.order("updated_at").order("id")实际只按id;游标却按updated_at, id。套餐列表同样只按created_at。 - 修复:
ordering改为数组,多次order()追加,SQL 生成order by a, b。路由调用未改。 - 验证:
local-postgres-order.test.ts。无 Docker,未跑test:db翻页重叠断言,见BLOCKED.md。 - 防复发:两次
order()必须都出现在 SQL 里;列表排序键与sessionCursorFilter键一致。 - 相关记录:BUG-553
- 复发自:无
- 修复版本:待发布
BUG-927 | / 与次级页各拉一份会话列表,切页就重拉
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
app/(app)/layout.tsx、session-list-context.tsx、page.tsx - 用户现象:对话、星盘、星历、报告四处侧栏不是同一份,每换一页都重新读一次。
- 触发条件:在
/与/chart/ephemeris/reports之间切换。 - 根因:
/不在(secondary)路由组,离开即卸载 Home;次级页另用useSidebarData再拉一遍。 - 修复:四个页面进
(app)路由组。一份SessionListProvider拉/api/sessions与/api/account。首页不再自己fetchSessions/fetchAccount。侧栏不随切页卸载。按任务书让步,改名/置顶/删除仍由首页注册,未注册时侧栏只读。 - 验证:
session-list-provider.test.ts、sidebar-contract.test.ts、chart-page-view.test.tsx。 - 防复发:
app/里SidebarProvider只许出现在(app)/layout.tsx。外壳搬家必须同步搬走针对page.tsx的源码合同,且交付前必须跑全量npm test与tsc --noEmit,不得只跑定向。 - 相关记录:BUG-745、BUG-926
- 复发自:无
- 修复版本:待发布
BUG-928 | 空的「新对话」落库堆积,两边列表还不一致
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
GET /api/sessions、startNewChat、首页启动落点 - 用户现象:首页 50 条里大量「新对话」;对话页把它们藏起来,星盘等页原样显示。
- 触发条件:点「新建对话」或启动时自动落一个空咨询会话。
- 根因:会话在用户开口前就
POST /api/sessions;列表页不过滤空咨询。 - 修复:列表
GET排除messages = [];详情仍可读。启动和「新建对话」复用已有空咨询。延迟到第一问才落库未做,见BLOCKED.md。 - 验证:
chat-session-authority.test.ts、session-list-filter.test.ts。无 Docker,未跑test:db空会话不入列。 - 防复发:列表路由必须带空咨询过滤;详情路由不得带。
- 相关记录:BUG-553、BUG-927
- 复发自:无
- 修复版本:待发布
BUG-929 | 会话标题类别在后,同名靠墙钟 HH:MM
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
resolveSessionTitle、侧栏行映射、GET /api/sessions的created_at - 用户现象:一列「新对话 / 9月14日 · 生时校正 / 9月14日 · 生时校正 02:14」,看不出类别,同日多条靠新建时的钟点区分。
- 触发条件:新建校正或今日节奏,或同日开第二条。
- 根因:
datedSessionTitle日期在前;uniquifySessionTitle用墙钟。侧栏副标题只在他人盘时出现。 - 修复:新标题「生时校正 · M月D日」「今日节奏 · M月D日」。删除 uniquify。两处侧栏共用
toSidebarSessionRow,副标题为上海时区创建时间。旧标题不批量改。 - 验证:
agent-reply.test.ts、chart-library-session.test.ts、session-open-preserves-identity.test.ts。 - 防复发:不得再给同名标题追加 HH:MM;打开已有会话仍不得改 title / updatedAt(BUG-699)。
- 相关记录:BUG-699、BUG-557
- 复发自:无
- 修复版本:待发布
BUG-930 | 主会话和生时校正回答落在结尾,读不到开头
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
useConversationScrollAnchor、主会话send()、生时校正面提交 / 点选项 / 开场、JumpToLatestButton - 用户现象:问一个问题后视口钉在回答最后一个字上,要读回答得不断上滑找开头。生时校正长旁白同样把本轮开头推出视口。
- 触发条件:主会话或生时校正发出一轮,模型输出超过一屏。
- 根因:跟随语义是「贴底」。
anchored初值为真,ResizeObserver在内容变高时把scrollTop设到scrollHeight;send()还会anchorToLatest()。对短回答两者等价,对长回答则把开头推出视口。 - 修复:同一 hook 改为新一轮把本轮开头(用户行,或没有用户行时的新助手行)钉在滚动容器顶部,流式期间不再跟随。回答长出视口时用已有的「跳到最新」贴底并恢复跟随。末尾动态留白让短回答也能把开头留在顶部。切换会话仍先落底。取代 BUG-041 / BUG-048 的贴底语义。
- 验证:
chat-notice-and-scroll-contract.test.ts源码合同与行为测试;校正面pinLatestTurn三处源码断言。验收方用 Chrome 真实布局骨架页跑过 S1–S4(长历史发问钉顶、短历史留白、先上滑再发问、切会话落底)。S5/S6 见 BUG-931 / BUG-932。 - 防复发:跟随只经
useConversationScrollAnchor,跳到最新只经JumpToLatestButton;send()必须调pinLatestTurn;校正面新轮只在提交文字、点选项、开场/无用户行助手到达时各 pin 一次。 - 相关记录:BUG-478、BUG-041、BUG-048、BUG-931、BUG-932
- 复发自:无
- 修复版本:待发布
BUG-931 | 无用户行的一轮钉不住,随后退回贴底跟随
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
useConversationScrollAnchorapplyTurnSpacer/pinLatestTurn;生时校正点选项、开场、自动续轮 - 用户现象:点选项或开场后新助手行不在顶部;流式一开始视口又钉到最后一个字。
- 触发条件:本轮没有
.message-user,pinLatestTurn()钉的是最后一条.message-assistant。 - 根因:最后一条助手行同时是 turn head 和留白承载。
applyTurnSpacer用head.offsetHeight写--latest-turn-head-height,量到的是「内容 + 上一帧留白」,公式自指后min-height坍成 0,scrollTo被夹住。随后distance ≤ 96解除holdUnpin,anchored翻回真。 - 修复:head 与
lastTurnTail是同一元素时--latest-turn-head-height写0px,该行min-height等于整个视口。选这一路而不是先clearTurnSpacer再量,是因为清变量后仍要等一次布局,offsetHeight在同一帧里不可靠。 - 验证:
chat-notice-and-scroll-contract.test.ts「a turn with no user row…」。 - 防复发:无用户行时 spacer 头高必须是 0;pin 后增高不得把
anchored翻回真。 - 相关记录:BUG-930
- 复发自:无
- 修复版本:待发布
BUG-932 | 闲置会话上滑后子元素尺寸变化被写留白
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
useConversationScrollAnchorfollow()非贴底分支 - 用户现象:在旧会话里上滑阅读时,窗口变宽、图片加载或思考块折叠会把最后一条助手行撑出一段空白,「跳到最新」也可能被顶出来。
- 触发条件:
anchored = false且本轮没有pinLatestTurn,滚动容器子元素高度变化。 - 根因:
follow()在未贴底时无条件applyTurnSpacer,把闲置上滑和「本轮已钉住」当成同一件事。 - 修复:只在
pinnedHeadRef.current非空时写留白。anchorToLatest()与resetKey变化时清 ref 和 CSS 变量。 - 验证:
chat-notice-and-scroll-contract.test.ts「idle history does not write a spacer…」。 - 防复发:未 pin 的上滑不得写
--conversation-viewport;latestBelowFold仍按尾部超出 96px 判定。 - 相关记录:BUG-930
- 复发自:无
- 修复版本:待发布
BUG-933 | 四条源码合同没跟着外壳搬家,门禁卡住
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
chat-navigation-a11y-contract.test.ts、rectification-history-open-20260909.test.ts、rectification-surface-contract.test.ts、chat-session-url.test.ts - 用户现象:会话列表单实现已落地,但 staging 门禁
npm test必红,今天的代码提交上不了线。 - 触发条件:
e4e73f56把<main className="chat-app">和侧栏 props 搬到(app)/layout.tsx,启动改成sessionListReady。 - 根因:四条合同仍按搬家前的
page.tsx形状断言。被保护的性质都还在,是合同陈旧。执行方只跑了定向测试。 - 修复:四条按「原值 / 新值 / 原因」改写,分别断 layout 与 homepage 注册两端,不得删测试。
- 验证:上述四文件;全量
npm test相对cc1a8980少这 4 条红。 - 防复发:见 BUG-927。
- 相关记录:BUG-927、BUG-745、BUG-939
- 复发自:无
- 修复版本:待发布
BUG-934 | 今日星语 / 生时校正入口合同仍找已删除的卡片类名
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
tests/test_daily_and_rectification_entrypoints.py - 用户现象:Python 定向跑这两条必红。不在
CORE_PYTEST_TARGETS,不挡门禁。 - 触发条件:断言
daily-starlanguage-card/birth-rectification-card。 - 根因:
62903b7a把空状态改成问候 + 输入框 + 两枚 pill,类名已全仓删除。既有欠账,不是会话列表单引入。 - 修复:改为断言
starter-entrypill 与startDailyStarlanguageConsultation/openRectificationFromHomepage,并断言旧类名不存在。 - 验证:
pytest tests/test_daily_and_rectification_entrypoints.py。 - 防复发:入口形态变了必须改这条合同,不得把已删除的卡片类名当存在。
- 相关记录:无
- 复发自:无
- 修复版本:待发布
BUG-935 | 列表只剩校正会话且无活跃会话时,普通输入框静默吞发送
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
page.tsxactiveSession回退、send()、普通ChatComposer - 用户现象:账号里只有校正会话时,普通输入框能打字;发送没有任何提示,字消失。
- 触发条件:
activeSession为undefined(列表全是校正,找不到非校正回退),校正面正在打开或打开失败。 - 根因:校正守卫把
?? sessions[0]改成find(非校正)。无活跃会话时activeRectificationSession为假,输入框可用;send()因!currentSession直接return false。 - 修复:
composerLocksAsRectification:无活跃会话但列表有校正会话时按校正占位锁定。send()无活跃会话时打开校正或提示「请先选一条对话」。 - 验证:
rectification-session-composer-guard.test.ts。 - 防复发:无活跃会话不得静默吞发送;列表全是校正时普通输入框不得可用。
- 相关记录:BUG-924
- 复发自:无
- 修复版本:待发布
BUG-936 | 首页永远停在「正在载入账户」,没有超时也没有报错
- 状态:兜底已补、根因待分流
- 首次发现:2026-09-17
- 最近更新:2026-09-22
- 影响面:
/首屏揭幕(app/page.tsx的!hydrated || (!account && !accountError)门)、AppLoadingIndicator、app/layout.tsx - 用户现象:产品负责人转述,一位已有账号的用户在 iPhone Safari 打开
https://staging.jyotisha.chat/,永远停在「正在载入账户 / 同步个人资料与对话记录」,进不去页面。转圈动画还在动。 - 触发条件:尚未定位到具体条件。已排除接口侧。
- 已确认事实(2026-09-17,线上
dc2f2a16):- 该用户登录态下
/api/account、/api/sessions?limit=40、/api/models、/api/rectification/cases/entry-summary全部 200,耗时 0.6–1.1 s,不是接口挂起。 /的预渲染 HTML 本身就含「正在载入账户」「同步个人资料与对话记录」与app-loading结构;转圈是.app-loading-orbit::after的纯 CSS 动画。因此这一屏在客户端 JS 一行都没执行时也会原样显示并继续转。- 兜底逻辑全部活在那段 JS 里:8 秒
bootstrapTimeout(写accountError并setHydrated(true))、prepare 阶段 4 秒揭幕、401 跳/login。JS 没跑 → 这三条都不会发生 → 永远停住、没有任何报错。这与现象完全吻合。 - 首页 HTML 引用 25 个
/_next/static/chunks/*.js,全部同步<script>,任何一个没加载或解析失败,React 就不会 hydrate。实测这 25 个当前都 200。 - 线上 bundle 的语法下限是 Safari 16.4:
1stw4tc266s7a.js(Next 自己的 app-router 运行时)含类静态块class y extends Component{static{this.contextType=...}};089-80cjt8-1t.js(本仓中文断句split(/(?<=[。!?;;])\s*/))与0dmsli_y64717.js(链接识别依赖)含正则后行断言。两者在 Safari < 16.4 都是解析期 SyntaxError,core-js 这类运行时 polyfill 救不了。 - 该下限不是新引入:
next: 16.3.1从仓库首个提交4aa0105f(2026-07-15)就在。所以如果这台设备以前能用,(5) 不是本次原因。
- 该用户登录态下
- 待定位(需要用户侧一条信息即可分流):① 设备 iOS 版本 < 16.4 → 命中 (5);② 某个 chunk 在弱网下没下全或被内容拦截器挡掉 → 无痕窗口 / 清除网站数据后恢复;③ Safari 缓存里留着坏掉的 chunk(chunk 带
cache-control: public, max-age=31536000, immutable)。 - 根因:未确认,不得提前写。兜底补上不等于这台 iPhone Safari 已经修好。
- 修复:根 layout 增加一段与 bundle 无关的内联经典脚本。13 秒后,若仍有
.app-loading且<html data-hydrated>不是"1",把加载内容换成「这个页面没能加载完」和只在点击时刷新的「重新加载」。揭幕成功后由首页装配 hook 写data-hydrated="1",正常路径不闪这一屏。匿名标记只留类别、构建标识和耗时,不留异常原文或请求体。接口超时仍走 bundle 里原来的「连接云端服务超时」,不并进这一屏。本仓断句的后行断言另见 BUG-998,那一项不能把 Next 运行时的语法下限降到 Safari 16.4 以下。 - 验证:
frontend/tests/first-paint-fallback-contract.test.ts锁定 ES5、超时大于 8000+4000、未揭幕才替换、按钮才刷新,以及 hydrate 超时 / chunk 404 / 解析失败 / 旧缓存 / 弱网 / 接口错误六类不互相冒充。未在 iPhone Safari 或断 chunk 的浏览器里走查。 - 防复发:首屏不得把「出错了」的唯一出口放在可能失败的模块 bundle 里。不得自动刷新。见
docs/tasks/TASK-home-bootstrap-reliability-20260922.md与docs/testing/home-bootstrap-reliability-20260922.md。 - 相关记录:BUG-716(
/chart白屏 45 秒,另一条链路)、BUG-998、BUG-1002 - 复发自:无
- 修复版本:未发布(本分支未推送,不得声称 staging 已部署)
BUG-937 | 咨询第 0 步 toolChoice: required 让全部咨询整轮失败
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
consultationNatalPrepareStep、consultationWindowPrepareStep;本命与申报时段两条咨询路线的每一轮 - 用户现象:staging 部署
dc2f2a16之后,本命与申报时段会话每一轮都失败,提示「Agent 未完成必要的方法与计算步骤,本次不会扣点。」回执只有 skill 与 runtime-contract-retry,没有任何 tool 步骤。 - 触发条件:已部署
dc2f2a16的 staging 上发一轮咨询。首轮固定开 thinking。同日晚间本命路线用gpt-5.6-luna发正经问题时,第 0 步强制工具调用被该供应商接受(事件流走到「正在计算本命盘」),所以「供应商一律拒收 required」不能解释所有模型。 - 根因:BUG-923 把两条路线的第 0 步改成
toolChoice: "required"。BUG-282 实证过至少一个 thinking 供应商拒绝非 auto 的tool_choice(原文Thinking mode does not support this tool_choice)。模型目录可切换,这条路仍会整轮失败。供应商拒收走 Mastraerror块,咨询流当时看不见,被翻译成合同未完成(见 BUG-938,本单第一优先)。同日反证:gpt-5.6-luna接受了required,那次截图另立单,不并进本条。 - 修复:第 0 步撤回
required,只留activeTools收窄到排盘工具,toolChoice为"auto"。BUG-922 的提示词(每轮必调、packet 不跨请求)不动。不在本单尝试第 0 步关 thinking 再发 required。activeTools+auto在所有供应商上都成立。 - 验证:
consultation-agentic-runtime.test.ts两条 prepareStep 断言;consultation-workflow-contract.test.ts源码契约禁止required与 namedtoolChoice。部署后真人走查见docs/testing/consult-followup-tool-contract-fix-20260917.md。 - 防复发:咨询侧
consultation-tools.ts不得再写toolChoice: "required"或 named{ type: "tool", toolName };thinking 模式只能用activeTools收窄。该约束以前只锁在校正 Agent 测试与 BUG-282 文字里,咨询侧无测试、任务书作者未检索到,所以没拦住 BUG-923。 - 相关记录:BUG-282、BUG-630、BUG-922、BUG-923、BUG-938
- 复发自:BUG-282
- 修复版本:
8b11ae7d
BUG-938 | 咨询流不处理 Mastra error 块,供应商拒收不可见
- 状态:resolved
- 首次发现:2026-09-17
- 最近更新:2026-09-17
- 影响面:
stream-agent-response.tsconsumeAttempt、agent-observability.ts - 用户现象:同 BUG-937。公开事件没有报错,回执没有 tool 步骤,
modelFinishReason为空,错误码被翻译成runtime_contract_incomplete。合同 retry 用同一套选项再失败一次。本命路线在gpt-5.6-luna上能进计算阶段,说明失败形状不只有「拒收 required」一种,真实错误只能靠本条落地后的日志看到。 - 触发条件:供应商调用失败(含 thinking 模式拒收非 auto 的 tool_choice,以及其它上游错误)。Mastra 1.50.1 把该失败 enqueue 成
{ type: "error" }后关流,不抛给fullStream迭代。 - 根因:
mapChunk/consumeAttempt不判断chunk.type === "error"。校正 Agent 在 BUG-282 已把这条路做成thinking_tool_choice_unsupported,咨询流没有对齐。本条是本单第一优先。 - 修复:遇到
error块立即结束本次 attempt,按原文分类为thinking_tool_choice_unsupported或provider_error,回执追加validation model-stream-error failed。这两个内部码不进公开run.failed枚举,公开层仍是calculation_failed。供应商错误不触发合同 retry。服务端打[consult-provider-error]日志(requestId、内部码、原文头 200 字,不含请求体)。 - 验证:
consultation-agentic-runtime.test.ts两条假流(thinking 拒收 / upstream 502):公开码calculation_failed、无loading-method、retry 不调用、onError收到内部码。agent-observability.test.ts把这两个码原样落日志。 - 防复发:咨询流必须识别 Mastra
error块;不得把供应商原文写进公开事件。合同 retry 只用于「没调工具」,不用于供应商拒收。 - 相关记录:BUG-214、BUG-268、BUG-282、BUG-937
- 复发自:无
- 修复版本:
8b11ae7d
BUG-939 | 出生时间旅程合同仍读已搬走的 app/page.tsx,staging 门禁全红
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
tests/test_birth_time_journey_contract.py_home_surface;Giteabackend-quality-gatevalidate;staging 无法发布e4e73f56之后的提交 - 用户现象:staging 门禁从会话列表单起连续失败,最新一次 run 2763 停在 Python 快速门。线上仍停在上次绿灯
dc2f2a16。 - 触发条件:push 到
staging且命中门禁路径。pytest 跑test_web_onboarding_uses_the_deterministic_free_journey。 - 根因:
e4e73f56把首页从frontend/src/app/page.tsx搬进(app)路由组。BUG-933/934 改了前端四条合同和两条 Python 入口合同,漏了这条已列入CORE_PYTEST_TARGETS的文件。_home_surface()对缺失文件if path.exists()静默跳过,于是断言变成「拼盘里找不到<BirthTimeRectification」,而组件仍在frontend/src/app/(app)/page.tsx。 - 修复:首页表面改为必读
(app)/page.tsx,hooks 仍可选。新增路径锁:PAGE.parts必须以(app)/page.tsx结尾,旧路径不得存在。 - 验证:
pytest tests/test_birth_time_journey_contract.py tests/test_session_management_entrypoints.py tests/test_supabase_user_data_contract.py tests/test_daily_and_rectification_entrypoints.py25 passed。未改产品代码。 - 防复发:扫首页源码的 Python 合同必须必读
(app)/page.tsx,不得对首页文件exists()跳过。外壳再搬家时,CORE_PYTEST_TARGETS里所有_home_surface都要一起改。 - 相关记录:BUG-927、BUG-933、BUG-934、BUG-940
- 复发自:BUG-933
- 修复版本:
39a0d7a9
BUG-940 | 校正守卫后 5 条前端源码合同仍按旧写法断言,门禁继续红
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
chat-composer-queue.test.ts、chat-session-url.test.ts、consultation-entrypoint.test.ts、consultation-recovery.test.ts;Giteabackend-quality-gatenpm test - 用户现象:BUG-939 修好 Python 合同后,门禁 run 2764 仍红。
npm test3484 条里 5 条源码正则失败。线上仍停在dc2f2a16。 - 触发条件:push 到
staging跑全量npm test。先前被 Python 快速门挡住,这 5 条从未在门禁里跑到。 - 根因:BUG-924/935 把校正会话判断收到
composerLocksAsRectification/activateFallbackSession/dropRectificationStoredPending。被保护的性质还在,合同仍按搬家前的字面量断言。BUG-933 只改了另外四条,漏了这五条。 - 修复:按「原值 / 新值 / 原因」改写这五条,不断性质、不删测试。
- 验证:上述四文件
tsx --test58 passed / 0 failed。未改产品代码。 - 防复发:改
page.tsx或校正守卫字面量的轮次必须跑全量npm test,不得只跑定向。源码合同跟着实现搬家,见 BUG-927。 - 相关记录:BUG-924、BUG-927、BUG-933、BUG-935、BUG-939
- 复发自:BUG-933
- 修复版本:
0378d9e0
BUG-942 | 思维链过滤器在句内删词,连词和孤儿标点留给用户
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
public-thinking.ts(已删)、stream-agent-response.ts、思考面板 - 用户现象:思考区出现
"这周哪些事最好先放一放" — are put .这类残句。 - 触发条件:模型 reasoning 含中英夹杂,旧 sanitizer 按句切、从第一个汉字截断、再删 ≥4 字母英文词。
- 根因:一个文本通道当三个用,下游用正则当刀。
- 修复:删除就地删词。provider reasoning 只打截断日志,不进事件。公开思考改为完整
think.step,校验不过整条丢弃。 - 验证:
public-thinking.test.ts5 条;agentic-runtime 两条 reasoning 合同改为「reasoning 不进公开通道」。 - 防复发:新增正则必须能回答「门还是刀」;刀不许进。
- 相关记录:BUG-943、BUG-944
- 复发自:无
- 修复版本:
94c1e81f
BUG-943 | 本命正文首节是参数罗列、尾节是技法审计表
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
consultation-thinking-plan.ts、product-voice.ts、咨询正文 - 用户现象:回答先堆岁差/宫位,文末再贴技法审计表,问题被挤到后面。
- 触发条件:本命咨询走旧
REPORT_HEADING(统一参数 / 领域 / 审计表 / 现代生活)。 - 根因:方法学记账被当成用户读物。
- 修复:正文章节改为「先回答你的问题 / 盘里支持这个判断的地方 / 时间怎么看 / 这周可以做的一件事」。审计表继续走回执折叠面板,不写进正文。
- 验证:thinking-plan / voice / timeline 合同。
- 防复发:正文出现「统一参数」「技法审计表」算 Pass 4 检测命中。
- 相关记录:BUG-298、BUG-942、BUG-944
- 复发自:BUG-298
- 修复版本:
94c1e81f
BUG-944 | 整轮共用 110 秒闸门,分段写作被静默掐断后变成空回答
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
consult/route.tscomposeSection、stream-agent-response.tscomposeByHeadings、领域时长常数 - 用户现象:等约 110 秒后「本次没有生成回答」。回执分不清模型没写和流被掐。
- 触发条件:工具耗时长(实测约 31s/领域),后续逐节写作共用同一个
AbortSignal.timeout(110_000)。 - 根因:分段写作 + 共享闸刀;abort 没有独立运行步。
- 修复:取消逐节
composeSection/section-empty-retry,一次成文。领域时长按 31s 重算。abort 记kind: "abort"。已有正文片段不得再以empty_answer结束。 - 验证:workflow-contract 与 agentic-runtime composeAnswer 合同。后台 job / 断线重放本轮未做,见进度记录让步。
- 防复发:每个 stream 自己的预算;abort 必须留痕。
- 相关记录:BUG-942、BUG-943
- 复发自:无
- 修复版本:
94c1e81f
BUG-945 | 三领域问题被 schema 整次拒绝,一个领域都不算
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
consultation-tools.ts工具入参domains.max、executableDomainPlan - 用户现象:一次问事业+财富+家庭时整轮没有排盘,回答空或只剩道歉。
- 触发条件:模型列出 ≥3 个领域。
MAX_CONSULTATION_DOMAINS因 31s/领域降到 2,schema.max(2)在 execute 前拒绝整次调用。 - 根因:执行上限与计划上限绑在同一个常数上。工具描述承诺截断进
omitted_domains,zod 却整次拒绝,截断逻辑成死代码。 - 修复:schema 上限改为
MAX_CONSULTATION_DOMAIN_PLAN_VALUES = 6;真正执行仍按墙钟上限 2 截断,其余进omitted_domains。描述与 mastra 指令对齐。 - 验证:
consultation-agentic-runtime.test.ts超限计划runWorkflow≥1、omitted_domains非空、schema 接受 3–6 条拒绝 7 条。 - 防复发:计划长度与执行长度必须是两个常数;超限合同必须走到 execute。
- 相关记录:BUG-944、BUG-946
- 复发自:BUG-944
- 修复版本:
e32ce624
BUG-946 | 领域时长改 31s 后 6 条预算断言仍按 21s / cap=3
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
consultation-agentic-runtime.test.ts - 用户现象:无(测试红)。门禁
npm test过不了,镜像不发布。 - 触发条件:
94c1e81f把领域时长改成 31s 后跑全量测试。 - 根因:实现改了常数,断言没跟。三领域调用被 BUG-945 的 schema 拒绝后,合并合同读
undefined。 - 修复:按 MAX=2 / 31s 重写 6 条断言,写原值/新值/原因。不得弱化成
cap >= 1。 - 验证:同文件预算与超限截断用例。
- 防复发:改
CONSULTATION_DOMAIN_DURATION_MS必须同步预算合同。 - 相关记录:BUG-944、BUG-945
- 复发自:BUG-944
- 修复版本:
e32ce624
BUG-947 | 校正流思考分片被条目门整条丢掉
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
rectification-agentic/v9/stream-mapping.ts、think-step-gate.ts - 用户现象:生时校正计算期思考行大面积空白。
- 触发条件:provider
reasoning-delta分片短于 8 字或没有句末标点。先核对经历。(7 字)被acceptThinkStepText判空。 - 根因:本命 Pass 2 的整条门被误用到校正分片通道。
- 修复:选任务书方案 (b)。校正流保留分片通道,改走
acceptThinkingFragment(只判 CJK / 工具名,不改写、不设 8 字下限)。另提供 assembler 给连续分片测可见行。 - 验证:
rectification-step-answer.test.ts,含「连续中文分片最终能产出一条可见思考行」。 - 防复发:条目门不得接到分片通道。新增正则必须能回答「门还是刀」。
- 相关记录:BUG-942
- 复发自:BUG-942
- 修复版本:
e32ce624
BUG-948 | 三类检测器没人调用,范围用户应期被旧口径挖空
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
timing-output-guard.ts、stream-agent-response.ts、consult/route.ts、出生时间模式 - 用户现象:只知道出生范围、或已校正后问应期,日期被挖成「[具体时间已省略]」;同时保证句和个人盘断言在恒等壳下什么都不拦。
- 触发条件:
94c1e81f把守卫改成恒等,Pass 4 只接了方法学记账检测。产品 2026-09-18 授权按模式分开处置。 - 根因:检测器保留了,生产路径没接线。身份壳看起来在保护。旧红线把分钟敏感主题一律挖日期,范围用户不能用。
- 修复:删除恒等壳。Pass 4 按模式:
exact-timing在有盘/窗口模式只记pass4-observe不改写;guarantee全模式pass4-reject,退回 Pass 3 一次,二次丢句;personal-chart仅general_no_birth_time退回,二次整段换成拒绝句。正文先 hold 再一次发出,避免闪两次。 - 验证:timing-output-guard / birth-time-mode / range-reading / birth-accuracy / agentic-runtime Pass 4 合同。范围用户含日期正文原样通过。
- 防复发:全仓不得再出现
guardPreciseTimingOutput/guardGeneralNoBirthTimeOutput/createBirthTimeModeOutputGuard。回执步骤名带pass4-observe:<kind>/pass4-reject:<kind>。 - 相关记录:BUG-298、BUG-943
- 复发自:BUG-298
- 修复版本:
e32ce624
BUG-949 | 思考计划变小后容量算术两条红
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
consultation-session-capacity.test.ts - 用户现象:无(测试红)。门禁过不了。
- 触发条件:本命思考计划改成四标题后,1 域 JSON 1053 < 旧下限 1200;19 轮旧口径不再 > 200,000。
- 根因:断言按旧计划形状写死上下界。
- 修复:按 2026-09-18 实测重算:分节 1053/1267/1267;19 轮详情 533,113 B、旧口径 177,973;50 轮 1,402,384 B。三栏说明。19→50 轮产品口径不变。
- 验证:同文件两条容量合同。
- 防复发:改 thinking plan 形状必须重测这两条,不得放宽成「大于 0」。
- 相关记录:BUG-732、BUG-943
- 复发自:无
- 修复版本:
e32ce624
BUG-950 | Pass 4 整段 hold,正文不再逐字出现
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
stream-agent-response.ts、timing-output-guard.ts - 用户现象:本命 / 窗口 / 一般咨询的正文整段一次性蹦出来,打字机没了,首字延迟等于整段写完。
- 触发条件:
pass4Mode三条路径全开;三个text-delta只产出 1 条answer.delta。 - 根因:
holdAnswer把整段攒进fullOutput,等finishPass4判定完一次发出。948 让步「不闪两次」等于关掉流式。 - 修复:只缓冲当前未闭合句。句子碰到
。!?.!?\n就对这一句跑classifyPass4:无reject立刻send;有reject整句不发。exact-timing的observe不阻塞。methodology整篇重写只允许发生在还没发出任何正文之前;已经发出过句子就只丢后续命中句。流末残句再过一次门。 - 验证:三个
text-delta在verified_chart下 ≥3 条answer.delta;保证句不出现在任何answer.delta且回执有pass4-reject:guarantee;日期句照发且有pass4-observe:exact-timing。 - 防复发:Pass 4 是句门不是刀,不得再整段 hold。合同锁 ≥3 条 delta。
- 相关记录:BUG-948
- 复发自:BUG-948
- 修复版本:
cd4775ae
BUG-951 | 没有出生分钟时,一句拒绝顶掉整段回答
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
timing-output-guard.ts、stream-agent-response.ts - 用户现象:一般知识句和个人盘断言写在同一段时,整段被换成「这部分需要具体出生分钟才能判断」。
- 触发条件:
general_no_birth_time二次落地;无composeAnswer的一般路径没有重写机会。 - 根因:二次命中
personal-chart时整段替换成GENERAL_NO_BIRTH_TIME_REFUSAL。旧实现是按句挖掉。948 任务书「兜底句替代整段」措辞造成。 - 修复:
dropRejectedClauses只丢guarantee/personal-chart/methodology命中句。其余原样。只有全部句子都丢掉、正文为空时才发兜底句。保证句静默删句,回执留pass4-reject:guarantee。 - 验证:混合文本二次后一般知识句仍在,个人盘句与保证句不在,无残留
。。;全丢用例仍落到兜底句。 - 防复发:不得再对
general_no_birth_time二次整段替换;合同锁「preserving general knowledge」。 - 相关记录:BUG-948、BUG-950
- 复发自:BUG-948
- 修复版本:
cd4775ae
BUG-952 | 没有出生时间时的具体日期既不记录也不拦
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
timing-output-guard.ts - 用户现象:无出生分钟模式下写了具体日期,回执没有任何痕迹。
- 触发条件:
classifyPass4对exact-timing在general_no_birth_time直接continue。 - 根因:948 让步 4 把「日期不删字」写成「连 observe 也不记」,恰恰在最没有依据给日期的模式下一笔痕迹都没有。
- 修复:该模式下
exact-timing记observe,仍不拦、不删字。 - 验证:含日期正文原样通过,回执有
pass4-observe:exact-timing。 - 防复发:
exact-timing在所有pass4Mode下都要能观察到。 - 相关记录:BUG-948、BUG-950
- 复发自:BUG-948
- 修复版本:
cd4775ae
BUG-953 | 校正流 token 级 thinking 是死链,按 P2 删除
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
rectification-agentic/v9/stream-mapping.ts、think-step-gate.ts - 用户现象:无(用户看不见这条通道)。947 修好的是测试里的死函数。
- 触发条件:生产校正流对
reasoning-delta在step-answer.ts直接{ kind: "none" }。toPublicThinkingDelta/mapStreamChunkToThinking零生产调用方。 - 根因:上游 P2 规定 provider reasoning 永不外发。把 reasoning 分片推给浏览器本身就违反 P2。947 按任务书方案 (b) 留了这条没人走的路。
- 修复:删除
toPublicThinkingDelta、mapStreamChunkToThinking、InternalThinkingDeltaEvent、acceptThinkingFragment、createThinkingFragmentAssembler。acceptThinkStepText保留。测试翻转成否定合同:源码不得出现这些函数名,reasoning-delta必须丢弃。 - 验证:全仓
frontend/srcgrep 不到上述函数;相关测试翻转后仍绿,测试总数不降。 - 防复发:校正流不得再把
reasoning-delta映射成对外事件。将来校正面板若要显示判断依据,走咨询流 Pass 2think.step。 - 相关记录:BUG-947、BUG-942
- 复发自:BUG-947
- 修复版本:
cd4775ae
BUG-954 | 申报时段咨询自 08-21 起每轮秒败:窗口 Agent 绑了 skill 却没把方法块写进系统提示
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
src/mastra/index.tswindowJyotishInstructions/getWindowJyotishAgent、src/mastra/skill-binding.tsjyotishSkillBoundProcessor;declared_birth_window模式的每一轮 - 用户现象:staging 申报时段会话问「未来半年我事业如何?」,
run.failed code=runtime_contract_incomplete,公开事件只有run.started→skill.started/completed→activity loading-method→run.failed,回执零 tool 步。 - 触发条件:任何
consultationMode=declared_birth_window的请求,与问题内容、模型、供应商无关。 - 根因(服务端日志实证,requestId 脱敏为前八位
95de38fd):docker logs jyotisha-staging-web-1命中两条[WORKFLOW] Error executing step ... input-processor.step.processor:jyotish-skill-bound: Error: Jyotish skill method is not bound into the system prompt for jyotish-vedic-astrology(两次 attempt 各一条)。- 同一 runId 的
[agent-observability]:toolCalls: []、modelStepCount: 0、inputTokens: 0、outputTokens: 0、run.total durationMs: 69、retryCount: 1、errorCode: runtime_contract_incomplete。模型从未被调用,整轮 69 毫秒结束。 - 代码对应:
jyotishSkillBoundProcessor在输入处理阶段断言系统提示里含BOUND_METHOD_MARKER,缺失即abort()。jyotishSkillMethodBlock只在本命jyotishInstructions里插值;windowJyotishInstructions从未包含它,而getWindowJyotishAgent照样...jyotishSkillBinding()。 - 时间线:处理器由
d04fc30b(2026-08-18)引入;窗口 Agent 由9958e00a(2026-08-21)新建,诞生时就带绑定、不带方法块。申报时段路线自 2026-08-21 起每轮必败,约四周。
- 为什么没被发现:
abort()被翻译成与「模型没调工具」相同的公开码runtime_contract_incomplete。见 BUG-955。 - 修复:选任务书方案 (a)。抽出
jyotishSkillMethodCoreBlock(方法主体 + marker,不含本命 Level 2 报告骨架),窗口指令注入该块;本命仍用带骨架的jyotishSkillMethodBlock。窗口口径写明不采用 Level 2 骨架,并优先于方法块里的报告模板 / 应期表述。 - 验证:遍历式源码合同——凡
index.ts里 attachjyotishSkillBinding()的 Agent,instructions 必须含 marker;窗口 AgentgetInstructions()后jyotishSkillBoundProcessor不 abort。部署后仍需在 staging 真发一轮申报时段提问,回执出现run-jyotish-window-consultationcompleted。 - 防复发:源码遍历合同;不得用删掉
jyotishSkillBinding()让窗口路线通过。 - 关联记录:BUG-205、BUG-214、BUG-922、BUG-923、BUG-937、BUG-938、BUG-955
- 复发自:无(与 922/923/937 同现象、不同根因;那三条均未覆盖窗口 Agent 的提示词装配)
- 修复版本:
5b6abc23
BUG-965 | 侧栏「星盘 / 星历 / 我的报告」点了不跳转,账户入口也没反应
- 状态:resolved(由 BUG-967 修复)
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
AppSidebar的NAV_PAGES链接与页脚账户入口;staging877128ce - 用户现象:产品负责人在 staging 的对话页点侧栏「星盘」「星历」「我的报告」没有任何跳转,点「个人资料」也没反应。对话本身可用(同一时段成功发出并收到了申报时段回答)。
- 触发条件:标签页跨过部署。产品确认:刷新之后一切正常,刷新前点什么都没反应。该标签页在 2026-09-18 17:12 的 staging 部署(
9cdcf96b→877128ce,容器重建)之前就已打开。 - 已排除(本轮静态核对):
- 路由存在:
/chart、/ephemeris、/reports在 staging 均返回 200(未登录也返回 200,说明是客户端门禁而非路由缺失)。 - 当前构建自洽:
/chart首屏引用的/_next/static/**资源抽查全部 200。 - 链接是真链接:三项导航是
SidebarMenuLink→next/link的<Link href>,onClick={leaveChat}只做persistLoginSessionReturn()与关抽屉,没有preventDefault。 - 没有覆盖桌面侧栏的
pointer-events: none或inert:inert只出现在移动端抽屉、时间轴折叠体、SidebarInset(insetInert = modalOpen,只盖正文区不盖侧栏)。 - 次级页页脚是
Link href="/"(账户菜单只在/),因此「在次级页点头像」应当跳回首页而不是没反应。
- 路由存在:
- 根因:见 BUG-967。标签页跨过部署后,客户端导航要拉的构建产物已被新镜像替换;Next 16.3.1 自带的构建不匹配回退在自托管 + 预取缓存这条路上没有把用户带到整页重载,点击表现为静默无反应。已经加载好的对话页因为代码都在内存里,照常工作。
- 修复:BUG-967 的版本比对自愈。旧标签页在下一次站内跳转走
window.location.assign。 - 验证:见 BUG-967。
- 防复发:见 BUG-967。不得再把「刷新就好」当成侧栏无响应的结案。
- 关联记录:BUG-967、BUG-204、BUG-744
746、BUG-926929、BUG-936 - 复发自:BUG-204 同类(跨发布客户端导航),那次修的是
deploymentId/?dpl=,在 Vercel 上靠部署路由 404 触发 MPA;本仓是自托管单镜像,那条路径不成立。 - 修复版本:
9ccb65b7
BUG-966 | 星盘 / 星历 / 我的报告进入时矮等待块推挤成高正文
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
chart-page-view.tsx、ephemeris-page.tsx、personal-report-center.tsx、use-chart-page.ts、SecondaryHeader - 用户现象:从侧栏进「星盘 / 星历 / 我的报告」有一瞬间抖动。
- 触发条件:进入这三页,尤其是同一会话里每次进入(没有首屏缓存)。
- 根因:三页各写各的等待布局——先渲染 46px 标题加一行短等待文案,数据到达后整块换成高正文。
SecondaryHeader的note未到时不渲染,标题区二次填充。use-chart-page进页才 fetch,零缓存,每次都重放中间态。产品 2026-09-18 选定方案一:消灭中间态,不加 spinner,红线不动。 - 修复:三页共用
SecondaryPageShell(标题行高度固定、note 槽占位不塌、内容区撑满剩余视口,等待文案居中在该区)。星盘 / 星历 / 报告首屏进模块级缓存,同一会话再进直接用上次结果并后台静默刷新。侧栏三项在pointerenter/pointerdown预热数据。等待句仍是静态句(「这一张盘还没拿到。」等),没有 spinner / 骨架 / 「正在加载」。 - 验证:
frontend/tests/secondary-page-entry.test.ts(三页 import 共享外壳、暖缓存第二次不经 null 等待、预取命中同一 inflight、CSS 内容区 min-height:0 且等待居中);既有chart-page-view/ephemeris-page/ sidebar 合同。浏览器量高度见docs/testing/secondary-page-entry-20260918.md(无 Chrome)。 - 防复发:次级页进入不得再各写一套等待布局。揭幕后不得加 spinner / 「正在加载」;骨架禁令仅在星盘盘位被 2026-09-24 BUG-1016 决策有限推翻,其它页不变。缓存必须模块级,组件 unmount 不能丢掉。
- 关联记录:BUG-716、BUG-717、BUG-745
- 复发自:无
- 修复版本:
9ccb65b7
BUG-967 | 跨部署旧标签页的客户端导航静默失效
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
AppLink、app-navigation.ts、StaleBuildGuard、侧栏NAV_PAGES与次级页账户入口、Web 镜像NEXT_PUBLIC_GIT_COMMIT - 用户现象:见 BUG-965。
- 触发条件:标签页在一次部署之前打开,部署后不刷新,点站内次级页或账户入口。
- 根因(查证 Next 16.3.1 源码,不得跳过):
- 不匹配回退写在
fetchServerResponse:RSC 不是text/x-component、或非 2xx、或x-nextjs-deployment-id/flightResponse.b≠getNavigationBuildId()时才doMpaNavigation。<Link>进页时已经把/chart/ephemeris/reports的 Flight 预取进内存;点击走缓存、不再 fetch,这条检查根本不跑。 - 自托管只有当前镜像。Caddy 对
/_next/static没有改写,只是reverse_proxy web:3000。?dpl=是 Vercel 按部署分流的参数,在本机被忽略。目标路由在新镜像上仍 200 返回新的 Flight,!res.ok也不成立。 handleHardNavError/useNavFailureHandler编译期开关__NEXT_APP_NAV_FAIL_HANDLING,standalone 默认关。旧 chunk 的ChunkLoadError若被 router reducer 吃掉,既有StaleClientRecovery(只听window.error/unhandledrejection)也看不见。客户端导航于是表现为点击无反应;挂起的useTransition还能让同一页的账户入口一起没反应。
- 不匹配回退写在
- 修复:编译期
NEXT_PUBLIC_GIT_COMMIT(Docker 构建时与NEXT_DEPLOYMENT_ID同值写入)对照/api/health.deployment.gitCommit。页面重新可见时拉一次 health,导航前读这份快照。不一致则window.location.assign,一致则继续客户端路由。health 失败不强制刷新。无轮询、无定时重载。 - 验证:合同测试——版本不一致走
assign,一致走传入的router.push/ 不 preventDefault;health 失败或 commit 为unknown不强制跳。真机跨部署见docs/testing/secondary-page-entry-20260918.md。 - 防复发:站内跨布局跳转走
AppLink/navigateAppPath,不得假定 Next 的 mismatch fallback 在自托管上会救场。不得用「每次导航都整页重载」或定时刷新糊过去。 - 关联记录:BUG-965、BUG-204
- 复发自:BUG-204
- 修复版本:
9ccb65b7
BUG-955 | 输入处理器 abort 与「合同未完成」同码,窗口装配失败藏了四周
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
stream-agent-response.ts、agent-observability.ts、jyotishSkillBoundProcessor - 用户现象:窗口路线 abort 后公开码仍是
runtime_contract_incomplete,与「模型没调工具」无法区分,观测上像 BUG-922。 - 触发条件:系统提示缺
BOUND_METHOD_MARKER,Mastra 发tripwire块且不调模型。 - 根因:
consumeAttempt把 tripwire 当未知块丢掉,流正常结束;contractReady()为假后走合同重试,最终抛同一公开码。 - 修复:识别
tripwire/ 「not bound into the system prompt」为内部码skill_binding_failed;回执追加validation skill-binding-abort failed;打[consult-binding-error](requestId + 内部码,不含提示词);不走合同重试。公开层仍用runtime_contract_incomplete,靠回执步骤名区分。无工具导致的合同未完成另记runtime-contract-incomplete。 - 验证:假流 tripwire vs 空流,回执步骤名分别为
skill-binding-abort/runtime-contract-incomplete;toAgentObservabilityErrorCode两个码都能落。 - 防复发:装配失败不得再并进合同未完成;参照 BUG-938 对供应商 error 块的同类处置。
- 关联记录:BUG-954、BUG-938、BUG-922
- 复发自:无
- 修复版本:
5b6abc23
BUG-956 | 合同未绿时静默丢弃模型正文,用户只看到「未完成」
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
stream-agent-response.ts - 用户现象:合同没绿时模型写过的正文被丢掉,用户只看到「Agent 未完成必要的方法与计算步骤」。
- 触发条件:
requireTool路径上工具成功次数为 0,但有非空text-delta。 - 根因:
outputText在contractReady()为假时直接 return,重试后仍未绿就抛runtime_contract_incomplete,正文从未发出。 - 修复:未绿期间仍缓冲正文。两次 attempt 后仍未绿、且零次成功计算、缓冲非空:交付该正文,文末附服务端说明「这次没跑完星盘计算,上面是模型直接写的,先看着。」,回执
validation contract-degraded failed,run.completed。无工具也无正文:维持runtime_contract_incomplete。不放宽contractReady()的「每请求一次真实计算」。二次成功计算仍失败,不走降级。 - 验证:假流无工具+有正文 → 正文 + 降级说明 +
contract-degraded;假流无工具+无正文 →runtime_contract_incomplete。 - 防复发:合同未绿不得再整段丢正文;既有「合同完成前的旁白不进可见回答」仍保留,成功计算后清空缓冲。
- 关联记录:BUG-954
- 复发自:无
- 修复版本:
5b6abc23
BUG-957 | 窗口计算由模型决定调不调,对结果无信息增益
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
consultation-tools.tsrun-jyotish-window-consultation、stream-agent-response.tswarmup、consult/route.ts申报时段分支 - 用户现象:只知道一段出生范围时,回答仍取决于模型是否调用计算工具;漏调就整轮失败或降级。
- 触发条件:
consultationMode=declared_birth_window。窗口工具inputSchema只有question,出生数据完全服务端绑定。 - 根因:由模型决定调不调对计算结果没有信息增益,只多一条失败路径(BUG-205/214/922/923/937)。
- 修复:缓存改挂在 agent context 上。服务端在模型循环前
precomputeWindowConsultation,进度经 warmup 写入同一 NDJSON 流;成功则把 packet 以服务端确定性消息注入本轮。工具仍留在窗口 Agent 上,再调用命中同请求缓存且consultationToolSuccessCount不得加第二次。预跑失败合同保持红,模型仍可调工具,既有 retry + 降级仍在。本命不预跑。没有toolChoice: "required"。contractReady()仍是「本请求恰好一次成功的真实计算」,预跑算那一次。 - 验证:预跑成功且模型不调工具 → 合同绿、回执有
run-jyotish-window-consultationcompleted、正文送达;预跑后模型再调 → 缓存命中、成功次数为 1;预跑失败且模型只写字 → 仍降级。源码合同锁窗口工具仍 attach、本命工厂不变。 - 防复发:窗口计算不得再依赖模型选不选工具;缓存不得回到工厂局部
let;咨询侧不得再写toolChoice: "required"。 - 关联记录:BUG-954、BUG-205、BUG-214、BUG-922、BUG-923、BUG-937、BUG-956、BUG-959
- 复发自:无(954 验证通过后按任务书单独做)
- 修复版本:
53d78137
BUG-959 | 降级正文绕过 Pass 4,保证性结论原样送达
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
stream-agent-response.ts合同未绿时的降级交付 - 用户现象:没跑完星盘计算时,模型自己写的「我保证你一定会升职」一类句子会进正文。
- 触发条件:
requireTool: true、零次成功计算、有非空正文、pass4Mode为有盘模式。 - 根因:BUG-956 的降级分支直接
send(uncontractedText),没有走releasePass4Sentences/classifyPass4。降级正文恰恰是没有任何计算依据时写的,保证句和个人盘断言最容易出现在这里。 - 修复:降级正文复用
releasePass4Sentences按句过门,reject整句丢弃,observe照记。CONTRACT_DEGRADED_NOTE是服务端文案,不过 Pass 4,贴在末尾。全部句子被丢弃时:general_no_birth_time发拒绝句再附说明;其余模式维持runtime_contract_incomplete,不发只有降级说明的空回答。不在句内改写。 - 验证:假流降级 +
verified_chart+ 保证句与安全句 → 保证句不出现在任何answer.delta,回执同时有contract-degraded与pass4-reject:guarantee;全丢不发 note-only;一般模式全丢发拒绝句 + 说明。 - 防复发:降级路径必须复用
releasePass4Sentences,不得另写分句或句内替换。Pass 4 是门不是刀。 - 关联记录:BUG-956、BUG-948、BUG-950、BUG-951
- 复发自:BUG-956(降级路径漏接 Pass 4)
- 修复版本:
5e8b2ac1
BUG-960 | 两次 attempt 的正文被拼起来一起送出
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
stream-agent-response.tsuncontractedText - 用户现象:合同重试后降级交付把第一稿和重试稿拼成一段,同一轮内容说两遍。
- 触发条件:第一次 attempt 写过字,合同仍红,retry 再写一段不同的字。
- 根因:
uncontractedText += text只在contractReady()为真时清零。合同一直红就一路累加,attempt 之间没有边界。 - 修复:每次
consumeAttempt开始时清空uncontractedText。降级只交付最后一次 attempt 的正文。 - 验证:假流 attempt1「第一段。」、attempt2「第二段。」、无工具成功 → 降级正文含第二段、不含第一段。
- 防复发:未绿缓冲按 attempt 隔离,不得跨 attempt 累加。
- 关联记录:BUG-956、BUG-959
- 复发自:BUG-956
- 修复版本:
5e8b2ac1
BUG-961 | 降级之后还会空跑一轮 compose
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
stream-agent-response.ts降级后的收口 - 用户现象:无(用户看不到 compose 产出)。本命形状的事件序列里出现
phase.started{phase:"compose"},白花一次模型调用与一段墙钟。 - 触发条件:降级交付成功,且该路径传入了
composeAnswer(本命线)。 - 根因:降级分支执行后流程继续走到
publishFindings/composeOnce。 - 修复:成功降级后设
deliveredDegraded,跳过 Pass 2 / Pass 3 / compose / answer-retry,直接run.completed。全丢的非一般模式在 compose 之前抛runtime_contract_incomplete。 - 验证:降级路径
composeAnswer调用次数为 0,事件无phase.started{phase:"compose"}。 - 防复发:降级是终态交付,不得再进 compose。
- 关联记录:BUG-956、BUG-959
- 复发自:BUG-956
- 修复版本:
5e8b2ac1
BUG-958 | 窗口指令「必须调工具」与「不得给应期」未写清,模型可能跳过计算或拒答
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
src/mastra/index.tswindowJyotishInstructions - 用户现象:应期类问题在窗口模式下可能因为「精确应期不可用」而跳过计算或整题拒答。
- 触发条件:申报时段咨询问到时间 / 应期。
- 根因:窗口指令写了必须调工具,也写了不得给精确应期,两句之间没有显式仲裁。
- 修复:补一句:应期类问题仍然要先调工具,再用稳定层给方向性回答,并说明哪部分需要出生分钟;不得因为精确应期不可用而跳过计算或拒答整题。
- 验证:
consultation-workflow-contract.test.ts源码断言。 - 防复发:窗口指令源码合同锁住该句。
- 关联记录:BUG-954
- 复发自:无
- 修复版本:
5b6abc23
BUG-962 | 聊天正文列表没有项目符号
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
.message-markdown ul.markdown-list/ol.markdown-list(聊天正文,不含个人报告 Markdown) - 用户现象:回答里的清单只是往右缩进的孤行,看不出是列表;正文段落靠左、列表块缩进,页面出现两级左边界。
- 触发条件:助手回答含 bullet / 有序列表,或
promoteDefinitionLists提升出的列表。Tailwind v4 preflight 把ul, ol的list-style清成none。 - 根因:
.markdown-list设了display: grid、gap、padding-inline-start,没有恢复list-style。 - 修复:
ul.markdown-list用list-style-type: disc,ol.markdown-list用decimal;::marker取--color-ink-secondary。保持display: grid。不改personal-report-markdown-view。 - 验证:
frontend/tests/chat-stream-layout.test.ts源码断言该选择器块含list-style-type(ul=disc / ol=decimal)。 - 防复发:
.markdown-list规则块必须含list-style;不得再只设缩进不设符号。 - 相关记录:BUG-356、BUG-963、BUG-964
- 复发自:无
- 修复版本:
dccedf37
BUG-963 | 四标题口语散文被自动提升成列表
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
promoteDefinitionLists(frontend/src/lib/chat-definition-lists.ts) - 用户现象:申报时段回答里三段带冒号的散文被改写成三条列表项,和文末真正的行动清单同一种样式。
- 触发条件:BUG-943 之后本命/窗口正文是四标题口语体,段落经常写成「你这半年的事业是 X:……」「推的是火星:……」。连续 ≥2 段被旧判据当成并列定义项。
- 根因:
DEFINITION_LINE = /^(.{2,80}?)[::](.+)$/的 term 允许含句号,body 只要求length >= 4。一段一百多字的多句散文照样算「解释」。 - 修复:收紧,不删除。连续 ≥2 段且同时满足:term 不含句末标点
。!?;!?;与换行;body 是单句(不含句末标点,或仅以一个句末标点收尾);body ≤ 40 字。仍跳过 fence 与已有列表。BUG-356 的并列短项(如「情绪与精力契合。」)继续提升。 - 验证:
frontend/tests/chat-definition-lists.test.ts既有 BUG-356 fixture 仍绿;新增申报时段口语 fixture 保持三段原文,不产出- **term**:。 - 防复发:不得放宽 term 句末标点或 body 长度上限来「多提升一些」;散文冒号不是列表。
- 相关记录:BUG-356、BUG-943、BUG-962
- 复发自:BUG-356(并列短项提升判据过宽,口语体冒号段被误伤)
- 修复版本:
dccedf37
BUG-964 | 思考条与正文之间空 56px
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
.consultation-thinking-report、.message-stage-and-answer、会话作用域.message-markdown h2 - 用户现象:思维链和正文中间间隔好大。harness 实测思考条底到正文首标题 56px。
- 触发条件:会话里助手回答以
h2开头(四标题口语体的「先回答你的问题」)。 - 根因:56px =
.consultation-thinking-report { gap: 24px }+ 首个h2的margin-top: 32px。.message-markdown > *:first-child { margin-top: 0 }被更高特指度的.conversation:not(.is-empty):not(.is-rectification) .message-markdown h2 { margin: var(--space-8) 0 var(--space-3) }盖掉。另一条路径.message-stage-and-answer的 gap 只有 8px。 - 修复:会话作用域补
.message-markdown > *:first-child { margin-top: 0 }(无!important)。两条路径的 gap 都改读--consult-think-answer-gap(var(--space-3)= 12px)。会话.markdown-list的 gap 从--space-4(16px)降到--space-2(8px)。 - 验证:
frontend/tests/chat-stream-layout.test.ts锁会话 first-child 清零、两处 gap 同一 token、列表符号恢复。headless Chrome:gapPx=12,h2MarginTop=0px,listStyle=disc。截图docs/testing/chat-markdown-list-20260918-after.png。 - 防复发:思考条与正文间距不得再各写各的;会话作用域不得让首元素
h2再带--space-8的 margin-top。 - 相关记录:BUG-962、BUG-356
- 复发自:无
- 修复版本:
dccedf37
BUG-968 | 账户弹窗打开后整个弹窗点不动,退出登录做不了
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
src/app/(app)/layout.tsxAppShell、src/app/(app)/page.tsxAccountDialogOverlay渲染位置、use-home-shell-registration.tsinsetInert;staging877128ce - 用户现象:点侧栏「个人资料 / 退出登录」等任一账户入口,弹窗出现但关闭按钮、表单、确认退出全部点不动;正文区同时点不动。刷新后依旧。
- 触发条件:任何让
modalOpen为真的弹窗(五个账户弹窗、onboarding 付费墙)。 - 根因:
e4e73f56把SidebarInset搬进(app)/layout.tsx后,inert={modalOpen}罩住了整个children,而AccountDialogOverlay由page.tsx渲染、没有 portal,落在 inert 子树里。搬家前弹窗是SidebarInset的兄弟节点。 - 修复:选 (a)
createPortal(..., document.body)。AccountDialogOverlay与 onboarding 付费墙都挂到document.body;SidebarInset的inert={modalOpen}和移动端抽屉isMobile && openMobile分支不动。选 portal 而不是把节点交给 layout,是为了不改registerShellControls协议、并保住现有 DOM 结构与样式。 - 验证:
frontend/tests/account-dialog-inert-20260918.test.ts断言 overlay / paywall portal 到document.body、layout 仍 inertSidebarInset、role="dialog"渲染不含 inert;现有 overlay / sidebar / a11y 合同见定向测试。真机五弹窗点关闭/提交见docs/testing/account-dialog-inert-20260918.md。 - 防复发:合同测试断言弹窗 portal 到
document.body,且SidebarInset仍吃inert。 - 关联记录:BUG-744
746(侧栏统一)、BUG-926929(外壳搬家)、BUG-965(跨部署标签页,另一问题) - 复发自:无
- 修复版本:
824ecff0
BUG-969 | 生时校正「换一件事问」之后无下文:快照拿不到,两端都不吭声
- 状态:mitigated(快照失败根因仍未由线上日志证实,不得写 resolved)
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:
GET /api/rectification/cases/[caseId]、rectification-agentic-chat.tsxloadCaseSnapshot、rectification-surface-state.tshydrateRectificationCase、确定性回复的题干投递 - 用户现象:口述两轮经历后说「想不到了」,回复「已记录。这题先不计分,换一件事问。」然后没有题卡;右侧盘面停在占位、时间轴无候选点、输入框占位是「再说一件带年月的事」。
- 触发条件:真实 Case 上服务端已落库下一道财务探针(active,绑在该回复轮),GET 快照本地用真实数据行重算完整、客户端解析函数全部通过;客户端却仍是开场时的快照状态。
- 根因:未定。已确认的是链路两端都静默:GET 路由所有异常 503 不写日志,客户端快照失败一律吞掉,边缘与应用层都无 GET 访问日志,PG 未开函数统计。事后无法证明请求是否到达服务端。
- 修复:① GET 每一个非 200(401/400/409/404/503,含 supabase 配置失败)
console.warn一条 JSON(event=rectification_case_get_failed、case_id、status、code、reason=safeToolErrorCode,不含用户资料)。②loadCaseSnapshot/hydrateRectificationCase/refreshRectificationCase把非 2xx 或网络错误带回调用方;校正面显示「这一问还没读到。」+「重新读取」,不再装成采集等待。③ 下一题是可渲染选择题时,persistApplied与persistCollectDenialTurn用composeCollectSpokenAssistantText把题干追加进正文并随流发出;卡片仍由 GET 挂上,attachQuestionsToTurns的stripQuestionSentences去重。 - 验证:
frontend/tests/account-dialog-inert-20260918.test.ts覆盖 GET warn、503 文案与按钮、ack+stem 流式及挂卡后题干只出现一次;hydrate 失败带failure.status。真机清单见docs/testing/account-dialog-inert-20260918.md。若真机仍复现,凭服务端 JSON warn 把本条转 confirmed 并补根因。 - 防复发:确定性回复不得只靠 GET 挂卡投递题干;快照失败不得静默。
- 关联记录:BUG-525(题干经 asked_turn_id 挂上)、BUG-930(回答钉顶:只见最后一轮是设计)、BUG-965
- 复发自:待确认
- 修复版本:
824ecff0
BUG-970 | 设置弹窗内容区留白过大、导航入口语义像页面跳转
- 状态:investigating
- 首次发现:2026-09-19
- 最近更新:2026-09-19
- 影响面:设置弹窗的桌面布局、个人资料头像区、星盘资料列表入口。
- 用户现象:设置弹窗左侧导航过窄,右侧内容贴边且留下较大空白;分区按钮尾部箭头暗示会进入下一页;个人资料头像锚点偏小;星盘资料的添加入口位于列表末尾,资料较多时不稳定。
- 触发条件:登录后打开设置弹窗,在桌面宽度切换四个设置分区,查看个人资料或星盘资料列表。
- 根因:旧布局使用 176px 导航、三列导航按钮(为同弹窗内切换保留无实际用途的箭头列)、内容区没有明确内边距,表单子内容上限为 440px;头像编辑仍以 48px 预览布局;添加入口与列表内容耦合在列表底部。
- 修复:导航调整为 200px 并增加 hairline 边界,移除误导性尾部箭头;内容区增加 32px 桌面内边距,表单阅读上限调整为 560px;个人资料头像预览调整为 56px;“添加其他人”移动到“其他人”分组标题操作区。保留四分区共享
.settings-modal和vh+@supports (height: 1dvh)回退契约。 - 验证:
./node_modules/.bin/tsc --noEmit安装依赖后退出码 0;npm run lint退出码 0(0 errors、120 warnings);设置相关定向测试 21/21 通过。广泛测试筛选命令为 3481 tests / 3402 pass / 79 fail,失败集中在重复迁移、Windows 路径、Docker/数据库和技能 symlink 等环境或基线问题,不能作为本轮 UI 回归归因。npm run build已完成编译和 TypeScript,但在收集/api/daily-starlanguage页面数据时因 Windows 禁止创建 Skill runtime symlink(EPERM)失败,因此尚未确认/Static 或首屏 gzip;登录态浏览器走查仍待受控环境。 - 防复发:设置导航继续使用 icon + label 的两列结构且不添加页面跳转箭头;右侧滚动容器负责内边距,表单 cap 只作用于其子内容;列表视图默认不渲染表单,添加入口固定在“其他人”标题操作区;
frontend/DESIGN.md与合同测试同步锁定这些关系。 - 相关记录:BUG-554、BUG-698
- 复发自:无
- 修复版本:待发布
BUG-971 | 首页侧栏注册反馈循环阻塞客户端导航
- 状态:investigating(代码与定向回归完成;部署和登录态真人闭环待核对)
- 首次发现:2026-09-19
- 最近更新:2026-09-19
- 影响面:首页侧栏「星盘 / 星历 / 我的报告」客户端导航与共享外壳更新。
- 用户现象:测试站刷新后仍点了不切页,Network 出现
/api/ephemeris;该请求来自 pointerenter/pointerdown 数据预取,不代表 Next 导航成功。 - 触发条件:登录态 Home hydrated 后注册侧栏操作;useSessionManagement 每次 render 产生新的 visibleSessions/操作函数,注册 effect 随之再次发布。
- 根因:SessionListContext 同时承载业务数据和 registration。Home 写 registration 又订阅同一 value,setRegistration 广播反向使 Home 重渲染,形成默认优先级更新循环,阻塞 Next Link 使用的 startTransition。旧共享外壳测试只有源码正则,不能执行 effect,未拦住运行时反馈。
- 修复:会话数据 context 保留稳定 registrar,registration 独立只读 context 仅供 AppShell 订阅;映射显式接收 registration,children 原样透传。保留注册 effect 完整依赖、最新回调及卸载注销,不冻结首次闭包、不改成整页刷新。
- 验证:真实 React 最小原版实验 122 render / 118 注册 / 2 maximum-depth 告警(保护阈值主动终止);稳定数组和回调的因果对照为 3/1/0。另一次 transition 实验直到主动停止反馈后目的地才 commit。正式新生命周期测试旧实现 3 fail / 1 pass、修复 4/4 pass;含普通/StrictMode 空闲收敛、transition 注销重入、最新业务数据与回调、401。实际 SidebarProvider/Inset 包裹,但不模拟浏览器点击/布局;完整主会话复验、基线失败对照与发布结果见进度记录。
- 防复发:Home 不订阅自己生产的 shell registration,外壳不重建 children;保留真实客户端 React 回归而非仅 SSR/源码正则。未稳定数组/函数的测试输入不得改成固定常量来掩盖回归。
- 相关记录:BUG-927(共享外壳)、BUG-965 / BUG-967(同名导航现象但不同跨部署机制);本轮不扩修 health 快照时序漏洞。
- 复发自:BUG-927 共享外壳订阅边界的新增运行时回归,非已证实的旧构建不匹配复发。
- 修复版本:本轮
fix(frontend): isolate sidebar registration to unblock navigation提交;发布状态见docs/tasks/PROGRESS-sidebar-navigation-loop-20260919.md。 - 验收缺口:Windows Skill runtime symlink EPERM 阻止完整基线构建;Static/gzip 与真实登录态浏览器检查不得标通过。见
BLOCKED.md和docs/testing/sidebar-navigation-loop-20260919.md。
BUG-972 | 上游私人案例与本机路径残留进入本仓跟踪资料
- 状态:blocked(清理与引用修复已实施;上游哈希快照、完整验收与交付仍待闭环)
- 首次发现 / 最近更新:2026-09-19。
- 现象与触发:历史镜像同步带入私人案例、会话转录和本机路径,散落于测试、研究台账及旧快照。
- 已确认根因:研究用输入被复用为测试 fixture;存量资料未受仓库级隐私守卫覆盖。主业务入口未直接引用某台账,不足以证明该台账没有间接读者。
- 修复进展:T2 改为明确虚构的共享常量;产品批准补充范围和具体删除目标后删除 3,494 文件,修复 8 份 oracle 派生资料及 manifest,索引 185 → 174,缺失路径为零,新增 9 项合同通过。额外历史文档已脱敏,保留 blocked/reference-only 限制;受哈希绑定的上游快照及校正历史包保持不变。
- 验证:实际基线
35ca7299c;全量 Python 在收集阶段因缺少 mcp / hypothesis 报错,不能据此声称用例全通过。定向验证、数量差异与未完成项见docs/tasks/PROGRESS-owner-case-purge-20260919.md。 - 防复发:清理必须连同真实依赖审计;不得只检索主入口就宣称模型上下文和运行时不受影响。日志与记录只留符号定位,不复写私人输入。
- 关联记录:BUG-295(live 咨询入口与哈希历史包边界)、BUG-973(同步与仓库隐私防线)。
- 修复版本:本轮本地工作树,尚未提交、推送或部署;不得标记 resolved。
BUG-973 | 同步入口缺少隐私路径模式,咨询隐私守卫不覆盖仓库
- 状态:investigating(路径拦截与仓库守卫已实现;最终独立复验和交付尚未完成)
- 首次发现 / 最近更新:2026-09-19。
- 现象与触发:修改 mirror 映射时,原同步策略仅保护业务和凭据路径,不阻止任务书列明的私人案例与本机扫描资料路径;咨询输出检查不会扫描跟踪文件。
- 根因:导入工具缺少不可由外部 policy 移除的隐私路径规则;现有禁止标记直接以字面量写在测试源文件,原样全仓扫描将自命中。
- 修复:新增独立
REQUIRED_PRIVACY_PATH_PATTERNS,在load_policy同时拒绝镜像源、目标与 semantic_merge 路径,根目录与嵌套路径、大小写一致受约束;不改原商业保护集合,避免现有 policy 全部加载失败。报错不回显命中内容。 - 验证:新增 42 条参数化用例通过(mirror 28 + semantic_merge 14);同步工具整体 70 passed / 2 failed,两条失败均为基线已有 Windows 符号链接权限缺口(WinError 1314)。仓库守卫含实际扫描共 57 项通过,补齐链接只读目标文本与本地规则 BOM 回归。第二轮验收发现
pl9_1993模式撞答案键守卫,已删除该重复模式;答案键禁止字面量测试保持不变并通过。快速门缺少 mcp,未声称通过。 - 防复发:保留源/目标、根/嵌套、大小写回归;仓库扫描复用既有标记并检查用户目录前缀,已接入 quick。仅精确许可经过核实的声明/合成样例及无关数值节点,48 项自测通过;历史校正包正常扫描,不豁免整目录。该固定规则集不是通用 PII 检测器。
- 关联记录:BUG-190 / BUG-191 / BUG-192(导入约束历史)、BUG-261 / BUG-278(导入与保护边界)、BUG-972(存量残留)。旧防线覆盖范围有限,未拦截本次路径资料。
- 修复版本:本轮首轮提交
fe9489e6;第二轮 P0 修复尚未推送或部署,待远端认证恢复后发布并由验收方复核。
BUG-974 | 跟踪的 .venv 符号链接让 staging 隐私扫描 READ_ERROR
- 状态:resolved
- 首次发现:2026-09-20
- 最近更新:2026-09-20
- 影响面:
tests/test_repo_privacy_markers.py、stagingbackend-quality-gatevalidate - 用户现象:门禁 run 2807 红。Python 快速门 858 通过、1 失败:
test_tracked_repository_has_no_privacy_markers报files=1 hits=1/READ_ERROR .venv。publish跳过。线上仍停在上次绿灯d6fc4fb8。 - 触发条件:staging 推送触发门禁;validate 先
rm -rf -- .venv再python3 -m venv --clear .venv,然后跑仓库跟踪文件隐私扫描。 - 根因:
9375012f(文档提交)把名为.venv的符号链接写入跟踪树。.gitignore写成.venv/,Git 只忽略同名目录、不忽略同名符号链接。CI 用真实虚拟环境目录盖掉该链接后,扫描器对跟踪路径.venv做lstat得到目录而非普通文件,记为READ_ERROR。不是又扫到姓名或本机路径。 - 修复:从跟踪树删除该链接;
.gitignore改为.venv(文件、目录、符号链接都忽略)。合同测试锁定:git ls-files不含.venv,git check-ignore --no-index .venv命中。 - 验证:
pytest tests/test_repo_privacy_markers.py62/62;前端头像合同见 BUG-975。门禁 2809 成功,staging/api/health=12fdaed9。 - 防复发:不得把本地运行时目录或指向它的符号链接加入跟踪树;忽略规则不要只写带尾斜杠的目录形式。
- 相关记录:BUG-972、BUG-973
- 复发自:无
- 修复版本:
8dc460da
BUG-975 | 头像说明文案已改,源码合同仍锁旧句,门禁卡在 npm test
- 状态:resolved
- 首次发现:2026-09-19
- 最近更新:2026-09-20
- 影响面:
frontend/tests/beam-avatar.test.ts、frontend/src/components/profile-panel.tsx - 用户现象:门禁 run 2804、2805、2806 的
npm test红:renders the persisted Beam avatar in the sidebar, account menu, and profile editor。run 2807 先在 Python 步失败,未跑到这一步。 - 触发条件:设置弹窗改了头像区说明后,任何会跑
npm test --prefix frontend的 staging 门禁。 - 根因:产品文案已是「Beam 形象会在刷新和换设备后保持一致」,合同仍断言「Beam 形象由随机种子生成」。
- 修复:合同改为锁定现行文案。不改产品文案。
- 验证:
frontend/tests/beam-avatar.test.ts定向;断言三栏见进度记录。 - 防复发:改设置弹窗可见文案时同步源码合同,不得只改组件。
- 相关记录:BUG-970
- 复发自:无
- 修复版本:
8dc460da
BUG-976 | 普通对话寒暄被当作咨询:全量排盘、长判词与扣点
- 状态:blocked(独立审查、Linux 全量 3566/3566、标准 DB 40/40 及构建/gzip 通过;staging 部署与真人实测待闭环)
- 首次发现 / 最近更新:2026-09-20。
- 影响面:普通
POST /api/consult三种模式、流式 UI、咨询账务与历史恢复。 - 现象与触发:纯打招呼也进入强制计算与报告形状,并扣一次咨询点数。
- 根因:入口没有非咨询轮,预留与完整咨询链覆盖全部普通消息;原取消函数只退款不持久化回复,没有免费完成语义。
- 修复:所选模型一次短结构化分类并写寒暄回复,3 秒 fail-open,无 Skill/工具/出生资料注入;只看本轮问题、称呼与最后完整可见问答。普通入口命中后先原子免费结算,再发 answer.delta + 无 receipt 的完成事件。新 service_role-only RPC 沿用 advisory lock,校验所属/状态/回复,记录真实 usage、按 credit_amount 退款,完成请求并释放 reservation。完成重试不退款,已收费咨询不能借此退款。UI 标记持久化,寒暄不造步骤/思考/技法。
- 验证:假模型、schema、超时、断连、流协议和 SSR 展示定向通过;真实 PostgreSQL 17 覆盖退款幂等、回复冲突、跨用户/会话、取消竞态、已收费拒退、真实成本、事务回滚和无点数来源。Linux tsc 0、lint 0 error/120 既有 warnings;标准 DB 基线 39/39、候选 40/40,完整构建两侧首页 Static,首屏 JS gzip 621,299 → 621,605 B(+0.0493%)。Windows 迁移镜像与 Skill alias EPERM 的历史不删除,Linux 完整全量及部署证据见进度。
- 防复发:保留分类失败回咨询、服务端专有授权、先持久化后完成、刷新标记、取消/完成共享锁回归;不得加入寒暄关键词/正则/字数分类。独立审查补获 SDK strict 校验日志可能携带原文、且非法输出丢 usage 两项,已局部静默 SDK logger 并保留生成结果后严格解析,真实 SDK 假模型回归断言隐私、fail-open 与成本保留;相关定向主会话复跑 59/59,真实库定向复跑 1/1。
- 关联记录:BUG-922 / BUG-923 / BUG-937 / BUG-938;这是非咨询轮的新边界,不是旧每轮排盘防线复发。every turn、prepareStep、contractReady 与两处 requireTool:true 未改。BUG-280/305 的空回答或失败不得结算语义保持;BUG-347 的真实咨询思考历史不删除。
- 边界:reserve 仍在分类前,零点无订阅仍被拦;超时未知 usage 不伪称零,晚 usage 只观测不追写完成账本。
- 修复版本:
539d4dae,已合入 staging 并部署(/api/health的deployment.gitCommit与apiGitCommit均为该 SHA,latestMigration=20260920010000_consultation_free_completion.sql,2026-09-20 由 Claude 独立核对);详见 PROGRESS-consult-smalltalk-fastpath-20260920。状态保持 blocked:真人三模式与余额走查未做。
BUG-977 | 寒暄提示词豁免只存在于本命 Agent
- 状态:blocked(本地合同通过;真实三模式模型验收待执行)
- 首次发现 / 最近更新:2026-09-20。
- 影响面:productConversationVoice、natalSpokenReportContract,窗口/无出生分钟的简短社交回复。
- 现象与触发:只打招呼也套开场形状;窗口与一般模式拿不到原本命 chit-chat 豁免句。
- 根因:豁免写在仅本命拼接的常量内,共享 voice 的四步形状没有例外。
- 修复:将豁免移到三模式共享 voice,明确只回一句、≤20 字、无句号、无星盘主张;本命领域问题仍需原 opener-plus-skeleton。主修仍是 BUG-976 分流,提示词不代替计费与计算边界。
- 验证:三种指令模板展开共享 voice 后均含豁免,原 OPENER SHAPE 合同保留;未用真实模型假装语义验收。
- 防复发:共享语气规则不能只放单一模式专属合同;混合咨询/解释/纠错/抱怨仍走完整路径。
- 关联记录:BUG-922 / BUG-923 的边界之外,不撤销每轮咨询排盘;与 BUG-976 配套。
- 修复版本:
539d4dae,已合入 staging 并部署,Skill 版本不变。状态保持 blocked:真实模型三模式语义验收未做。
BUG-978 | 封存契约当前打分身份与资料审计状态过期
- 状态:resolved
- 首次发现:2026-09-20
- 最近更新:2026-09-20
- 影响面:封存 holdout 契约、当前实现评测身份与离线成绩归属。
- 用户现象:历史冻结值、历史 sidecar 当前值、当前代码实测哈希互不相同;契约仍写旧资料审计状态与评测日期,当前成绩无法正确归属。
- 触发条件:打分文件发生变化而契约未刷新,再读取契约判断当前引擎验证状态。
- 根因:人工维护契约;既有测试仅与旧报告相互核对,没有断言真实当前树身份与数据集状态。
- 修复:本轮新增固定口径重跑与 freshness 回归;执行与证据见
docs/tasks/PROGRESS-rectification-validation-20260920.md。保持发布六键与确认门不变。 - 验证:freshness 与确认门相关定向通过;独立复算逐例结果及汇总与冻结重跑 JSON 一致,六个 runtime 键未变。新增测试通过既有快速门 glob 桥接实际收集。旧 v2 哈希测试与 quick 环境失败与基线相同,详见进度。独立盲测因案例曾曝光仍 blocked,固定实现重跑不恢复未见性;resolved 仅指元数据过期与可追溯缺口,partial 指新独立盲测未完成。
- 防复发:当前打分文件 SHA-256、源审计状态与契约必须一致;新鲜度检查须进入实际快速门收集范围,不能仅存在于测试目录。新盲测还必须有未曝光样本和独立标注审核。
- 相关记录:BUG-098、BUG-427、BUG-428。
- 复发自:BUG-098 的独立 holdout 边界与 BUG-427 的身份绑定缺口;前者要求未被落实为独立封存流程,后者没有当前树 freshness 检查,旧 sidecar 自洽仍可过测试。
- 修复版本:本轮执行分支,未交付。
BUG-979 | 真值中心候选窗掩盖申报偏差导致的真值出窗
- 状态:resolved
- 首次发现:2026-09-20
- 最近更新:2026-09-20
- 影响面:离线候选窗评测与生产申报中心搜索窗的可比性。
- 用户现象:离线评测真值永在候选窗中心,无法衡量真实申报偏差大于搜索半径时的失败模式。
- 触发条件:用户申报偏离独立记录且偏差超过候选窗半径。
- 根因:旧评测从真值构窗,缺少独立的申报偏移变量和真值在窗覆盖指标。
- 修复:并行新增偏移敏感性评测,不修改历史候选函数;排名后才揭示真值,用既有 opaque 哈希破同分。
- 验证:完整修正版 900 组合、45 格,排除 0;新增偏差与 freshness/桥接回归 28/28 通过(包含桥接重复收集),真实引擎跨日分组与逐候选重算相同。报告见
docs/research/reported_offset_2026_09_20.md,口径与哈希见同名 JSON。resolved 仅指离线缺少偏差维度的缺口,不代表产品分钟准确或生产跨日问题已修。 - 防复发:锁定零偏移、窗外、口径字段、非真值破同分与跨日行为;窗口覆盖与排序命中分开记录,不以敏感性代替真实用户准确率。
- 独立审查追加:首版偏差评测的矩阵为全窗共用一个日期,跨日时辅助 transition proximity 有偏差。已在离线适配层按候选日期分组计算并与逐候选真实引擎结果对照,不修改生产打分;原初扫统计作废。生产同类日期处理不在本单修复范围,记入 BLOCKED,不能声称生产端到端等价。
- 后续生产问题:候选级 Dasha 日期修复单独跟踪于 BUG-981;本记录 resolved 仍仅限离线评测缺口。
- 相关记录:BUG-098、BUG-978、BUG-981。
- 复发自:BUG-098;既有防复发聚焦候选公开门与同案稳定性,没有离线与生产窗心可比性的测试。
- 修复版本:本轮执行分支,未交付。
BUG-980 | 缺少事件丰富且从未曝光的独立封存集
- 状态:investigating
- 首次发现:2026-09-20
- 最近更新:2026-09-20
- 影响面:分钟级评测可推广性与独立发布证据。
- 用户现象:旧封存集事件少;事件丰富的开放集用于调参、成绩已见,不能替代新的独立验证。
- 触发条件:把开放评价集或真值方向最佳答案回放当成产品分钟级准确率。
- 根因:开放集建设之后未建立独立采集、未曝光、独立人审的封存孪生集。
- 修复:本轮仅制定 v5 采集协议与不重叠公开候选池,不采事件、不生成新成绩。
- 验证:协议含事件/领域目标、独立来源、人审封存、曝光日志、工作量和发布边界;备选池与旧集去重通过(仅候选资格筛查,原文准入仍需复核)。v5 未建成,保持 investigating。
- 防复发:采集、标注、评分权限分隔,调参前封存,首次揭晓前不得以事件内容调参;拒绝自动置真人审标志和重新封存已曝光案例。
- 相关记录:BUG-098、BUG-428、BUG-978、BUG-979。
- 复发自:BUG-098;原记录明确延期独立分区与校准,本轮仍无真正独立样本,不能将文档协议写成验证完成。
- 修复版本:协议执行分支;数据集未就绪,不标 resolved。
BUG-981 | 跨午夜候选共用窗口日期导致 Dasha 邻近度偏移
- 状态:investigating
- 首次发现:2026-09-20
- 最近更新:2026-09-20
- 影响面:生产事件贡献矩阵 transition proximity、跨午夜候选排序与实现身份追溯。
- 用户现象:跨午夜窗口的部分候选按窗口起始日计算 Dasha 边界,结果可能改变分数与排序;此前离线评测用日期分组适配绕开,生产问题仍在。
- 触发条件:候选实际日期与请求
birth_date不同,进入merge_transition_proximity()。 - 根因:helper 将请求日期用于所有候选的 Vimshottari / Narayana 起始计算及缓存键;候选的完整
candidate_at未用于此层。 - 修复:按产品已批准的任务书,先红测再改候选级日期与缓存键,不调权重;冻结重跑和 V9 版本标记同步实施。进度见
docs/tasks/PROGRESS-rectification-cross-midnight-20260920.md。 - 验证:原实现新增测试 4 failed / 4 passed,修复后 9 项与 quick 桥接重复执行共 18 passed;另 3 项既有版本/枚举回归通过。19 个公开 AA 同日窗口、2299 个候选分数与矩阵字节不变;实跨日窗口全候选等于逐候选日期正确独算。性能配对中位 -0.87%,满足不超 +20%;口径 v3、raman/mean、±60、步长 1,旧 12 文件身份
b15d9ea15227cd58、新扩展生产身份7fffd1db612af1f2。独立主会话日期/隐私补验 80 项通过。尚无部署证据,保持 investigating。 - 验收边界:已有时段缓存与聚合回执身份导致 T4 未完整通过,见 BUG-984;另发现 BUG-982/983,不把候选级日期一致夸大为所有跨午夜路径端到端正确。
- 独立 review:
aa46da10核心修复本身通过;新增回归的门禁级跨机浮点哈希问题单独立号 BUG-985。F3 已确认时段重算经过已修 helper,证据未变的历史跨午夜时段缓存却会绕过新算法,故 BUG-984 是本项端到端验收阻塞项,不以测试修复代替缓存或部署闭环。 - 防复发:新增生产打分层跨午夜回归并接入 quick 收集;候选枚举正确不再被视为下游日期正确的充分证据。保留 BUG-427/428 的实现身份与已曝光边界。
- 相关记录:BUG-098、BUG-427、BUG-428、BUG-621、BUG-978、BUG-979。
- 复发自:BUG-098 的跨午夜兼容只覆盖逐分钟枚举,未覆盖后加的 transition-proximity 层;BUG-979 已在离线适配发现,但其范围禁止改生产,因而没有消除此生产缺陷。
- 补充回归:最终三个校正 Python glob 共 300 项,296 passed / 4 failed,四项失败与基线逐条相同、新增失败 0。算法 -8 的版本断言及真实引擎 golden 两项身份已精确同步,数值与比较器不变;独立定向 25 项通过。900/20 正式重跑的真实源码身份、历史字节与报告汇总经独立审查通过,确认门与独立性边界不变。
- 修复版本:
aa46da10,经b27d4de9合入 staging,2026-09-20 deploy-staging run 2821(f09f3d80)部署,含于当前部署8a409434(2026-09-26 对账);欠受控真人验收,端到端另待 BUG-984 独立验收,状态保持 investigating。
BUG-982 | 跨午夜短簇按钟点排序被扩成全天跨度
- 状态:investigating
- 首次发现:2026-09-20
- 最近更新:2026-09-21
- 影响面:
candidate_contrast.py、decision_policy.py的簇范围与宽度。 - 用户现象:跨午夜连续三个分钟候选组成的短簇,被表示为从当天最早钟点到最晚钟点,宽度扩成全天。
- 触发条件:同一签名簇跨午夜,钟点字符串排序而没有候选日期或全窗序号。
- 根因:签名聚类及
_cluster_span()丢失日期,只用钟点决定首尾;这不是本轮 Dasha 日期修复新引入的行为。 - 修复进展:日期锚点任务已本地实现 ordinal 排序、真实 offset 计宽及声明 segment 内 envelope,Python 聚类/决策、TS credible union/plateau、transition/probe 与交付贯通;不连续段不补洞。算法身份更新为 scoring-9,历史不重标。三公开 AA 独立子进程同机 A/B 分数、矩阵、簇边界与确认门逐位相等。真实 native golden 经前端请求/推断/交付 SSR 验证;最终全量及部署待验,保持 investigating。
- 验证:纯虚构最小探针沿
build_candidate_decisions()实测一簇三个连续跨午夜成员被报为 1440 分钟。未涉及真实案例资料,未标 resolved。 - 统一验收进展(2026-09-21):final-1 隔离 Linux 快照前端3649/3649、真实DB56/56通过,诊断性三公开AA七项投影21/21零容差相等;Python广域338通过/11失败,其中4个既存失败、7个新增失败,quick未运行,整体验收未通过。新增项包括3个版本/回执契约失配及4个历史冻结身份拒绝;后者是更早BUG-981冻结记录不能代表当前实现时正确fail-closed,不是final-1停写后生产源码漂移。旧证据原样保留。Grok 接续:三合同测试独立复验34/34;新扩展身份补四模块后真实20/900重跑并派生 current contract;新最终快照 final-3 tsc/lint0、前端3649、DB56、quick通过、AA 21/21、Python广域352/4(0新增失败)。无部署/真人证据,保持 investigating。详见PROGRESS。
- 防复发:后续需完整日期/相对窗序号回归贯穿签名聚类、候选决策与交付范围,不能只测试辅助
_primary_cluster。 - 相关记录:BUG-623、BUG-624、BUG-639、BUG-981。
- 复发自:未发现同症状既有记录;BUG-624/639 的范围断言仍在但均是同日样本,跨午夜诊断测试覆盖的是另一条聚类路径,未覆盖本路径。
- 修复版本:实现
b85c4a68,2026-09-21 经 Claude 独立验收后快进合入staging;随1420471a部署(2026-09-24 核对:b85c4a68是已部署提交的祖先,其后 5 个提交is-docs-only-range.sh退出 0)。仅差真人验收,故状态保持investigating。 - 存量数据核对(2026-09-24):报告与普通对话不会对修复前的跨午夜存量范围做错误锚定。
read_report_candidate_range对无intervals的旧行只返回纯钟点{start_time, end_time},parseReportCandidateRange遇startTime > endTime即返回null,两条链路随即退回存储的单个代表分钟(报告标provisional、不带候选范围)。影响是这批用户的报告丢失范围展示,不是算错日期;实跑已确认。证据及待验项见docs/tasks/PROGRESS-rectification-midnight-date-anchor-20260920.md。
BUG-983 | 凌晨申报窗口的候选日期锚点可能偏到次日
- 状态:investigating
- 首次发现:2026-09-20
- 最近更新:2026-09-21
- 影响面:V9 搜索窗、
engineRequestBody()与_candidate_datetimes()的日期传递。 - 用户现象:申报在午夜后的范围向前跨日时,生产候选中心可能落到申报日期的次日。
- 触发条件:申报日内早凌晨分钟采用左右搜索半径,起始钟点位于前一天,而请求仍只传未调整的申报日期。
- 根因:前端只传钟点范围与原申报日期,生产枚举将起始钟点绑定该日、较小终止钟点推到次日;候选日期本身已错误,helper 按候选日期一致计算并不能修正上游锚点。
- 修复进展:按已批准 D1/D2/D3 传显式本地日期区间,凌晨申报锚在申报日、前半窗落前日;late_night 追问一次,未知/跳过保留同日两段。真实服务端 schema 回归发现新选项数字钟点被既有文案过滤拒绝,改中文钟点,未放宽过滤。D4 明确授权后新增兼容迁移,保存独立 active date/来源/实际候选 offset,原 birth_date 保留;report sampler 与 VedAstro 使用实际候选日期。SQL 采用权限合成测试不代表真实候选通过确认门;未修 IANA/DST。最终全量与部署待验。
- 验证:纯虚构窗口探针证明申报中心候选与申报日期的日期差为 +1 日;不含真实用户资料,未标 resolved。
- 补充回归:预冻结审计发现新widen wrapper仍会按baseline重猜日期,丢持久区间与D1侧别;真实DB红测复现后,限本单30000迁移修为普通分钟按持久绝对日期扩窗、D1允许集合服务端保存、C/skip缩到单段仍保留原允许两段。侧别冲突等拒绝必须全事务无写入;真实DB dossier/compute→评分service→score请求捕获验证日期保持,网络截停不冒充引擎评分完成。最终标准DB与统一门禁见PROGRESS,仍未部署。
- 统一验收进展(2026-09-21 Grok 接续):隔离 Linux final-3 全门 tsc/lint0、前端3649、DB56、quick通过、AA 21/21、Python 广域 0 新增失败。无部署/受控真人证据,保持 investigating。
- 防复发:必须从申报日/分钟经实际前端请求构建到后端枚举端到端校验日期,而非只验证钟点范围与“支持跨午夜”;扩窗不得重猜日期或将未知缩窄结果伪作用户选侧。
- 相关记录:BUG-098、BUG-198、BUG-979、BUG-981。
- 复发自:未发现同症状既有记录;BUG-198 时段开窗与 BUG-098 枚举回归仍在但不校验申报日期中心。离线 holdout 已有前日中心保护,未覆盖生产请求链。
- 修复版本:实现
b85c4a68,2026-09-21 经 Claude 独立验收后快进合入staging;随1420471a部署(2026-09-24 核对:b85c4a68是已部署提交的祖先,其后 5 个提交is-docs-only-range.sh退出 0)。仅差真人验收,故状态保持investigating。 - 存量数据核对(2026-09-24):报告与普通对话不会对修复前的跨午夜存量范围做错误锚定。
read_report_candidate_range对无intervals的旧行只返回纯钟点{start_time, end_time},parseReportCandidateRange遇startTime > endTime即返回null,两条链路随即退回存储的单个代表分钟(报告标provisional、不带候选范围)。影响是这批用户的报告丢失范围展示,不是算错日期;实跑已确认。证据及待验项见docs/tasks/PROGRESS-rectification-midnight-date-anchor-20260920.md。
BUG-984 | 时段旧分数缓存不校验算法身份且聚合回执可能显示新版本
- 状态:investigating
- 首次发现:2026-09-20
- 最近更新:2026-09-20
- 影响面:V9
block_scan缓存、工具活动回执与 turn 级聚合。 - 用户现象:升级算法后,同证据的时段模式仍可能直接返回旧分数;开始活动显示新版、成功结果实际属于旧版,聚合字段又可能展示新版。
- 触发条件:历史时段缓存证据指纹未变;本次运行默认版本已升,而缓存算法身份仍旧。
- 根因:
scoreAndPersistCurrentEvidence()的时段分支在版本探测之前按证据指纹提前返回;成功回执使用旧结果身份,但 SQL 将不同阶段的engine_version取字符串最大值,而不是成功结果来源。分钟模式另有环境覆盖/探测失败回退边界,不能宣称身份无条件一致。 - F3 调用链查证(2026-09-20):前端时段分支 →
runV9BlockScan()→ POST/api/rectification/v5/block_scan→_compute_rectification_v5_block_scan()→api_service.block_scan()→score_candidates()→build_event_contribution_matrix()→merge_transition_proximity();时段支持率确实消费该候选分数。故本项升级为 BUG-981 端到端验收阻塞项。受影响的是证据未变、未触发三轮转分钟的历史跨午夜时段缓存命中;新建/无缓存/证据变化仍走已修路径。late_night299 分钟跨日;unknown1439 分钟虽同样走时段扫描,其首轮00:00–23:59本身同日,不能据此声称有跨日偏移,选中跨午夜时段后才触发。逐层行号见docs/tasks/PROGRESS-rectification-cross-midnight-gate-fix-20260920.md。 - 修复:补单策略 b 已批准,前轮认证 blocker 已由主会话解除(前置代码
3f39bafc已合 staging,部署另验)。本轮仅完成 F2 A:compare/diagnostics 的 started/failed 不再写版本,completed 不回退前端常量。真实工具同 turn 混版本 completed 已实测成立,触发 B,但 SQL 未授权,停止进一步实施;F1/F3/F4 pending,BUG 保持未解决。进度见docs/tasks/PROGRESS-rectification-cross-midnight-fix-20260920.md。 - 验证:独立纯虚构 TS 探针使用替身 RPC/fetch,旧时段缓存返回
cached:true、算法尾号 -7、fetch 次数 0;真实工具调用路径记录 started 尾号 -8 / completed 尾号 -7。聚合 SQL 的max(engine_version)已核源码,未真跑 DB,因此不伪称数据库端到端通过。 - 防复发:后续必须分别覆盖分钟/时段缓存、实际版本接口、部分环境覆盖与接口失败,成功回执必须绑定实际结果来源;历史打开与 Skill 绑定不变,不得重标或删除旧结果来掩盖问题。
- 相关记录:BUG-427、BUG-621、BUG-981。
- 复发自:未发现同症状既有记录;既有分钟算法身份缓存门不覆盖提前返回的时段分支,回执测试未组合新版 started 与旧缓存 completed。
- F2 补充实测:原生虚构输入 golden 经真实 compare/diagnostics 工具顺序执行,仅替换算法标签模拟滚动版本;同一 turn 可写 completed scoring-9 与 scoring-10,而字符串 max 为 scoring-9。既有聚合 RPC 不暴露各成功行身份,历史 fingerprint 无法还原,应用层 A 不足以闭环。定向身份 9/9(含缺陷诊断)、tsc 0;五文件回归 61 项中 57 pass / 4 Windows symlink EPERM,未冒称 DB 聚合已验。
- 授权恢复与修复:产品明确授权 B 后,新增兼容函数体迁移,仅将身份聚合改为选中成功/最新 attempt 的 completed 非空来源、按时间/id稳定排序;表/已应用迁移/历史行与权限不变。分钟/时段统一完整算法+策略pair与输入指纹;未知身份保留旧值只读且不强制重算,服务端采用/确认/候选选择独立重查。历史GET/刷新和工具均标注;新算响应与当前pair不一致也只读。可信当前版本已知的stale历史保留显式「重新比较」,未知身份没有入口,避免只读锁死升级。以上替代授权前局部状态,前文保留事故与停点历史。
- 恢复验证:真实native虚构golden的minute/block旧缓存重算及当前缓存复用、部分env冲突/接口超时与故障、缺source/非最新result、历史read→compare路径均回归;F1先红4项后绿。B真实PostgreSQL先红8≠7后绿,标准DB40/40(无skip);Python日期/bridge/memoization32/32。Linux完整基线3575/3575→最终diagnostics补丁快照3588/3588(0fail/skip),首页Static、完整28资源gzip+0.040085%,tsc/lint0error;主会话独立DB40/40、最终定向40/40+tsc通过。不提前算部署通过。
- diagnostics追加复核:旧failure回归未调用诊断工具,新增调用先得16绿/6红,确认未知身份仍会重计算;现未知身份+旧结果仅返回只读来源/notice,不执行诊断或方法,不允许分钟确认。可信身份下诊断新响应pair不一致也只读、确认false,成功来源仍是真实9/10;修后身份22/22、身份+spoken37/37;改后全量3588/3588与build已新快照复跑通过,旧delivery证据另外保留,主会话也已独立40/40及tsc0。
- 修复版本:
d575e89a(F2 A)+8d0359fc(B:迁移20260920010000_rectification_receipt_result_identity.sql、身份统一与 diagnostics 追加)已在 staging;2026-09-20 deploy-staging run 2824(8d0359fc)部署,此后 migrate-staging-database 各 run 均成功(最近 2929),含于当前部署8a409434(2026-09-26 对账)。未见独立验收记录,欠受控真人走查,状态保持 investigating。
BUG-985 | 同日不变性回归写死浮点分数哈希导致跨机门禁失败
- 状态:resolved
- 首次发现:2026-09-20
- 最近更新:2026-09-20
- 影响面:
tests/test_dasha_transition_proximity_cross_midnight.py及快速门 bridge;阻塞aa46da10的合入验收,不是打分修复本身的回归。 - 用户现象:执行机器测试绿,换到不同浮点环境后 ordinal 2/3 红;选择源文件与 bridge 时重复为四项失败,门禁无法通过。
- 触发条件:同日 121 个浮点分数的 canonical JSON SHA-256 与某台机器写死的历史字面量比较。
- 根因:把“同环境新旧实现相同”的相对不变量写成跨环境绝对不变量。四位分数本身就可能不同,不是序列化尾噪。BUG-733 的既有防复发与同机差分测试仍在,但没有阻止新文件重引绝对浮点哈希,属同形复发。
- 修复:保留三个 ordinal 和 121 个候选;A 使用真实 contexts,B 仅在 transition helper 边界去掉
candidate_at进入既有 legacy 回退,主事件引擎继续使用完整 contexts;同机分数与矩阵 canonical 字节均严格相等,不加容差、不删测试、不 skip/xfail。bridge 顶部记录重复收集原因与独立用例数。 - 验证:Windows 原测试 18 通过;隔离 Linux 原测试 14 通过/4 失败,复现跨环境差异。修改后两环境定向(含 memoization)均 32 通过、校正 glob 均 216 通过;内存回退候选日期后两环境均同日三项绿、真实跨午夜一项预期红。生产红线文件与历史 golden 本轮不改。
- 独立机器验收闭环:2026-09-20 产品提供并批准第三套独立 Linux/Python 3.13 环境 review(记录于
0ca3871d的任务索引):定向 18 项全绿;日期回退同日 3 绿、跨午夜相关 4 红;校正 glob 从 staging 200 通过到修复版 216 通过,零新增失败。两台机器要求已满足,本项 resolved 仅指测试缺陷,不代表 BUG-981/984 部署与缓存闭环。完整 quick 的既有环境失败仍见进度记录。 - 防复发:同日/不变性类回归一律用同机相对比对,不得写死跨机浮点字面量;参照
tests/test_rectification_engine_memoization.pydocstring 的跨机舍入教训。同日不变性与跨午夜正确性必须分开,以日期回退探针分别验证绿/红;桥接覆盖和重复计数必须明示。 - 相关记录:BUG-981、BUG-733、BUG-712;F3 同步确认 BUG-984 为 BUG-981 端到端阻塞项。
- 复发自:BUG-733(同进程可证的等价关系被跨机 golden 代替)。
- 修复版本:
25232ce4,第三环境 review 通过;本次按产品授权合入 staging,详见docs/tasks/PROGRESS-rectification-cross-midnight-gate-fix-20260920.md。
BUG-986 | staging publish 冷构建在 22 kB/s 镜像源下撞 60 分钟超时
- 状态:investigating
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:Gitea
backend-quality-gatepublish job、deploy/railway-api.Dockerfile、deploy/reclaim-runner-disk.sh - 用户现象:run 2828 的 validate 通过后,
Build and publish exact-SHA ACR images在 API 镜像pip install pandas期间被取消;结论failure,context deadline exceeded。 - 触发条件:向 staging 推送代码后 publish;runner 空闲 677 GiB,但距上次命中的 API 依赖层已超过 72 小时。
- 根因:
reclaim-runner-disk.sh在磁盘充足时仍执行docker builder prune --filter until=72h,丢掉 BuildKit 层缓存。随后 Aliyun debian/pypi 约 22 kB/s:apt-get update9.4 MB 用 7 分、102 MB 软件包约 40 分,pyswisseph 6.9 MB 又 5 分,pandas 12.4 MB 下载未完成即达 publish 的 60 分钟上限。业务测试未失败。 - 修复:磁盘不低于
MINIMUM_FREE_GIB时保留 BuildKit 缓存;API 镜像 apt 与 pip 分两层,apt/pypi 改走华为云镜像(与 SWR 基础镜像同路径)。不改 workflow 超时、评分或确认门。 - 验证:定向更新
staging-backend-workflows.test.ts与test_railway_deployment.py;远端以新 staging 门禁为准,本记录不提前标 resolved。 - 防复发:磁盘充足时不得 prune BuildKit;API 依赖安装不得把 apt 与 pip 绑在同一层以致超时后整层作废。
- 相关记录:BUG-142
- 复发自:BUG-142(publish 超时预算;本次是缓存被过早丢掉后的冷构建)
- 修复版本:待本修复合入 staging 的门禁 run
BUG-987 | 列表用 messages=[] 过滤,生时校正会话全部从侧栏消失
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:
GET /api/sessions、excludeEmptyConsultations、侧栏历史 - 用户现象:staging 侧栏一条生时校正都没有;深链
?c=仍能打开同一会话。 - 触发条件:打开
/或次级页拉会话列表。校正会话的chat_sessions.messages永远是[]。 - 根因:
e4e73f56用全表messages <> '[]'当「有没有内容」。该判据只对咨询成立。客户端isListedSidebarSession放行校正,服务端先把行拿走。源码正则not("messages", "eq", [])从未拿真实行验证。 - 修复:过滤收窄为
session_type.neq.consultation,messages.neq.[]。归档视图同一条规则。校正内容在 case 表,不在messages列。 - 验证:
database-session-list-visibility.test.ts(真实 Postgres,无 Docker 时 skip)、session-list-filter.test.ts跨层对断、chat-session-authority.test.ts。 - 防复发:不得用「某列为空」推断会话有没有内容。列表过滤必须带
session_type限定。不得用源码正则代替真实行测试。 - 相关记录:BUG-928、BUG-704
- 复发自:BUG-928(空咨询过滤写成全表
messages <> '[]') - 修复版本:
df329fa9(staging 已部署,门禁 run 2833 全绿)
BUG-988 | 侧栏副标题用创建时间,排序用最后活动时间,列表读起来是乱的
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:
sessionSidebarSubtitle、SESSION_LIST_COLUMNS - 用户现象:侧栏时间不单调(9/18 → 9/17 → 9/7 → 9/16),9/7 创建、9/16 仍在用的会话落在「最近 7 天」却显示 9/7。
- 触发条件:会话创建后隔天继续使用。
- 根因:
e4e73f56把created_at加进列表列,副标题读创建时间;排序、分组、游标仍用updated_at。产品 2026-09-21 推翻 BUG-929 的「副标题为创建时间」。 - 修复:副标题改
updatedAt。列表列去掉created_at。详情接口仍返回created_at。 - 验证:
session-groups.test.ts(早创建晚活动:位置按活动时间,副标题显示活动时间)、chat-session-authority.test.ts列合同。 - 防复发:显示、排序、分组、游标必须用同一个时间字段(
updated_at)。SESSION_LIST_COLUMNS不得再含created_at。 - 相关记录:BUG-929、BUG-553
- 复发自:BUG-929(副标题改创建时间后与排序键分叉)
- 修复版本:
df329fa9(staging 已部署,门禁 run 2833 全绿)
BUG-989 | 点「新建对话」复用旧空会话,第一问之前就落库
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:
startNewChat、send()、GET /api/sessions的draft、首页启动 - 用户现象:点新建落到 9/17 的旧「新对话」;连点不会开新的。空咨询在开口前就已经在库里。
- 触发条件:点「新建对话」,或启动时并入
draft。 - 根因:BUG-928 让步成「复用空咨询 + 服务端 draft」。复用既不 bump 时间也不改创建时间。产品 2026-09-21 收掉该让步。
- 修复:
startNewChat只在本地创建,不POST、不写?c=。第一问send()先POST /api/sessions再走既有 append。落库失败撤销本地会话并保留输入框草稿。删除draft字段、draftRow、findReusableEmptyConsultation。 - 验证:
session-list-filter.test.ts源码合同(新建无 POST / 无 URL,send 时 create,失败不setDraft)、chat-session-url.test.ts、session-list-lifecycle.test.tsx仍覆盖无 draft 的列表响应。 - 防复发:第一问之前不得
POST /api/sessions。未落库会话不得写入?c=。列表响应不得再带draft。 - 相关记录:BUG-928、BUG-987
- 复发自:BUG-928(延迟落库让步未收口)
- 修复版本:
df329fa9(staging 已部署,门禁 run 2833 全绿)
BUG-990 | GET /api/sessions?archived=1 在自托管 Postgres 上 500
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:
LocalPostgresQueryBuilder.not()、GET /api/sessions?archived=1、归档视图 - 用户现象:当前无用户可见现象——归档视图自
92558ee6(2026-08-22)起没有 UI 入口(见 BUG-991),前端不会发出archived=1。发现路径是 BUG-987 新加的真实 Postgres 测试第一次跑到归档分支。接口层面确为 500「聊天记录暂时无法读取」。 - 触发条件:自托管本地 Postgres 上请求
?archived=1。托管 Supabase 不受影响。 - 根因:兼容层
not()只实现 PostgREST 算子子集eq/cs。applyArchiveFilter(..., true)调用not("archived_at", "is", null),构造期不报错,真跑 SQL 时 throwunsupported not filter,被列表路由外层 catch 成 500。该调用自a1956deb(BUG-553)就在,此前没有真实数据库测试走到归档分支。 - 修复:
not()增加is分支,新 filter kindisNot,编译为is not null/is not true/is not false。不得写成<> null(恒为 NULL,会静默 0 行)。 - 验证:
local-postgres-not.test.ts(本机绿);database-session-list-visibility.test.ts归档分支在 Gitea 门禁 run 2833 转绿(ok 1238,3653 条全过、0 红 0 skip),deploy-stagingrun 2835 成功,/api/health的deployment.gitCommit=df329fa9。执行方本机 Docker 网段耗尽未跑test:db,见BLOCKED.md。 - 防复发:兼容层新增算子必须有真实 Postgres 覆盖,不得只加源码正则。
- 相关记录:BUG-553、BUG-926、BUG-987
- 复发自:BUG-926(同一兼容层的不同缺口:当时是
order()只留最后一键) - 修复版本:
df329fa9(staging 已部署)
BUG-991 | 会话归档后没有任何入口能看回来或恢复
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:会话行菜单「归档 / 恢复」、
GET /api/sessions?archived=1、archived_at存量 - 用户现象:会话行菜单里可以「归档」,归档后它从侧栏消失;此后没有任何界面能列出归档记录,行菜单里的「恢复」也跟着不可达。除非记得
?c=<uuid>深链,归档等于单向丢失。 - 触发条件:在会话行菜单点「归档」。
- 根因:
92558ee6(2026-08-22)删掉了分组页眉上的归档切换按钮,但保留了整条数据链路。产品 2026-09-21 拍板整体下线,不装回入口。 - 修复:删行菜单归档/恢复、删
archived=1查询与 PATCH 写archived_at(旧 bundle 仍发送则 2xx 忽略)、迁移把存量archived_at清空且不 bumpupdated_at。删除会话能力不动。archived_at列本轮不删。 - 验证:Gitea 门禁 run 2837 全绿(3656 条 / 3656 过 / 0 红 / 0 skip)。真实 Postgres 测试
ok 1240 - retire archive migration clears archived_at without bumping updated_at:执行从迁移文件切出的update后archived_at归零,同一行的pinned/messages/updated_at与执行前逐字节相同,重跑仍 0 行;ok 1239断言archived_at非空的行仍不入列;ok 3444锁定行菜单只剩重命名/收藏/转发/删除;ok 788断言 PATCH 收到archived_at回 2xx 不落库。local-postgres-not.test.ts原样保留。migrate-staging-databaserun 1435 与deploy-stagingrun 1436 成功,/api/health的deployment.gitCommit=d2cec179、database.latestMigration=20260921010000_retire_chat_session_archive.sql、八项 check 全 ok。验收方独立变异测试:把「归档」菜单项注回sidebar-session-row.tsx后,Python 合同与sidebar-contract.test.ts同时转红。真机走查(归档项消失、旧归档会话回列表且时间未变、删除仍可用)见docs/testing/,仍欠。 - 防复发:删除某个视图的唯一入口时必须同轮下线其数据链路,或留一条断言入口存在的测试。
- 相关记录:BUG-990、BUG-992、BUG-553
- 复发自:无
- 修复版本:
5a4b8738+d2cec179(staging 已部署)
BUG-992 | 归档下线后门禁 Python 合同仍要求归档入口存在
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:
tests/test_session_management_entrypoints.py - 用户现象:无直接用户现象。Gitea 门禁 run 2836 红,
5a4b8738未部署。 - 触发条件:向 staging 推送删掉归档入口的前端改动。
- 根因:Python 合同对前端源码做字符串断言,要求
toggleArchivedSession等必须出现。前序单 grep 只扫frontend/,扫不到tests/。同类第四次。 - 修复:那五个 token 从「必须出现」翻成「必须不出现」,且只对
sidebar-session-row.tsx与use-session-management.ts做反向断言。归档测试重写,保住writeChatSession的 POST/PATCH 合同,并把「归档不走 DELETE」改成「删除仍走 DELETE」。frontend/AGENTS.md要求删符号前git grep -- tests/ frontend/。 - 验证:
python -m pytest tests/test_session_management_entrypoints.py3 条全绿(验收方独立复跑);门禁 run 2837 全绿、publish不再跳过。变异测试:把「归档」注回行菜单后该合同转红。 - 防复发:删除或重命名任何前端符号、类名、可见文案之前,必须
git grep -n "<符号>" -- tests/ frontend/,不得只 grepfrontend/。 - 相关记录:BUG-933、BUG-934、BUG-939、BUG-991
- 复发自:BUG-939(Python 合同盯着已搬走的前端源码)
- 修复版本:
d2cec179(staging 已部署)
BUG-993 | 侧栏当前会话的标题和三点菜单又拆成两块高亮
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:侧栏会话行选中态、
.session-menu-trigger - 用户现象:选中一条会话时,标题一块底、右边「⋯」另一块底,看起来像两个控件。
- 触发条件:侧栏展开并选中任意带副标题的会话。
- 根因:复发自 BUG-024。整行选中底已经在
.session-row上,但.session-main仍有独立圆角,.session-menu-trigger固定 44px 高并在悬停/展开时刷--color-canvas。旧契约只锁了「不是 50% 圆」,锁不住这块独立画布。 - 修复:选中/悬停只画在
.session-row;标题和「⋯」背景透明、无独立圆角;「⋯」拉高到整行。悬停不再铺 canvas。 - 验证:
sidebar-contract.test.ts「one unified row surface」补 overflow、标题圆角 0、trigger stretch、hover 不含--color-canvas。 - 防复发:会话标题和行内操作必须共享同一个行级状态面。不得给
.session-menu-trigger单独的 canvas/选中底。 - 相关记录:BUG-024
- 复发自:BUG-024(旧测试没锁住 trigger 的 canvas 悬停底)
- 修复版本:
f9685f28(已进 staging;部署被 BUG-994 / migrate run 2841 磁盘写满挡住)
BUG-994 | staging 迁移拉 digest 镜像时 containerd 磁盘写满
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:
deploy/run-staging-migration.sh、deploy/run-staging-deploy.sh、staging 主机 Docker 镜像层、migrate-staging-databaserun 2841 - 用户现象:无终端用户可见功能回归。
f9685f28门禁 validate 全绿、镜像已发布,但测试站没有切到该 SHA:迁移在主机docker pull新 web digest 时失败,后续deploy-staging未执行。当时线上仍是上一版d2cec179。 - 触发条件:向 staging 推送会触发门禁的改动后,publish 派发
migrate-staging-database,主机再拉一枚新的 exact-SHA web 镜像。 - 根因:每次部署留下不可变 digest 镜像,脚本在 pull 前不回收未使用镜像。run 2841 在
Apply digest-pinned migration under host lock拉copse/jyotisha@sha256:a00887de…时,containerd ingest 报write /var/lib/containerd/io.containerd.content.v1.content/ingest/631c40bfaf7318a3ce6ca6ead3b0f8ce0eb25e2a2e93851c76bfc9eb5060c25d/data: no space left on device。门禁 runner 的reclaim-runner-disk.sh只管构建机,不管 staging 主机。 - 修复:迁移与部署脚本在 pull 前打印
docker system df,执行image prune --force再image prune --all --force。正在跑的jyotisha-staging容器会保住当前层。禁止volume prune,避免误删 Postgres 数据卷。 - 验证:源码合同
staging-backend-workflows.test.ts锁定 prune 出现在 pull 之前且不得volume prune --。门禁 run 2842 全绿。migrate run 2843 在 pull 前Images 166 / 77.64GB / reclaimable 70GB,image prune --all回收 75.38GB 后剩 4 张在用镜像,随后 digest pull 成功、无待应用迁移。deploy run 2844verified_sha=a06829b3。GET /api/health的deployment.gitCommit=a06829b3,八项 check 全 ok。staging 其后仅文档前进到26c1fbc6(BUG-995 任务书)。 - 防复发:staging 主机拉 digest 前必须先回收未使用镜像。不得用
volume prune换磁盘。 - 相关记录:BUG-150、BUG-993
- 复发自:无
- 修复版本:
a06829b3(staging 已部署;migrate 2843 / deploy 2844)
BUG-995 | 寒暄隐私测试接管 stdout,吞掉 node:test 自己的 TAP 报告
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-22
- 影响面:
frontend/tests/consultation-smalltalk.test.ts三条default Mastra adapter protects privacy and usage on *用例的报告通道 - 用户现象:无用户可见现象,纯测试基础设施。 这三条失败时,报告可能只剩
not ok 1 - <文件绝对路径>,没有用例名和断言消息。退出码仍是 1,门禁不会漏掉失败。 - 触发条件:
t.mock.method(process.stdout/stderr, "write", …)全局接管标准输出,且不把内容交回原始write。 - 根因:要验证的「私密文本不得出现在进程输出」与
node:test的 TAP 报告共用process.stdout。mock 只logs.push并硬编码返回true。t.mock.restoreAll()在finally里,但报告行在测试回调返回之后异步写出,下一条 scenario 已经重新装上 mock,于是上一条 TAP 落进logs。 - 修复:装 mock 前
bind保存原始write;mock 先把 chunk 记入logs,再把chunk/encoding/callback交给原始write并返回其返回值。console.*五个 mock 维持原样。隐私断言logs.join(" ").includes(sentinel)不改宽。 - 验证:单文件
tsx --test tests/consultation-smalltalk.test.ts连续三次均为# tests 33/# fail 0/default Mastra adapter的ok行每次 4 条。变异:invalid_schema注入PROBE后出现not ok 21 - default Mastra adapter protects privacy and usage on invalid_schema且PROBE可见;bad_json为not ok 22同样可见。隐私探针:mock 装上后process.stdout.write(sentinel),三条均not ok且断言文案SDK must not log private text可见;探针已撤回未提交。本机全量npm test因 Windows Docker/symlink 既有缺口# fail 89,但default Mastra adapter仍报 4 条;Linux 门禁以推送后 run 为准。 - 防复发:测试不得全局接管
process.stdout/process.stderr而不透传。需要断言进程输出时,mock 必须把内容原样交回原始write。比较测试规模时比用例名列表 diff,不比# tests汇总数。 - 相关记录:BUG-976、BUG-977
- 复发自:无
- 修复版本:
2503c019
BUG-996 | 更新出生资料后星盘仍可能显示旧盘或无法区分失败原因
- 状态:resolved
- 首次发现:2026-09-22
- 最近更新:2026-09-22
- 影响面:
PATCH /api/account、GET /api/account、GET /api/chart-view、frontend/src/lib/secondary-page-data.ts的星盘快照缓存 - 用户现象:已经更新出生时间,打开星盘仍像没有可显示内容,或点开后看不到刚保存的日期和时间。现场这一户的数据库行没有取到,不能把某一种残缺资料写成已经证实的唯一原因。
- 触发条件:账户资料 PATCH 成功后星盘模块缓存仍留着上一张快照;或
profiles读取失败、已采用日期缺 offset、时区解析失败、引擎 429 / 超时 / 坏结果被收成同一种「资料不完整」或同一句失败。 - 根因:三处代码断点同时存在,不是一条已经证实的现场数据。第一,chart-view 忽略 profile 查询错误,空行会落到
birth_profile_incomplete;已采用日期缺 offset 或时区解析抛错时没有稳定分类。第二,chartCache不按账户和资料指纹换键,PATCH 成功后也不丢弃旧快照。第三,账户响应和星盘排盘各自读日期、时间、offset,确认后的普通编辑虽不覆盖 active time,但调用方看不到同一份服务端真值。guard_adopted_birth_date会把 adopted date、offset、provenance 一起清空,不会留下「有日期、没 offset」的半截元组,因此本轮不加迁移。 - 修复:账户 PATCH / GET 与 chart-view 共用
resolveServerOwnedChartBirth。chart-view 把查询失败、资料不完整、adopted 计算不完整、时区失败、引擎忙、超时、坏结果和响应格式失败分开,查询失败不再写成资料不完整。PATCH 成功后按账户和资料指纹钉住星盘缓存,进行中的旧请求不能把旧盘写回去。确认状态的普通编辑仍不覆盖 active time。不把chart_profiles当作/chart真值,不放宽 accepted / confirmed。 - 验证:
frontend/tests/chart-profile-update-consistency.test.ts覆盖 reported、accepted、confirmed、跨午夜、缺 active offset、查询失败、时区失败、引擎分档、schema 失败、旧快照失效和 D1。旧行为下查询错误没有独立 reason、缓存也没有账户指纹键,这些断言不会过。现场单户数据库状态仍未取证。 - 防复发:数据库查询错误不得映射成
birth_profile_incomplete。已采用日期存在而 offset 缺失时不得回退到声明 offset 或清空active_birth_time。星盘快照键必须包含账户和资料指纹;引擎缓存键仍包含日期、时间、offset、ayanamsa、node mode。失败文案不得写「过一会儿再打开」或没有上下文的「没有可显示内容」。 - 相关记录:BUG-073、BUG-076、BUG-715、BUG-716、BUG-717、TASK-chart-profile-update-consistency-20260922
- 复发自:无。BUG-073 仍要求服务端不信任客户端咨询模式;BUG-076 仍要求
profiles而不是星盘库 JSON 为真值;BUG-715 至 717 的星盘打开、分档日志和开页只打/api/chart都还在,本条补的是资料真值和快照失效。 - 修复版本:本分支,尚未推送、尚未部署
BUG-997 | 切换人物后普通聊天仍用登录用户本人的出生资料
- 状态:resolved
- 首次发现:2026-09-22
- 最近更新:2026-09-22
- 影响面:普通聊天
/api/consult的排盘对象,以及POST/PATCH /api/sessions的人物绑定 - 用户现象:会话已经记下另一位人物,继续提问时计算和咨询上下文仍使用当前登录用户的
profiles。对方资料被删除后,发送还会退回本人。 - 触发条件:会话绑定
chart_profile_role=other与某个chart_profile_id后调用普通咨询;或客户端同时提交另一套出生字段。 - 根因:
chart_profile_id/name/role只是客户端可写的展示快照。咨询读取会话时不取chart_profile_id,准备阶段无条件loadProfile(userId)读取登录用户的profiles。客户端出生字段因此可以和实际排盘对象不一致,缺失或越权的 other 资料也会落回本人。 - 修复:新增服务端 subject resolver。
self与未绑定的旧会话只读当前用户权威profiles;other只读当前用户拥有的chart_profiles行,并在服务端重新派生姓名与角色。会话展示名、客户端name/year/month/day/hour/minute/city/lat/lon/tz都不能覆盖该结果。另一用户的 id、随机 id、角色不一致、删除、缺失或不完整资料一律失败,不退回本人,也不扣点、不调用模型。空会话可以由服务端按 id 填上角色和姓名;已有消息的会话拒绝改绑。刷新与深链只使用库里的绑定,不读取全局activeChartId。未改表结构、计费语义或模型选择,也未接到报告、每日星语或生时校正。 - 验证:
frontend/tests/consultation-subject-binding.test.ts10 pass / 0 fail,覆盖 self/other、越权与随机 id、角色与伪造姓名、删除/缺失/不完整、客户端出生字段冲突、咨询准备拿到 other 资料、失败不扣点且不进入准备、有消息后改绑拒绝、刷新保留绑定、并发删除后发送失败。与consultation-route-service、application-billing-contract、chat-session-write、chat-session-authority、consultation-workflow-contract、consultation-birth-time-mode合跑 88 tests、87 pass / 1 fail。单独再跑consultation-birth-time-mode为 9 pass / 1 fail;失败项是本机无法创建 skill symlink(EPERM),同文件的咨询 route 顺序断言通过。tsc --noEmit通过;npm run lint0 error。未部署。 - 防复发:普通咨询在扣点前必须经 subject resolver。other 查询必须同时约束
id与当前user_id,失败不得调用loadProfile作为退路。客户端出生字段不得进入toolInput。已有消息的会话绑定必须与库存值一致,否则拒绝整次 PATCH。 - 相关记录:BUG-001、BUG-010、BUG-011、BUG-018、BUG-073、BUG-076、BUG-188
- 复发自:无
- 修复版本:本次分支提交(未推送,未部署)
BUG-998 | 本仓中文断句使用正则后行断言,Safari 16.4 以下在解析期失败
- 状态:resolved
- 首次发现:2026-09-17(写在 BUG-936 的已确认事实里,当时未单独编号;BUG-937 / BUG-938 已被咨询故障占用)
- 最近更新:2026-09-22
- 影响面:
frontend/src/lib/sentence-split.ts;原先后行断言在个人报告断句、校正开场句、引擎含义展示、采用旁白、采集提示、轮次旁白 - 用户现象:无单独的用户报告。若设备 Safari 低于 16.4,含后行断言的 chunk 会在解析期抛出 SyntaxError,React 不会 hydrate。这不能解释「以前能用、这次不能用」的设备,也不能单独修好 BUG-936。
- 触发条件:浏览器解析本仓断句 chunk,且不支持正则后行断言。
- 根因:八处
split使用后行断言来「在句末标点后切开并保留标点」。这是语法,不是运行时 polyfill 能补的。 - 修复:共用
splitAfterSentencePunctuation,用手写扫描在标点后切开并保留标点。frontend/src不再出现后行断言。Next 运行时里的类静态块改不了,语法下限仍是 Safari 16.4。 - 验证:
frontend/tests/sentence-split.test.ts用表驱动用例和旧正则对照,确认切开结果逐字一致;源码合同扫描frontend/src无后行断言。校正与报告的既有断言未改。 - 防复发:
frontend/src不得再写后行断言。测试文件可以保留旧正则作对照。 - 相关记录:BUG-936
- 复发自:无
- 修复版本:未发布(本分支未推送)
BUG-999 | 普通报告导出仍带出内部技法与工作流字段
- 状态:resolved(本地候选,未部署)
- 首次发现:2026-09-22
- 最近更新:2026-09-22
- 影响面:咨询 Markdown 导出、个人报告阅读、普通 Markdown 下载、
GET /api/reports/:id与报告列表摘要。聊天消息行不在本次回归里。 - 用户现象:聊天里已经不显示内部证据徽章,但导出的报告和已存 Markdown 仍可能出现
technique_truth、workflow_route、workflow_status、precise_timing、missing_layers,以及评分、内部地址、密钥样式字段、模型调试和 job/attempt/provider 元数据。 - 触发条件:从咨询会话复制或导出报告;打开或下载一份已经写好的个人长报告,包括命中旧缓存 Markdown 的情况。
- 根因:聊天层按 BUG-011 隐藏了这些字段,报告阅读、导出和 API 没有同一套普通用户 allowlist。普通下载还把
/professional-reference当成正文来源。可见性不能靠接口名字判断。 - 修复:新增
frontend/src/lib/report-public-projection.ts。文档类型显式区分聊天导出、个人报告详情、普通 Markdown 下载和专业参考。普通输出只保留正文、结论、行动建议、自然语言限制、图盘围栏和安全的引擎 SVG。内部限制改写成白话,不写出内部键。阅读 API、详情分类、普通下载和列表摘要都走这层投影。专业参考响应标明documentKind: professional_reference;本轮没有单独专业权限,所以它的正文同样 fail-safe 到普通投影。普通下载改为读GET /api/reports/:id,不再请求专业参考。 - 验证:
frontend/tests/report-public-projection.test.ts覆盖字段在与不在、旧缓存、专业参考种类、图盘围栏、HTML/script/iframe/object/embed/img、危险 URL,以及普通输出不含内部 URL、密钥和模型字段。consultation-report-export、personal-report-longform-md、报告归属与 ready 检查的测试名保留。tsc --noEmit通过,npm run lint0 error。没有登录态,浏览器导出未验收,见docs/testing/report-public-content-20260922.md。 - 防复发:普通报告的公开字段以 allowlist 为准,不能只靠删除若干字符串。缓存命中和已存 Markdown 必须再过投影。专业参考不得再被普通下载当作 fallback。不得放宽 Markdown 的 XSS、危险 URL、HTML 和
jyotish-chart围栏合同。 - 相关记录:BUG-011、BUG-058、BUG-188
- 复发自:无
- 修复版本:本分支
fix(report): project ordinary reports onto public prose only(未推送,未部署)
BUG-1000 | 普通聊天顶栏不能在开口前选择人物
- 状态:resolved
- 首次发现:2026-09-22
- 最近更新:2026-09-22
- 影响面:普通聊天顶栏的人物入口;空会话的待发送绑定;已有消息会话的换人路径
- 用户现象:服务端已经能按会话绑定人物,但顶栏仍是一段不能点的名字。新对话沿用账户里上次选中的星盘,开口前看不见、也换不了人。开口之后没有「新建会话并使用此人物」这条路。
- 触发条件:打开普通聊天,想在第一条消息前换成另一位本人名下的人物;或在已经说过话的会话里点另一个人。
- 根因:BUG-997 只补了服务端真值。
startNewChat仍把activeChartId抄进新会话,顶栏.chat-header-chart仍是静态span,没有选择器。历史会话打开时还会把该人物写回账户级activeChartId。 - 修复:普通咨询顶栏改为「当前星盘」按钮。本人是看得见的默认。空会话选择只更新该会话的绑定,已落库的空会话 PATCH 只提交 id 和角色,姓名由服务端填写。已有消息的会话不改绑,只提供新建会话。人物列表失败、未读完或不完整时不写入绑定。资料已删除的旧会话仍可阅读,发送阻断,不退回本人。报告、每日星语和生时校正不挂这个选择器。
- 验证:见
docs/tasks/PROGRESS-chat-subject-picker-20260922.md。没有登录态,浏览器点击未验收,见docs/testing/chat-subject-picker-20260922.md。 - 防复发:新普通会话的人物 id 固定为本人,不读
activeChartId。打开历史会话不得改写该会话绑定,也不得把绑定抄回activeChartId。有消息的会话不得 PATCH 人物。列表未就绪时不得创建绑定。 - 相关记录:BUG-997
- 复发自:无
- 修复版本:本分支
fix(chat): choose the chart person before the first message(未推送,未部署)
BUG-1002 | 首屏揭幕标记在没有 documentElement 时抛错,外壳注册测试整组失败
- 状态:resolved
- 首次发现:2026-09-22
- 最近更新:2026-09-22
- 影响面:
useHomeShellRegistration的揭幕标记;frontend/tests/session-list-lifecycle.test.tsx - 用户现象:无单独的用户界面现象。门禁 run 1445 在
6d06fca0变红,发布因此停在1bc6a954。随后 run 1448 在3fed71ab同样失败。 - 触发条件:外壳测试挂载这个 hook 时
document存在,但document.documentElement不存在。 - 根因:BUG-936 的揭幕标记写在独立 effect 里,只判断了
document,然后直接读document.documentElement.dataset。测试环境没有documentElement,effect 抛TypeError,同文件的外壳注册用例整组失败。真实浏览器有documentElement,这条不是那 7 条后来才出现的 CSS 断点 / composer 失败。 - 修复:没有
documentElement时直接返回,不写标记,也不抛错。有根节点时仍写data-hydrated="1"。 - 验证:
session-list-lifecycle.test.tsx里原先抛错的四条(home registration 两种 StrictMode、shell callbacks、401 signed-out)恢复通过;first-paint-fallback-contract.test.ts锁住「没有 documentElement 就返回」。 - 防复发:揭幕标记不得在
documentElement缺失时抛错。源码合同要求先判断!document.documentElement。 - 相关记录:BUG-936
- 复发自:无
- 修复版本:本分支,尚未部署
BUG-1003 | 对外原始附录带出工程状态词与第三方页码
- 状态:investigating(原实现已合入 staging,本轮清洗一致性修复待验证,未确认部署)
- 首次发现:2026-09-22
- 最近更新:2026-09-23
- 影响面:
/api/professional_report_reference的对外 Markdown、报告独立原始附录下载;内部审计与 CLI 不变。 - 用户现象:附录出现内部函数名、工程状态词、第三方引擎名与产品页码。
- 触发条件:原始引擎导出作为读者附录返回,而非普通正文投影。
- 根因:内部审计与读者副本未分开;BUG-999 的普通正文 allowlist 不覆盖附录。初版 Python / TS 两份规则又因 Unicode 单词边界不同漏掉 Python 第三方页码。
- 修复:
bbd96d3b增加读者副本清洗;本轮 F4 将 Python / TS 规则收敛为 scripts 下共享 JSON,正则采用相同 ASCII 边界,保留行数、原始字段、英文散文与图盘围栏,不以整行删除代替翻译。 - 验证:本轮主会话报告定向 165/165、Python 定向 101/101 通过,含完整虚构 golden 两端逐字一致、页码归零及行/表/数字/图盘字节保留。前轮相差 36 行的问题已有回归覆盖;完整门和部署仍未通过,详见 PROGRESS。
- 防复发:保留
test_reader_appendix_language.py与前端原始附录清洗 / golden 回归;F4 必须覆盖同一真实引擎虚构 fixture 两侧逐字一致及页码归零。普通正文投影与安全边界不变。 - 相关记录:BUG-999、BUG-1004;
TASK-report-density-fix-20260923.mdF4 - 复发自:BUG-999 同现象族的未覆盖通道,不是撤销普通正文限制。
- 修复版本:原实现
bbd96d3b;本轮工作树修复待验证,未确认部署。
BUG-1004 | 读者没有独立完整原始附录入口
- 状态:investigating(入口已实现,受控浏览器与部署待验)
- 首次发现:2026-09-22
- 最近更新:2026-09-23
- 影响面:报告下载动作、
POST /api/reports/[reportId]/raw-appendix、新长报告计算快照。 - 用户现象:普通下载只提供净化正文,完整计算表不能作为独立附录取得。
- 触发条件:用户需要核对原始表格,但只有 BUG-999 建立的普通正文下载通道。
- 根因:缺独立附录产物与入口;不能把完整表格塞回普通正文投影作为补救。
- 修复:
bbd96d3b新增独立附录路由和下载动作,普通 Markdown 仍走原投影。新计算通过既有 section RPC 持久 snapshot,重试从快照恢复结构化数据;快照行从章节列表及错误聚合排除。旧 Markdown-only 缓存不回填。 - 验证:前轮验收入口与快照读写静态核对通过;
personal-report-density-retry.test.ts已含中断、并发、损坏绑定、旧缓存等用例。测试存在不等于本轮通过;登录态下载与重试真人证据仍缺,见 PROGRESS。 - 防复发:
personal-report-raw-appendix.test.ts、personal-report-density-retry.test.ts与report-public-projection.test.ts分别守住独立附录、快照恢复和普通下载不变;不能让 snapshot 成为 writer 章节,不能借附录授权放宽 XSS / 危险 URL 合同。 - 相关记录:BUG-999、BUG-1003、BUG-1005
- 复发自:无;为经产品授权新增的独立通道,普通下载限制仍有效。
- 修复版本:
bbd96d3b已合入 staging;未确认部署。 - 2026-09-24 产品修订:
TASK-report-reader-actions-20260924D2 撤下阅读页原始附录按钮,普通导出投影与快照恢复不变;不新开 Bug。清理组件、raw-appendix 路由和 helper 的删除操作被权限系统拒绝,本轮遵从用户后续边界,三个文件及其回归测试原样保留,状态为删除权限 blocked,不能声称完整退役或部署通过。
BUG-1005 | 个人报告没有可承载引擎事实表的结构化层
- 状态:investigating(事实层已实现,可读性与打印补修待验)
- 首次发现:2026-09-22
- 最近更新:2026-09-23
- 影响面:报告契约、服务端装配、报告页事实表与 writer 边界。
- 用户现象:引擎已有大运、力量、功能吉凶等表,但报告只有叙述与图盘;初版补表后又把路径清单显示给读者,见 BUG-1009。
- 触发条件:生成包含上述已计算层的个人报告。
- 根因:原报告没有
factTables;初版未规定用户列结构,writer 拒绝表格与打印闭合也缺回归覆盖。 - 修复:
bbd96d3b增加 8 组服务端事实层,不给 writer 可写字段;保留证据状态。F1 补真正列结构,F5 补 writer 提示与拒绝测试,F6 补全折叠时打印。Sade Sati 只展示源中已有框架,三轮日期计算范围外。 - 验证:本轮报告定向 165/165、Python 定向 101/101 通过;writer 去拦截变异使 4 个拒绝用例全部变红,正常对照仍绿。Chrome 153 隔离真实组件 28/28,全部折叠与混合状态 PDF 各 5 页、各 130/130 行完整,打印后恢复;不是受控登录 E2E。完整构建受 Windows symlink EPERM 阻塞,尚未提交或部署。
- 防复发:
report-fact-tables.test.ts真实 golden 与逐行来源回查不可削弱;writer 的 Markdown / HTML 表格或factTables输出必须 fail closed;打印测试必须从全折叠状态开始并核对打印后恢复。 - 相关记录:BUG-1009、BUG-153、BUG-999;修复单 F1/F5/F6
- 复发自:BUG-153 的打印合同遗漏新增事实表 disclosure;旧静态合同不能证明关闭的 details 进入 PDF。
- 修复版本:原实现
bbd96d3b;本轮补修待验证,未确认部署。
BUG-1006 | 报告契约分盘种类上限不足
- 状态:investigating(16 种契约已实现,本轮构建与真机待验)
- 首次发现:2026-09-22
- 最近更新:2026-09-23
- 影响面:
CHART_IDS、长报告分盘装配、报告页布局。 - 用户现象:引擎可提供更多分盘,但报告契约只接受 9 种,无法交付约定的 16 张。
- 触发条件:新生成完整个人报告并查看分盘。
- 根因:契约枚举和服务端交付层未承载完整目标分盘集合。
- 修复:
bbd96d3b将枚举扩为任务书指定 16 种,装配真实宫位与星体,沿用 BUG-616 grid/card 和 BUG-617 memoized 文章树;旧 v1 枚举兼容保留。 - 验证:前轮验收记录
/Static、gzip +0.007%,golden 覆盖 16 盘完整宫位;本轮未重跑,不作为修复版通过。16 张盘不重叠、滚动不重建及旧报告读取待受控浏览器复核。 - 防复发:
report-fact-tables.test.ts检查图盘完整性;personal-report-markdown-view.test.tsx中 grid、禁止 float / 负 margin 与文章无滚动态合同仍在,不得只检查是否存在 SVG。 - 相关记录:BUG-616、BUG-617、BUG-1005
- 复发自:无;分盘扩容须持续遵守旧布局与性能防线。
- 修复版本:
bbd96d3b已合入 staging;未确认部署。
BUG-1007 | 辅助大运族缺少读者可理解的适用性说明
- 状态:investigating(新生成路径已实现,回归与真人待验)
- 首次发现:2026-09-22
- 最近更新:2026-09-23
- 影响面:新报告原始附录的辅助时间轴适用性说明。
- 用户现象:辅助大运只剩状态词,不能看出是否适用、缺什么,以及只能交叉核对的边界。
- 触发条件:读取辅助大运族数据,包括引擎已算出但仍限制可断言程度的层。
- 根因:对外附录缺读者语言说明,未把引擎适用性和使用边界一同交付。
- 修复:
bbd96d3b对 16 个大运族提供逐族说明,读取引擎applicable而不自行放宽;标明第二时间轴、不单独生成应期。新快照保存说明,旧 Markdown-only 缓存不补算、不回填。 - 验证:前轮验收记录 16 族各有白话说明、模块不自行判定;本轮清洗变化后的回归与受控下载待验。
- 防复发:真实 golden 的附录 / durable retry 回归须保住适用性与限制;普通正文投影不能接纳这些替代时间轴成为断言,旧缓存缺证据必须如实降级。
- 相关记录:BUG-1003、BUG-1004、BUG-999
- 复发自:无;不把展示授权当作证据等级升级。
- 修复版本:
bbd96d3b已合入 staging;未确认部署。
BUG-1008 | 任务书写入真实个人出生资料并推到 staging
- 状态:mitigated(staging 历史已重写,不再含该资料;旧提交在 Gitea 上仍可按 SHA 匿名访问,待服务器垃圾回收)
- 首次发现:2026-09-23
- 最近更新:2026-09-23
- 影响面:
docs/tasks/TASK-report-density-20260922.md,自 stagingbaeec66f起;Gitea staging 历史。GitHub 只读镜像最后同步于 2026-08-14(12414971,落后 1,314 个提交),尚未包含这份资料。 - 用户现象:无用户界面现象。隐私门
tests/test_repo_privacy_markers.py::test_tracked_repository_has_no_privacy_markers在下一次代码推送f968cb21上命中 R001 / R003,指向该任务书第 13 行,使该实现的门禁变红。 - 触发条件:撰写任务书的对照实测段落时,把对照报告那张盘的出生日期、时刻与经纬度原样写入;同一文件第 7 行还写入了含个人姓名的本机路径。
- 根因:纯文档推送不触发门禁(
docs/**不在deploy/gated-paths.txt),隐私扫描只在之后第一次含门禁路径的推送时运行。撰写者推送前没有自跑扫描。任务书的硬红线第 6 条写了"出生资料不进仓",但撰写者自己的实测段落没有过同一把尺。 - 修复:两处改为不含资料的描述(
TASK-report-density-fix-20260923同一提交)。 - 验证:修复后在 staging-docs 树上重跑同一扫描,只剩 1 处,为新 fixture 的数值碰撞(已独立重跑确认是虚构输入的引擎时间戳,见 BUG-1010)。
- 防复发:凡是写入实测数字的文档,推送前必须跑
python3 -m pytest tests/test_repo_privacy_markers.py -q,结果写进提交说明。实测段落只写"真实个人资料"或虚构 / 公开名人输入,不写具体值。建议(需产品负责人决定,涉及.gitea/workflows/**):让纯文档推送也跑隐私扫描。 - 历史清除:产品负责人 2026-09-23 决定清除。按原作者、日期与说明重建泄漏之后的三个提交,唯一改动是该任务书换成已清理版本:
baeec66f→a7f82dc2、f968cb21→bbd96d3b、fcca56fe→abf28321,以--force-with-lease推送。核对:新旧 head 的树逐字节相同;泄漏 blob 在新历史中出现 0 次;此前没有其他分支建在该段历史上。 - 未决:Gitea 仓库可匿名访问,旧 SHA 的 raw 地址重写后仍返回 200。须 Gitea 管理员执行服务器垃圾回收,之后以匿名请求确认返回 404 才可改 resolved。在此之前 GitHub 只读镜像不得重新同步。
- 相关记录:BUG-999
- 复发自:无(与 2026-09-20 的本仓个人案例清除属同一类资料)
- 修复版本:
abf28321(HEAD 清除);历史重写见上
BUG-1009 | 普通报告事实表显示引擎字段路径而不是可读列
- 状态:investigating(F1 修复执行中,待定向与独立验收)
- 首次发现:2026-09-23
- 最近更新:2026-09-23
- 影响面:
assembleReportFactTables、事实表渲染、普通报告可见单元格。 - 用户现象:8 组表为“原始字段 / 数值或状态”两列键值清单;出现 snake_case、数组下标、内部时间字段,主运年数还可能被出生时剩余量替代。
- 触发条件:查看原报告密度实现新增的普通报告事实表。
- 根因:原任务书只要求可追溯,未明确用户列结构;实现把来源路径直接当行名。普通正文的 BUG-999 投影不覆盖这块新增结构化渲染。
- 修复:F1 按修复单逐组改成主运 / 分运 / 小运、SAV / BAV、六分量、功能吉凶、行星状态、特殊点、阶段框架、年度与 Saham 表。来源保留在不可见
sourcePath,数字不补算,主运年数与出生剩余量分开。 - 验证:前轮 948 个叶子行的缺陷证据不再作为新表行数合同。本轮真实 golden 的 8 组、11 张实际表、130 行、545 单元格在 Chrome 完整渲染;可见 snake_case、数组下标、超两位小数均为零;Node 回归核对来源路径、原始数值与缺值不补算。首段主运直接取 full_years 显示 18.00,余额 0.72 仅说明;年度源无行星位置,保留空表,不冒用本命数据。构建与登录 E2E 缺口见 PROGRESS,尚未部署。
- 防复发:使用真实引擎虚构 golden,同时测试可见内容与来源追溯;不能只通过“值来自引擎”就宣称面向读者可读,不能削弱 BUG-999 普通正文安全合同。
- 相关记录:BUG-1005、BUG-999;
TASK-report-density-fix-20260923.mdF1 - 复发自:BUG-999 同现象族的新增表格通道,旧正文测试未覆盖。
- 修复版本:缺陷引入
bbd96d3b;修复在codex/report-density-fix-20260923工作树实施,待验证,未确认部署。
BUG-1010 | 新虚构 fixture 的隐私扫描数值碰撞未登记
- 状态:investigating(F2 按精确数值碰撞机制处理,待本轮验证)
- 首次发现:2026-09-23
- 最近更新:2026-09-23
- 影响面:
tests/test_repo_privacy_markers.py与frontend/tests/fixtures/report-density-fictional-reader.json。 - 用户现象:无用户界面现象;质量门隐私扫描命中虚构引擎时间戳的数值片段,阻断发货。
- 触发条件:新增真实引擎虚构 golden 后运行仓库隐私门。
- 根因:时间戳数值与私有标记发生碰撞,但新 fixture 未登记到既有已审阅数值碰撞机制;不是 fixture 包含真实个人资料。任务书真实泄漏另见 BUG-1008,不得混称误报。
- 修复:F2 只登记已审阅的单处数值碰撞,说明来自虚构输入的引擎时间戳;不更改私有标记、扫描规则,也不豁免整份 fixture 或目录。
- 验证:前轮独立重跑已复现该虚构时间戳,本轮没有重跑引擎。主会话转述 F2:精确登记及反例 62 passed / 1 deselected;完整专项用 audit hook 尊重
pending_packets禁读,62 pass / 1 fail(17 个保护文件 READ_ERROR),其余 4,647 文件无命中。完整隐私门未绿,不能标 resolved;不复制碰撞数值或私有 marker。 - 防复发:新增 golden 必须保留虚构来源声明并跑隐私扫描;数值碰撞逐处核实、精确登记,真实泄漏必须分别清除,不能靠扩大 allowlist 隐藏。
- 相关记录:BUG-1008、BUG-1009;
TASK-report-density-fix-20260923.mdF2 - 复发自:无;既有扫描正确触发,缺的是已核实碰撞的精确登记。
- 修复版本:缺陷引入
bbd96d3b;F2 工作树修复待验证,未确认部署。
BUG-1011 | 快照落库的数据库测试把 42 万字节 SQL 当一个命令行参数,Linux 上启动即失败
- 状态:resolved(Gitea run 2858 / validate 6349 全绿,完整快照与新增 stdin 事务合同通过;staging 已部署
018b2b48) - 首次发现:2026-09-23
- 最近更新:2026-09-23(真实 Linux 门禁、迁移、部署与健康 SHA 核验通过;本机 Docker 地址池缺口仍保留)
- 影响面:
frontend/tests/database-personal-report-sections.test.ts中报告密度快照一例;backend-quality-gate的数据库测试步骤。生产写入走 RPC 请求体,不经命令行,不受影响。 - 用户现象:无用户界面现象。门禁在含该测试的提交上变红;报告快照行在真实 Postgres 里的写入、幂等与越权隔离从未被验证。
- 触发条件:在 Linux 上以 Docker 跑
npm run test:db(即门禁环境)。 - 根因:
tests/helpers/postgres-fixture.ts的psql/psqlAs用psql -Atc <sql>把整段 SQL 作为单个 argv 元素传给docker compose exec。该测试把整份快照(含 374,097 字节的附录 Markdown)内联进 SQL,单个参数 428,557 字节,超过 Linux 单参数上限MAX_ARG_STRLEN131,072 字节。验收时用同一段 SQL 以同样方式调用/bin/true,稳定得到spawn E2BIG。执行方两轮都因 Docker 地址池耗尽未跑数据库测试,验收机无 Docker,因此一直没暴露。 - 修复:按更正任务 G1 新增
psqlScriptAs,通过 stdin 传 SQL,使用-X -At -v ON_ERROR_STOP=1 --single-transaction -f -和 16 MiB 输出缓冲。共享psql/psqlAs一字未改;仅完整快照写入及大 JSON 读回改用新 helper,原 fixture、幂等与 RLS 断言不变。 - 验证:run 2858 / validate 6349:3,764 pass / 0 fail / 0 skip,相对 run 2857 既有测试名消失 0、新增 2;
personal report sections enforce owner-read RLS and service-owned durable transitions与stdin PostgreSQL scripts carry large SQL and preserve transaction-local settings通过。完整 test:db 的 47 个名称均在门禁日志中找到成功记录,包含超 131,072 字节 SQL、事务本地设置和超 1 MiB 输出。publish 6350、migrate run 2859/job 6351、deploy run 2860/job 6352 均成功;health 200 且双 deployment SHA 等于修复提交,login 200、匿名 account 401。本机 tsc/lint 通过,数据库因 Docker 地址池耗尽未进入 SQL,Windows build 因 symlink EPERM 失败,未冒充本机通过。 - 防复发:超过数十 KB 的 SQL 一律走单事务的标准输入专用函数,并显式设
maxBuffer;专用函数须有测试覆盖超过 131,072 字节的 SQL,并证明事务内set_config(…, true)对后续语句可见。 - 相关记录:BUG-1005、BUG-1009
- 复发自:无
- 修复版本:
018b2b4883eede47dae60f8860d92da0fac82f5e(已推 staging 并部署;未提升 main)。进度见PROGRESS-report-density-fix2-20260923.md。
BUG-1012 | 首页入口新增 375px 断点违反既有白名单
- 状态:resolved(run 2857 的 H1 门禁通过;staging 总门禁仍因 BUG-1011 失败,尚未部署)
- 首次发现:2026-09-23
- 最近更新:2026-09-23
- 影响面:首页入口响应式样式与 staging 门禁。
- 用户现象:无独立已复现线上现象;新增切点导致断点合同失败,阻断发布。
- 触发条件:运行 viewport-breakpoint-contract 的 CSS width breakpoints stay on the published allowlist。
- 根因:45ec4617 新增 375px 规则,未遵守 BUG-697 收敛口径;此前红色门禁掩盖新增失败。
- 修复:按任务决策将两条防溢出规则并入既有空 480px 块,不扩白名单,不更改按钮尺寸。
- 验证:修复前真实 run 2856 / job 6345 证实失败;修复后测试和五宽度浏览器证据见 PROGRESS-staging-gate-red-20260923.md。
- 防复发:保留断点白名单测试;360/375/390/414/480 宽度核对溢出,376~480 若出现可见变化须停止报告。
- 相关记录:BUG-697、BUG-1002;TASK-staging-gate-red-20260923.md。
- 复发自:BUG-697 断点收敛合同仍在,红色门禁下未逐项辨别新增失败。
- 修复版本:
36a737616c67a251610662ccab76845b193993fb(staging;未部署)。
BUG-1013 | 聊天人物选择器恢复焦点可能滚动面板
- 状态:resolved(run 2857 的 H2 门禁通过;staging 总门禁仍因 BUG-1011 失败,尚未部署)
- 首次发现:2026-09-23
- 最近更新:2026-09-23
- 影响面:普通聊天人物选择器关闭后的焦点与面板滚动。
- 用户现象:恢复顶栏入口焦点时浏览器可能自动滚动聊天面板;scroll guard 合同失败。
- 触发条件:选择器关闭或 Escape 后调用 triggerRef.current.focus。
- 根因:2a3013f2 两处新增 focus() 未携带 preventScroll,旧滚动守卫仍有效但失败被已有红色淹没。
- 修复:两处均显式 focus({ preventScroll: true }),不改变焦点落点或选择人物的业务。
- 验证:修复前真实 run 2856 / job 6345 证实失败;修复后 guard、焦点回归与浏览器证据见本轮 PROGRESS。
- 防复发:保留全组件 scroll guard,不增加豁免;picker 两条恢复路径都必须带 preventScroll。
- 相关记录:BUG-1000、BUG-1002;TASK-staging-gate-red-20260923.md。
- 复发自:聊天面板既有 focus 防滚动合同的新增组件遗漏。
- 修复版本:
36a737616c67a251610662ccab76845b193993fb(staging;未部署)。
BUG-1015 | 次级页「新建对话」没有新建意图,回首页打开旧对话
- 状态:resolved(Claude 2026-09-24 于 Linux/Node 20 独立验收
1420471a:tsc 0 错、lint 0 error、全量 3807 条 / 56 红与改前基线(8902e484)失败名单逐条相同、next build --webpack四路由标记与基线一致(//chart/ephemeris○,/reports*ƒ)、首屏 gzip 130,932 B 与基线相同;staging/api/healthdeployment.gitCommit = 1420471a;真机清单docs/testing/secondary-new-chat-20260923.md由产品走) - 首次发现:2026-09-23
- 最近更新:2026-09-23
- 影响面:星盘、星历、报告列表及详情页的只读侧栏新建入口与首页启动落点。
- 用户现象:在次级页点击「新建对话」后进入旧会话,而不是空咨询。
- 触发条件:只读侧栏缺少首页 controls,其新建入口原为无意图的
/;若还有旧 reserved 咨询,启动恢复还会主动激活旧会话。 - 根因:回首页与新建共用无参数地址,首页按既有 URL/登录存根/默认选择规则落点;原任务书只补 URL 意图,独立验收又确认后台恢复在新建激活后覆盖活动会话。前者不是缓存或导航性能复发,后者是同一入口的组合路径遗漏。
- 修复:唯一 URL helper 生成
/?new=1、按参数存在性识别,优先于 c 与登录存根;sessionHref 一律消费 new。首页复用已有本地 createSession 分支,激活前 replace 清除 new/c 并清存根;产品追加授权后,在旧咨询恢复完成的同步区块内恢复新建落点并清本次恢复提示,保留 pending/recovering,不取消后台任务。首问保存与列表过滤沿用 BUG-989。 - 验证:URL/落点/侧栏及既有深链、列表、增长合同通过;新增动态执行真实恢复函数及首页装配区块的测试,new-chat 保持新落点、无 new 保持旧恢复行为。最终全量数字、独立验收及环境证据见
docs/tasks/PROGRESS-secondary-new-chat-intent-20260923.md。未以源码测试冒充登录浏览器或部署验收。 - 防复发:保持 BUG-744 单侧栏、BUG-745/927 共享外壳、BUG-989 首问前不落库/不写 c/列表无 draft;新增优先级、参数消费、激活顺序、本地创建与 reserved 恢复冲突测试。原合同只锁导航方式及无参数默认落点,没有连接“新建”意图,故未拦住本次问题;既有断言未删除或弱化。
- 相关记录:BUG-744、BUG-745、BUG-927、BUG-989、BUG-705、BUG-995;
TASK-secondary-new-chat-intent-20260923.md(D7 为本轮追加授权)。 - 复发自:无;与 BUG-745 同入口但不同根因。
- 修复版本:
8902e484(含于1420471a,staging 已部署、health 核对一致)
BUG-1018 | 报告列表元信息直接显示英文深度与主题 id
- 状态:resolved(Claude 2026-09-24 于 Linux/Node 20 独立验收
1420471a:tsc 0 错、lint 0 error、全量 3807 条 / 56 红与改前基线(8902e484)失败名单逐条相同、next build --webpack四路由标记与基线一致(//chart/ephemeris○,/reports*ƒ)、首屏 gzip 130,932 B 与基线相同;staging/api/healthdeployment.gitCommit = 1420471a;红线核对:无window.confirm/ 剪贴板、旧「导出报告(.md)」0 命中、下载只走downloadMarkdownReport、抽屉零 fetch、新增断点只有 860;D5 逐字节锁与ordinaryOutputLeaks为空由report-export-blocks.test.ts锁住;真机清单docs/testing/report-chapter-export-20260924.md由产品走) - 首次发现 / 最近更新:2026-09-24。
- 现象 / 触发:打开有报告的列表,元信息出现 standard、career 等内部 id,日期未按产品要求中文全写。
- 根因:
rowMeta直接拼report.depth与report.themes;已存在主题中文映射但此入口未复用,深度映射缺失。旧入口测试只锁状态 / 导出路径,没有执行元信息 formatter。 - 修复:深度映射与主题映射同置 progress helper;rowMeta 使用中文全日期、中文深度和主题,未知 id 原样回退,非法日期显示时间未知;不改 GET 字段。
- 验证:真实 formatter 回归覆盖 standard / career、未知 id、空主题及无效日期;真实组件挂载显示中文。定向测试、类型与独立浏览器证据由本轮进度汇总,未以源码断言冒充真人。
- 防复发:新增
report-row-delete.test.tsx的元信息执行测试;既有报告 API 日期投影、列表 cardSummary 合同保留。 - 关联:BUG-352(日期序列化 / 失败码)、BUG-574(列表字段查询);不是这两项复发,本轮不改路由。
- 修复版本:
1420471a(staging 已部署、health 核对一致)
BUG-1019 | 报告 DELETE 接口存在但列表没有删除入口
- 状态:resolved(Claude 2026-09-24 于 Linux/Node 20 独立验收
1420471a:tsc 0 错、lint 0 error、全量 3807 条 / 56 红与改前基线(8902e484)失败名单逐条相同、next build --webpack四路由标记与基线一致(//chart/ephemeris○,/reports*ƒ)、首屏 gzip 130,932 B 与基线相同;staging/api/healthdeployment.gitCommit = 1420471a;红线核对:无window.confirm/ 剪贴板、旧「导出报告(.md)」0 命中、下载只走downloadMarkdownReport、抽屉零 fetch、新增断点只有 860;D5 逐字节锁与ordinaryOutputLeaks为空由report-export-blocks.test.ts锁住;真机清单docs/testing/report-chapter-export-20260924.md由产品走) - 首次发现 / 最近更新:2026-09-24。
- 现象 / 触发:报告列表没有删除按钮,用户无法管理已完成、生成中或未完成记录。
- 根因:owned DELETE API 已存在且有鉴权测试,但列表没有装配调用;旧测试只覆盖服务端删除,不检查用户可达入口。
- 修复:每行 44px 菜单只有删除;行内确认 / 取消;same-origin DELETE,in-flight ref 阻止双发。失败保留行和短错误,成功移除并 invalidate reports cache。cache epoch 拒绝旧 GET 回写,组件 tombstone 拒绝旧快照使行复活,旧 finally 不清掉新请求。
- 验证:真实挂载覆盖取消不请求、HTTP / 网络失败保留、重复点击仅一个 DELETE、成功回调;整个 Center 挂载覆盖成功移除及旧 GET 返回仍不复活;独立 cache race 验证 reset / 新旧请求。服务端鉴权 / owned-delete 既有 API 回归保留;不声称受控账号删除持久化已验证。
- 防复发:
report-row-delete.test.tsx动态交互与 race 锁;不用 window.confirm,不新增接口 / 数据库 / GET 字段。 - 关联:BUG-154(报告列表入口)、BUG-574(列表查询);分块导出是功能变更另记 CHANGELOG,普通投影继续遵守 BUG-999 / 1003。
- 修复版本:
1420471a(staging 已部署、health 核对一致)
BUG-1014 | 输入框源码合同未跟上已删除人物的发送限制
- 状态:resolved(run 2857 的 H3 门禁通过;staging 总门禁仍因 BUG-1011 失败,尚未部署)
- 首次发现:2026-09-23
- 最近更新:2026-09-23
- 影响面:chat-composer-queue 源码合同,不是输入框业务回归。
- 用户现象:源码正则仍期待旧表达式,门禁失败;生成中可输入的业务性质仍成立。
- 触发条件:BUG-1000 在 inputDisabled 加入 subjectDeleted || 后运行旧合同。
- 根因:合同固定整个旧串,没有同步合理的已删除资料限制,且未独立检查禁止生成态禁用的性质。
- 修复:只更新测试期望,保留 page.tsx 业务;新增 inputDisabled 不含 isLoading/cancellationPending 的检查。
- 验证:修复前真实 run 2856 / job 6345 证实失败;正常/变异验证及原值、新值、原因见本轮 PROGRESS。
- 防复发:原测试名和断言目的保留;临时注入 isLoading || 时新检查必须失败,不通过修改业务迎合源码测试。
- 相关记录:BUG-1000、BUG-1002;TASK-staging-gate-red-20260923.md。
- 复发自:无;源码合同落后于授权业务改动。
- 修复版本:
36a737616c67a251610662ccab76845b193993fb(staging;未部署)。
BUG-1016 | 星盘主盘等待句没有超时与登录出口,缓存异常路径留空
- 状态:resolved(Claude 2026-09-24 于 Linux/Node 20 独立验收
1420471a:tsc 0 错、lint 0 error、全量 3807 条 / 56 红与改前基线(8902e484)失败名单逐条相同、next build --webpack四路由标记与基线一致(//chart/ephemeris○,/reports*ƒ)、首屏 gzip 130,932 B 与基线相同;staging/api/healthdeployment.gitCommit = 1420471a;红线核对:chart-skeleton-breathe仅 CSS 一处声明一处引用、骨架只在 chart-page 组件、无 Python / 路由 / 并发改动;真机清单docs/testing/chart-page-skeleton-20260924.md由产品走) - 首次发现 / 最近更新:2026-09-24
- 影响面:星盘首屏 hook、共享快照缓存、fetch JSON 读取、盘位等待渲染。
- 现象 / 触发:请求不返回时一直显示等待句;401 跳转前仍为空;初始请求失败而缓存刚由其它路径补齐时 state 仍 null。
- 根因:只有 null / view,没有客户端期限;unauthenticated 快照映射为 null;catch 命中缓存后早退。预取与首次请求共用在途 promise,因此超时必须由共享请求持有,不能仅约束挂载后的新 fetch。
- 修复:共享请求(含预取和身份重试)25 秒 timeout signal;client_timeout 独立 status 对应 timedOut 文案;响应 JSON 的 abort 不误判 schema;缓存快照回填 state;401 在跳转前可见登录失败体。主盘空模型复用 VedicChartSvg,不复制几何,五 Tab 可见禁用;空盘与西洋盘共享唯一呼吸 CSS,减弱动态效果静止。
- 验证:chart-page-hook.test.ts 实际挂载 React hook,模拟期限触发、预取复用、缓存后到、401 缓存与响应、旧身份失败;chart-page-view.test.tsx 渲染骨架、禁用 Tab、空几何和 reduced-motion。完整成绩见本轮 PROGRESS,未把 Node 生命周期渲染当作浏览器证据。
- 防复发:BUG-716 / 966「不得骨架」条款由 2026-09-24 D1 仅限星盘盘位推翻;其它页面、表格和 spinner 禁令不动。首屏共享期限从预取起计时,旧身份完成或异常不得覆盖新缓存,读响应体同样受期限约束。
- 相关记录:BUG-715、716、717、723、966、996;TASK-chart-page-skeleton-wait-20260924。
- 复发自:BUG-966 的同一等待链覆盖不足;旧测试仅检验静态壳、缓存与无动画,未模拟挂起请求和后到缓存。BUG-996 的账户指纹与旧响应防线保留并追加旧异常回归。
- 修复版本:
1420471a(staging 已部署、health 核对一致)
BUG-1017 | 分盘与按需层失败仍永久显示等待句
- 状态:resolved(Claude 2026-09-24 于 Linux/Node 20 独立验收
1420471a:tsc 0 错、lint 0 error、全量 3807 条 / 56 红与改前基线(8902e484)失败名单逐条相同、next build --webpack四路由标记与基线一致(//chart/ephemeris○,/reports*ƒ)、首屏 gzip 130,932 B 与基线相同;staging/api/healthdeployment.gitCommit = 1420471a;红线核对:chart-skeleton-breathe仅 CSS 一处声明一处引用、骨架只在 chart-page 组件、无 Python / 路由 / 并发改动;真机清单docs/testing/chart-page-skeleton-20260924.md由产品走) - 首次发现 / 最近更新:2026-09-24
- 影响面:星盘 varga、Chara、西洋、七政按需数据与失败投影。
- 现象 / 触发:引擎忙、按需请求失败或抛错后 pending 清除,却无失败出口;分盘一直「这一分盘还没拿到」。
- 根因:hook 无 layerFailures,只接受顶层 ok;真实 assembleChartView 对后续层失败经 enginePayload 压为 null,外层仍 ok,连引擎 429 原因也在投影前丢失。该事实是任务书未覆盖的必要透传缺口。
- 修复:最小可选 layerFailures 投影保留既有引擎原因,未改路由、超时或并发;客户端逐层失败,重试先清失败再 pending,成功仅合并请求层,不抹其它已完成层。BFF rate_limited 保持原类,实际引擎429保留 engine_busy。分盘/西洋等待用空盘,大运/七政仍静态;失败均撤等待图并写对应原因。返回缺层不能标已加载,旧资料层结果不能写回。
- 验证:chart-page-hook.test.ts 真实 assemble→fetch解析→React hook 四层429、catch重试、响应体超时、BFF限流、无效429体、并发乱序、401及资料指纹过期;chart-page-view.test.tsx 分盘、西洋、七政、Chara 的失败句与等待图互斥。详见 PROGRESS。
- 防复发:层失败不能经 null 投影丢失;只有完整成功层可进入 loaded;失败不自动重试,显式点原 Tab/chip 重试,不增加入口。BUG-715/723 的429独立档及 BUG-717 按需、并发红线保留。
- 相关记录:BUG-715、716、717、723、966、996、1016。
- 复发自:BUG-715/723 的失败分档未覆盖按需层投影与组件出口;旧测试仅验主盘错误和端点不可用说明,未串联真实 busy 层响应到 hook。
- 修复版本:
1420471a(staging 已部署、health 核对一致)
BUG-1021 | 慢网首页半揭幕,模型与账户状态脱节
- 状态:resolved(Claude 2026-09-25:代码已验收,
fabe0114已部署且/api/healthgitCommit一致;真机清单由产品走) - 首次发现 / 最近更新:2026-09-24。
- 影响面:首页账户、会话、模型目录、咨询状态 bootstrap 与重试。
- 现象 / 触发:Slow 3G 强刷首页,原 8 秒串行预算先显示云端超时;SessionListProvider 后到仍写账户/会话,页面可能揭幕为模型不可用、发送禁用并重新询问称呼的半成品。
- 根因:Home 自有 8 秒 AbortController 与 SessionListProvider 独立请求脱节;模型目录失败只写 composer 提示却仍允许账户提交;渲染门未要求模型目录存在。
- 修复:引入 20 秒
HOME_BOOTSTRAP_SLOW_MS;模型、咨询状态与 provider 并行启动;必要数据齐全前统一等待,模型/必要请求失败保持全屏错误;重试取消旧请求、reload provider 并局部重跑;provider 使用可重入读取和旧 generation/controller 防护;首屏 ES5 fallback 调为 25 秒。 - 验证:
tsc --noEmit通过;首页与 fallback 定向合同通过;完整 lint 有 0 error、既有 warnings;浏览器与部署验收未在本地宣称通过。 - 防复发:保留一次等待一次揭幕、错误屏要求
account与modelCatalog,禁止 React 错误屏使用location.reload();provider 旧请求不得提交状态。 - 相关记录:BUG-479、BUG-927、BUG-936;
TASK-home-slow-network-20260924.md。 - 复发自:无;BUG-927 引入的 provider/Home 时序脱节首次在本单补齐门控。
- 修复版本:
659bd9600(待 staging 推送与部署核对)。
BUG-1022 | 账户与点数首次打开慢且离线预取会误触发刷新
- 状态:resolved(Claude 2026-09-25:代码已验收,
fabe0114已部署且/api/healthgitCommit一致;真机清单由产品走) - 首次发现 / 最近更新:2026-09-24。
- 影响面:账户菜单、BillingPanel 动态分包、套餐接口与支付/兑换余额同步。
- 现象 / 触发:首次打开账户与点数需等待懒加载 chunk 和套餐请求;面板挂载还会重复请求完整
/api/account;指针悬停离线时动态 import reject 可能被全局 stale-client recovery 当作 ChunkLoadError 并整页刷新。 - 根因:账单 chunk 无预取/静态占位;套餐零缓存;挂载账户请求结果被 Home 传入账户覆盖;prefetch reject 未隔离。
- 修复:账户菜单 pointer enter/down 预取 chunk 和套餐;chunk 未到时显示静态占位;套餐结果模块缓存 60 秒并后台刷新;独立 route 查询并行;删除挂载
/api/account,兑换和支付直接用响应credits通过既有共享事件同步;prefetch 显式 catch 隔离失败。 - 验证:账单、membership、epay、首页 bootstrap/fallback 定向合同共 65/65;TypeScript 通过;定向 lint 通过;完整 lint 0 error、既有 warnings;浏览器/部署验收待完成。
- 防复发:合同锁定无
/api/account、响应 credits 同步和 prefetch catch;动态 import 预取失败不得冒泡到全局刷新逻辑。 - 相关记录:BUG-479、BUG-1021、
TASK-home-slow-network-20260924.md。 - 复发自:BUG-554(账户与点数离开首页的旧入口已修复);本单是性能与慢网回归,不改变设置弹窗入口。
- 修复版本:
1fbe14995、6a097b6a3(待 staging 推送与部署核对)。
BUG-1020 | CRLF 普通报告下载保留图盘 JSON 围栏
- 状态:resolved(Claude 2026-09-25:代码已验收,
fabe0114已部署且/api/healthgitCommit一致;真机清单由产品走) - 首次发现 / 最近更新:2026-09-24。
- 影响面:旧普通Markdown下载与新分块导出的正文组。
- 现象 / 触发:真实引擎虚构golden仅将LF换成CRLF,正文下载残留22段图盘JSON;新分盘组仍可提取22盘。
- 根因:report-chart-block.ts的FENCE_RE在jyotish-chart语言标记后仅匹配LF,stripReportChartBlocks共用它;旧测试验证LF剥除和CRLF字节等价,没有覆盖含真实围栏的CRLF无JSON。
- 修复:按产品D14有限例外,将语言标记及围栏尾部边界改为匹配
\\r?\\n;不标准化整篇换行,不改SVG、投影、生成链或下载器。 - 验证:真实虚构reader golden的LF、CRLF、混合换行均输出0段
jyotish-chart围栏、22个SVG且ordinaryOutputLeaks为空;当时 LF 普通导出 SHA256 为3c52e8bfcfcb3ce3852bd5323867a69bc47d0f9aa821a34692172e7d61c6bcb8。定向测试已通过;尚无部署health证据。 - 2026-09-25:BUG-1026 把内部词从截断改成整行删除,上述 LF 哈希不再代表当前投影。围栏与 22 个 SVG 的合同仍在。新哈希见 BUG-1026。
- 防复发:report-chart-block与report-export-blocks定向回归覆盖真实golden CRLF/混合输入及独立LF hash锁;禁止只比较两个共同修改后的helper而声称历史输出不变。
- 相关记录:BUG-999/1003;TASK-report-chapter-export-fix-20260924.md。
- 复发自:BUG-999同输出边界的未覆盖换行路径,继承剥离器缺陷,非本轮新增回归。
- 修复版本:本地待提交。
BUG-906 | 星盘 SVG 把 CSS 的 height:auto 写成了属性
- 状态:investigating(分支已修,定向回归见进度记录;未部署,不标 resolved)
- 首次发现:2026-09-16
- 最近更新:2026-09-25
- 影响面:星盘页西洋盘
WesternWheelSvg、报告与星盘页共用的VedicChartSvg。 - 用户现象:控制台反复出现
Error: <svg> attribute height: Expected length, "auto".。盘仍能画出,但每张盘一条;报告页多张盘就多条。 - 触发条件:打开星盘页西洋盘,或打开含北印度盘的报告页。
- 根因:SVG 表现属性
height只接受长度或百分比。auto只是 CSS 值。星盘页与报告盘面把样式表里的height: auto写进了属性,浏览器丢弃属性并报警告。.personal-report-chart-svg与.chart-page-western-svg的样式表规则已经包含height: auto,属性是多余的。 - 修复:删掉这两个组件上的
height="auto"。viewBox与width="100%"不动,不新增 CSS。离线 Markdown 导出没有样式表,改为在序列化后的<svg上直接写入height="452",不再依赖替换height="auto"。 - 验证:
frontend/src不再出现height="auto"。chart-page-view.test.tsx锁定两份 SVG 源文件不含该属性,且.chart-page-western-svg仍有height: auto。离线导出抽查开头为<svg xmlns="http://www.w3.org/2000/svg" height="452" ...>,没有height="auto"。tsc --noEmit0 错。尚无 staging 控制台截图。数字见docs/tasks/PROGRESS-console-noise-20260916.md。 - 防复发:星盘 SVG 合同禁止再写
height="auto"。高度自适应只放在已有的 CSS 规则里;离线导出要长度时写在导出序列化,不写回组件属性。 - 相关记录:无。引入提交为星盘页
830799fa与报告盘面2fdcb14f,不是已编号缺陷的复发。 - 复发自:无
- 修复版本:本分支
codex/console-noise-20260916,待提交与部署核对。
BUG-907 | 没有后台咨询任务时,状态查询仍返回 404
- 状态:investigating(分支已修,定向回归见进度记录;未部署,不标 resolved)
- 首次发现:2026-09-16
- 最近更新:2026-09-25
- 影响面:
GET /api/consult/status无参查询,以及首页启动、页面重新可见、online时的fetchActiveConsultationStatus。 - 用户现象:没有后台任务时,控制台出现
GET /api/consult/status 404 (Not Found)。切回标签页会再出现一次。业务上没有失败。 - 触发条件:已登录且当前没有
reserved咨询行,客户端不带sessionId与requestId查询活动任务。 - 根因:无活动任务和「指定的 requestId 不存在」共用了同一个空结果 404。前者是常态空结果,浏览器仍把非 2xx 打成红字。带
sessionId+requestId的 404 仍是 BUG-276 刷新恢复的计数依据,不能改。 - 修复:
activeLookup查空返回204且无 body;带参查空仍返回 404「咨询请求不存在」,两支不再共用一个if (!data)。fetchActiveConsultationStatus在response.json()之前同时把 204 和 404 当成没有活动任务。带参的fetchConsultationStatus不改。 - 验证:
consultation-stream-recovery.test.ts原结构断言保留,并新增 204 / 404 分叉与无参客户端兼容断言。consultation-recovery.test.ts仍要求带参 404 计入缺失次数。tsc --noEmit0 错。尚无 staging Network 证据。数字见docs/tasks/PROGRESS-console-noise-20260916.md。 - 防复发:无参空结果不得再返回 404。带参「咨询请求不存在」必须保持 404。客户端读无参状态必须先看 204/404,不得先解析没有 body 的 204。
- 相关记录:BUG-276(带参 404 与刷新重放,本条不改那条语义)。控制台第三条
reportAllChanges读取startTime的报错是浏览器扩展注入脚本,非本仓代码,不加 web-vitals、不加 try/catch;验证方法是无痕窗口。 - 复发自:无
- 修复版本:本分支
codex/console-noise-20260916,待提交与部署核对。
BUG-1001 | 校正会话标题仍写日期,同一天多条无法区分
- 状态:resolved
- 首次发现:2026-09-22
- 最近更新:2026-09-25
- 影响面:侧栏校正会话标题、
resolveSessionTitle、agentic_rectification_cases的结果列、存量chat_sessions.title - 用户现象:副标题已经是最后活动时间,标题仍印日期,同一天多条校正完全同名;旧标题里的钟点是创建时间,和副标题对不上。
- 触发条件:侧栏列出多条生时校正;或校正结果(已确认、已采用、当前范围)变化后标题仍是日期。
- 根因:复发自 BUG-929。BUG-988 把副标题改成最后活动时间之后,标题继续承载日期,同一行两列是同一个事实,而且日期不够区分同日多条。BUG-929 当时决定旧标题不批量改。
- 修复:标题算式只有
public.rectification_session_title。已确认优先于已采用,写生时校正 · HH:MM;否则起止都通过agentic_rectification_is_clock时写生时校正 · HH:MM–HH:MM(U+2013);否则生时校正。触发器agentic_rectification_cases_title_from_result只挂在accepted_time、confirmed_time、candidate_range。回填与触发器共用rectification_session_title_is_automatic,只覆盖自动派生标题。不写updated_at,不改 pinned、messages。今日节奏仍带日期。已有会话的 open 仍不改 title / updatedAt。新建会话打开后读取库里的结果标题;keep_rectification_result_title阻止登录用户把自动标题写回去盖掉结果,手改的非自动标题仍可保存。 - 验证:
database-rectification-session-title-result.test.ts在本机真实 Postgres 通过:四分支、provolatile = i、采用分钟与区间收窄不改updated_at、手改「我的校正」不被覆盖、touch 触发器仍推进活动时间、从迁移文件切出的回填改写前五条、第六条不动、六行updated_at/pinned/messages逐字节不变、重跑 0 行。authenticated把结果标题改回「生时校正」不生效,改成「孩子校正」可以,且updated_at不变。跨层对断测试读迁移里的正则,与isAutoDerivedSessionTitle同组输入答案相同;把该正则的间隔号改成连字符后测试转红,已撤回。 - 防复发:标题算式只能有一处实现,运行时与回填共用。侧栏同一行的两列不得承载同一个事实。
- 相关记录:BUG-929、BUG-699、BUG-704、BUG-988、BUG-987、BUG-992、BUG-995
- 复发自:BUG-929(标题口径两次返工;「旧标题不批量改」被本单推翻)
- 修复版本:本分支提交,尚未推送、尚未部署
BUG-1023 | Chara 0 年大运把整张星盘收成算不出来
- 状态:resolved(定向回归已锁;浏览器与部署未验收,不把本分支写成已上线)
- 首次发现:2026-09-24
- 最近更新:2026-09-25
- 影响面:
chart-view-mapper.ts的 Chara 时间分配、星盘大运 Tab - 用户现象:大运右栏是「这张盘算不出来,我们已经记录下来了。」
- 触发条件:某段 Chara 大运
duration_years为 0。虚构 1985-08-03 03:59、北京可复现,射手段为 0 年。 - 根因:子运权重用
finite(duration) ?? 1,0 不会被换成 1。12 段子运权重都是 0 时总和为 0,0/0得到 NaN,new Date(NaN).toISOString()抛RangeError。assembleChartView把非 Zod 错误收成engine_bad_payload。0 是 kn_rao「先保底 12 年、再按落陷减 1」的合法结果,不是引擎坏包。 - 修复:总权重不是正数时不再构造非法日期。0 年段标注「0 年」,起止同一天,不展开子运,子运区写「本段时长为 0,不展开」。不改
scripts/jaimini.py。 - 验证:真实
_compute_chart+_compute_chara_dashagolden。12 段、射手 0 年、前后日期相接、assembleChartView({layers:["chara"]})为 ok。UI 不渲染该段子运。 - 防复发:0 权重不得再进入
Date#toISOString。0 年是显示规则,不是引擎失败。BUG-715 的失败分档仍留给真正的坏响应。 - 相关记录:BUG-715、BUG-1017
- 复发自:无
- 修复版本:本分支,待推送与部署核对
BUG-1024 | 星历切到新日期后下面仍是前一天
- 状态:resolved(定向回归已锁;浏览器与部署未验收,不把本分支写成已上线)
- 首次发现:2026-09-24
- 最近更新:2026-09-25
- 影响面:
ephemeris-page.tsx、/api/ephemeris - 用户现象:顶部日期已经换成新的一天,五要素和行运还是前一天。失败后一直留着旧数据,再点也不会重取。
- 触发条件:切到还没缓存的日期;或请求 429、非 JSON、坏包;或进页时「今天」的刷新在切日期期间回来。
- 根因:BUG-966 为了不让整页换成等待句,删掉了缓存未命中时的清空。失败时
load直接返回,effect 又只跟日期变,所以同一天不能重试。进页刷新只比较响应里的payload.date,切日期期间会把今天写回去。 - 修复:只有响应日期等于当前所选日期才算就绪,否则保留日期条,三段改静态句。失败句为「这一天的星历没取到,点日期再试一次。」再点日期或失败时的左右箭头会重试。今天的刷新和迟到响应都先看当前日期。本命盘按账户和出生资料缓存 5 分钟,引擎调用带上
request.signal。 - 验证:生命周期测试覆盖未缓存静态句、迟到的今天、迟到的前一日期、429 后箭头重试。路由测试里同一账户连续两天只打一次本命
/api/chart,并且上游 fetch 能看到已中止的 signal。 - 防复发:星历仍不得出现骨架、spinner 或「正在加载」。旧日期响应不得写回新日期。BUG-966 的测试只静态渲染了
EphemerisView,没有挂上切日期的状态机,所以没拦住这次副作用。 - 相关记录:BUG-966、BUG-1016
- 复发自:BUG-966
- 修复版本:本分支,待推送与部署核对
BUG-1025 | 星盘没有出生资料时只有一句话,没有去填写的入口
- 状态:resolved(定向回归已锁;浏览器与部署未验收,不把本分支写成已上线)
- 首次发现:2026-09-24
- 最近更新:2026-09-25
- 影响面:星盘失败页、
settings-url.ts、首页设置深链 - 用户现象:没有出生资料时只看到说明,不能从这一页去填。
- 触发条件:
birth_profile_incomplete,以及资料查询失败、已采用时间缺时区、时区解析失败。 - 根因:失败页只有一段文字。
parseSettingsQuery以前只认?settings=billing。 - 修复(历史):最初加标题「还没有星盘资料」、原句和主按钮「去填写出生资料」,链到
/?settings=chart,当时打开设置里的星盘资料。另外三种资料失败给同一按钮。2026-09-25 星盘档案上线后,当前入口改到/people,旧?settings=chart也转到/people;设置里的星盘资料分区已撤下。?settings=billing仍只开账户与点数。 - 验证(历史与现状):四种资料失败都渲染该按钮;引擎忙不渲染。原回归锁
?settings=chart的星盘资料解析;现行合同锁/people跳转,不再将旧解析称为当前行为。billing 的既有断言没改。 - 防复发:查询失败不得再写成资料不完整。这四类以外的失败不得带上「去填写出生资料」。
- 相关记录:BUG-996、BUG-715
- 复发自:无
- 修复版本:本分支,待推送与部署核对
BUG-1026 | 报告正文仍是审计版,投影把内部句截成半句
- 状态:resolved(Claude 2026-09-26:代码 09-25 已验收;
4d801e53已部署,/api/healthgitCommit一致;真机清单由产品走) - 09-25 修复单补充:
tableIsInternal把通用 Score 当内部键,adjacentInternalTableIndexes越过说明段继续删除标题;此前只锁章节关键词和泄漏零命中,没有检查真实十二宫表完整性。现报告表允许 Score / Weight(明确内部键仍拦截),删内部表不连标题;PL9 新规则只用于报告正文,聊天及安全图盘不受影响。真实引擎重算的虚构 reader-main golden 新增整段 Bhava Bala 原样保留断言,且原ordinaryOutputLeaks=[]不变。前端相关三文件 40/40;旧 density golden 逐行差异只有新增标题/空行,两个冻结 SHA 的原值/新值/原因见PROGRESS-report-reader-main-fix-20260925.md。本轮未提交、未推送,不提前 resolved。 - 首次发现 / 最近更新:2026-09-24 / 2026-09-25
- 影响面:新个人长报告正文、普通投影、旧报告下载后的可见正文。已生成的 Markdown 不回填。
- 用户现象:正文出现「结论等级规则」、英文 boundary、「producer failed」和异常原文、
full-report://、页码和「…所有行保持」这种半句,以及删表后留下的说明段。 - 触发条件:生成或打开个人长报告。正文走专业参考渲染;普通投影只拦聊天键,并在
parameter_sensitive处截断句子。 - 根因:本仓最后一次引擎同步
f2241463(2026-09-03)早于上游读者版f3b5196f(2026-09-09)。cfcd369d把参考/审计版设成正文。本仓又自行加入结论等级规则、英文 boundary,以及把 error/reason 打进不可用小节。投影的projectProseLine保留内部词之前的半句,tableIsInternal只删表。 - 修复:新报告请求
edition: "reader_main"。读者版从上游origin/main23b9609e(含f3b5196f)整段引入scripts/pl9_reader_export.py。缺省 edition 仍是参考版。参考版也去掉上述三处自加泄漏;数据不可用的节整节不输出。投影遇内部词删整行,删表时连同紧邻说明段。报告版本pl9_personal_long_report.v3。旧报告不迁移、不重算。 - 验证:虚构盘「虚构甲」1991-04-07 09:15 的真实引擎读者版
ordinaryOutputLeaks为 0,并含出生资料、Ashtakavarga、标准大运、KP、Year Lord、Tajika、Muntha。定向 Python 与前端投影测试见 PROGRESS。没有登录浏览器,未声称生成过真实用户报告。 - 防复发:读者版 golden 与
ordinaryOutputLeaks不得弱化;新增长报告请求体必须带edition: "reader_main"。旧report-density-fictional-reader.json继续锁旧报告。投影 sha 改动须写明原值锁的是泄漏输出。 - 相关记录:BUG-999、BUG-1003、BUG-1009、BUG-1020、BUG-1027、BUG-1028
- 复发自:BUG-999。当时的测试只覆盖聊天导出键(
technique_truth、workflow_route等)和一份已经泄漏的参考版 sha,没有读者版正文,也不认识 PL9 审计词;截断被 sha 锁成了正确输出。BUG-1003 只清附录,BUG-1009 只改事实表列名。 - 修复版本:本分支,未推 staging,未部署。
- sha 三栏:普通投影
01cc1e03c5adca2833e014156e1162fff12e5cb336e4484c5871859cd878acd7→f3aeb8f4b09ba0a78af4ab5e7aee4db32d7499b8e6b1488f2925b35fd91750b9;LF 下载3c52e8bfcfcb3ce3852bd5323867a69bc47d0f9aa821a34692172e7d61c6bcb8→b7f6947f6ae6f82e4a972be3c1f2bc50260ed0c5f029847264f40a5ee6ba7b88。原值锁的是截断后的泄漏正文。
BUG-1027 | KP 与三项 transit 因空实例字典拷贝失败
- 状态:resolved(Claude 2026-09-25 验收:
type('Args')参数下 KP 与三项 transit 均非 blocked,无SimpleNamespace(**vars(残留;已部署fabe0114,health 核对一致) - 首次发现 / 最近更新:2026-09-24 / 2026-09-25
- 影响面:网页全读路径里的 KP,以及 double transit、transit LL7L、行星聚集。CLI 的 argparse 实例不受影响。
- 用户现象:KP Lord / Sub 节为 blocked,原文是
'types.SimpleNamespace' object has no attribute 'year'。 - 触发条件:
_compute_full_reading_for_thematic用type('Args', (), {...})()把字段放在类上。cmd_kp和 transit 执行SimpleNamespace(**vars(args)),实例字典为空,随后读.year失败。 - 根因:
vars()不复制类属性。上游origin/main23b9609e的 API 同样用这种 Args,引擎同样用vars(),空结果时不把异常打进读者正文,所以缺陷一直潜伏。 - 修复:
_copy_call_namespace同时读取实例字典和类属性。不改岁差、宫制或 KP 算法。API 主文件未改,类方法数不增加。 - 验证:
tests/test_report_reader_main.py用同样的空实例 Args 调用cmd_full_reading,KP 与三项 transit 的 status 不是 blocked,文本里没有has no attribute。 - 防复发:该类 Args 的回归留在上述测试。不要再写
SimpleNamespace(**vars(args))。 - 相关记录:BUG-561、BUG-1026
- 复发自:无。这是网页参数构造和
vars()的潜伏缺陷,不是 BUG-561 的渲染分支。 - 修复版本:本分支,未推 staging,未部署。
BUG-1028 | 太阳返照漏了出生上升,年主恒为火星
- 状态:resolved(Claude 2026-09-26:代码 09-25 已验收;
4d801e53已部署,/api/healthgitCommit一致;真机清单由产品走) - 09-25 修复单补充:上轮用户可见结果未修:真实两侧年主相同,但 Tajika 没有
year_lord_sign,被当成冲突;读者表又从年主对象读取 Muntha、选取依据,未来年白名单漏solar_return。旧测试只查表头、或使用键集相同的合成字典,没守住可见单元格。现 D1 仅比较年主行星,D2 分别绑定真实字段、补保留未来返照结果;真实冲突仍同时显示两侧。参考提示按缺失/冲突/可读状态生成,不再恒说年主分歧。上游23b9609e的calc_panchadhikari_year_lord闭包依赖进程内 PyJHora,并会回退已 blocked 的代理算法,故按 D2 让步不引入、不解除边界。真实完整引擎三年测试与故障注入回归保留两侧真实值,不把本地一致性冒充外部验证。修复文件、测试及三栏见PROGRESS-report-reader-main-fix-20260925.md;本轮未提交、未推送。 - 首次发现 / 最近更新:2026-09-24 / 2026-09-25
- 影响面:Muntha、Year Lord、年度章里的冲突句。
- 用户现象:Muntha 为 blocked;Year Lord 恒为火星 / 白羊座;年度句出现「当前优先候选指向conflict between vs Capricorn」。
- 触发条件:
solar_return_full_report读取birth_asc_sign_idx,本仓calc_solar_return_chart的成功字典没有这个键。冲突门用整份字典比较,blocked 与正常值必判冲突;空的左侧对不上冲突正则,落到普通句。 - 根因:
f2241463同步上游时漏了scripts/solar_return.py里设置birth_asc_sign_idx的那一行。上游当前origin/main在返照字典里写入该键。本仓冲突门另外比了整份字典,上游已改成只比星座和主星,但仍会把 blocked 字典算进去;本单按 D4 在一侧status == blocked时不判冲突。 - 修复:成功的返照结果写回
birth_asc_sign_idx。Muntha 没有星座序号时 Year Lord 标 blocked,不再默认 0(火星)。冲突门跳过 blocked 一侧,只比较星座与主星。 - 验证:虚构盘 1991-04-07 的 2024/2026/2028 返照都有出生上升序号,Muntha 非 blocked,Year Lord 至少两年不同。一侧 blocked 的 Muntha 不再生成 conflict between。
- 防复发:
tests/test_report_reader_main.py锁住出生上升、两年不同的年主,以及 blocked 不冲突。不要在同步时只合一半太阳返照返回值。 - 相关记录:BUG-1026;同步提交
f2241463 - 复发自:无。这是同步漏合,不是旧 Muntha 测试覆盖过的路径。
- 修复版本:本分支,未推 staging,未部署。
BUG-1029 | 首页拆分后出生时间合同仍只扫 page.tsx
- 状态:resolved(定向回归已锁;部署待这次门禁)
- 首次发现 / 最近更新:2026-09-25
- 影响面:
tests/test_birth_time_journey_contract.py的首页引导扫描面 - 用户现象:无。门禁 run 2887 在
test_web_onboarding_uses_the_deterministic_free_journey失败,staging 停在已部署的9167d75e。 - 触发条件:首页拆分把引导屏 JSX 从
page.tsx搬到home-onboarding-shell.tsx后推 staging。 - 根因:合同把
<BirthTimeRectification锁在旧的首页文件清单里。拆分后组件还在,清单没跟上。 - 修复:扫描面加入
home-onboarding-shell.tsx,并要求page.tsx仍挂载<HomeOnboardingShell。其余断言不放宽。 - 验证:Python 合同在
81db05c6已过。run 2888 露出 10 条前端源码合同,已在3dd48c0d跟上。run 2889 只剩打字机合同:拼接面把引导屏的aria-busy切进了打字机片段。打字机合同改为只读onboarding-chat-message.tsx。 - 防复发:引导屏再搬家时,合同必须同时锁住挂载点和组件文件,不能只扫
page.tsx。 - 相关记录:TASK-home-page-split-20260925
- 复发自:无
- 修复版本:本分支,待推送
- 断言三栏:原值是扫描面不含引导屏组件、只要求其中出现
<BirthTimeRectification;新值是扫描面包含home-onboarding-shell.tsx,并额外要求page.tsx挂载<HomeOnboardingShell;原因是 JSX 搬家,行为没变,合同文件清单没跟上。
BUG-1030 | 「设为默认」把页面资料换成别人,合盘问题和校正标签因此错人
- 状态:investigating(activeChartId 清除与 P0 修复均已部署
4d801e53;等产品按docs/testing/people-archive-p1-20260924.md真机走查后关闭) - 首次发现:2026-09-24
- 最近更新:2026-09-25
- 影响面:首页
profile、合盘问题里的「我」、生时校正会话标签、设置里的「当前默认」、星盘/星历/报告/今日星语。 - 用户现象:把别人设为当前星盘后,合盘问题把别人写成「我」;校正会话标签显示别人的名字,计算仍用户主;切人或刷新时页面还可能先显示本人资料。
- 触发条件:旧
activeChartId/默认资料入口、当前人物切换期间的未就绪请求、目录或会话响应晚到。 - 根因:BUG-466 留下每日星语缓存和
activeChartId不动。BUG-997 / BUG-1000 只把普通对话接到服务端人物,并明确报告、每日星语、生时校正另立任务。页面级profile仍会被别人的资料覆盖,而星盘、报告和校正继续读profiles;新目录/Provider 时序还可能复用旧 ready 或把可恢复目录失败误判为未登录。 - 修复:删除
activeChartId、makeDefaultChart和替换页面profile的 effect。当前人物只作为请求参数;星盘、星历、报告、今日星语和普通对话都经resolveSubjectBirth取服务端资料,失败不回落本人、不扣点。账户/目录绑定 ready 前不发默认本人首屏请求,当前 generation gate、消息 metadata merge 和 scope 代数拦截迟到响应;生时校正在当前人物不是本人时禁用。户主仍在profiles。 - 验证:永久行为测试覆盖
?new=1当前人物、A→B 分页晚响应、Provider reload 等待、catalog503不登出、跨账户缓存;独立5项前端探针通过。没有登录浏览器,未声称真实页面验收。 - 防复发:任何客户端代码不得再用他人资料覆盖页面级
profile。出生资料只从resolveSubjectBirth读取。不得恢复「设为默认」或把activeChartId写回 localStorage;ready闸门、metadata合并和异步scope代数必须有行为回归,不只源码字符串。 - 相关记录:BUG-466、BUG-997、BUG-1000、BUG-1031、BUG-1032、BUG-1024
- 复发自:无。BUG-1000 当时规定新对话固定本人,本单按产品决定推翻那一条,不把页面资料再换成人。
- 修复版本:人物冻结工作树,未推 staging,未部署。
BUG-1031 | 他人报告后台取户主资料,删除级联后未交付预留可能漏退
- 状态:resolved(Claude 2026-09-26:代码 09-25 已验收;
4d801e53已部署,/api/healthgitCommit一致;真机清单由产品走) - 首次发现 / 最近更新:2026-09-25 / 2026-09-25
- 影响面:报告排队worker、服务端人物解析、本人校正metadata、删除人物RPC与报告删除触发器。
- 用户现象:选择他人生成的报告仍使用户主资料并扣点;删除排队/运行中人物时,报告与job已被级联删除,预留点数可能无后续worker可释放。
- 触发条件:报告POST进入排队成功路径;或未交付报告的所属人物被真实删除。本人报告另存在select投影未带校正case metadata而丢失候选范围的修复期回归。
- 根因:路由虽解析正确人物,worker仍只按job.userId直读profiles;旧测试只锁路由失败路径。单靠worker发现人物缺失再退款覆盖不了删除RPC先级联清除job的真实路径;mock整行profile也掩盖了self metadata漏列。
- 修复:worker按报告持久化chart_profile_id经loadSubjectBirth/resolveSubjectBirth取权威资料;他人不读profiles,本人单独补读校正case metadata、候选窗仅本人。缺失/外属人物失败不回落本人并沿既有退款路径。报告subjectId别名与chartProfileId先规范化、冲突拒绝,未知字段仍拒绝。
- 真实删除修复:新增迁移在报告删除事务内复用release_usage释放未交付预留;ready已交付与completed已结算不退,already-released幂等,其他退款失败回滚删除。删除RPC按job→report锁序与worker一致,避免完成/删除双连接死锁。
- 权限边界:新delete_chart_subject为SECURITY DEFINER,不是依赖调用者RLS。函数显式校验auth.uid非空,锁定同owner且role=other的档案,对session/report删除与job锁均owner过滤;本人/跨用户不可删。触发器函数撤销public/anon/authenticated直接执行权限,RPC撤销public/anon、只授authenticated;两函数search_path为空,不开放通用退款入口。
- 验证:永久worker成功路径注入fake engine、真实select投影,并覆盖缺失/外属失败;新增永久DB用例走真实authenticated删除,不只mock人物null。独立退款r4六场景通过:queued、running、completed、already-released、ready待结算、running完成与删除双连接并发。整体永久定向54/54属于Windows Node22局部,不是最终Linux全量。
- 防复发:报告路由成功不得替代worker真实路径;本人metadata按select列裁剪测试;删除退款必须经真实PG事务与并发,不以worker失败mock替代。不得扩大SECURITY DEFINER授权或删掉owner/role验证。
- 相关记录:BUG-1030、BUG-997、BUG-1000;任务TASK-people-archive-p1-fix-20260925。
- 复发自:BUG-1030同轮多人物接入覆盖遗漏;不是已完成报告重算/退款需求。
- 修复版本:人物冻结工作树,未推 staging,未部署。
BUG-1032 | 默认本人历史的OR is.null在自托管Postgres上500
- 状态:resolved(Claude 2026-09-26:代码 09-25 已验收;
4d801e53已部署,/api/healthgitCommit一致;真机清单由产品走) - 首次发现 / 最近更新:2026-09-25 / 2026-09-25
- 影响面:LocalPostgres兼容层parsePostgrestOr、GET /api/sessions默认subject=self、侧栏本人历史。
- 用户现象:本人侧栏显示「聊天记录暂时无法读取」,存量历史消失;他人会话筛选不走此OR分支。
- 触发条件:本人过滤
chart_profile_id.is.null与chart_profile_id.eq.self通过.or()提交给自托管客户端。 - 根因:OR解析器只认比较算子,不支持is,抛unsupported or filter后被路由转为500。BUG-990修复的是not(...,is,null),OR分支未共用完整能力;mock query builder不编译真实SQL,旧测试漏掉此差异。
- 修复:OR支持is.null/is.true/is.false,复用一元IS编译;SQL使用IS NULL/IS TRUE/IS FALSE,不写=null,也不把null/布尔字面量当字符串。本人路由仍查存量null/self,不改筛选语义。
- 验证:local-postgres-or永久测试补算子行为,database-chart-subject新增真实sessions GET+Postgres,核对self返回null/self且不返回他人。Linux中间标准DB63/63是中间快照,不能冒充最终组合验收;最终DB与全量由协调方重跑。
- 防复发:沿用BUG-990要求,兼容层新增算子必须真实Postgres覆盖,不能只mock链式调用或源码正则;OR与not一元IS语义均锁回归。
- 相关记录:BUG-990、BUG-926、BUG-1030。
- 复发自:BUG-990,同一兼容层的另一个is入口未覆盖;旧not-is测试仍保留,不把复发写成无关新问题。
- 修复版本:人物冻结工作树,未推 staging,未部署。
BUG-1033 | 西洋盘轴点不参与避让,移动端命中圆遮住相邻文字
- 状态:resolved(Claude 2026-09-26:代码 09-25 已验收;
4d801e53已部署,/api/healthgitCommit一致;真机清单由产品走) - 首次发现 / 最近更新:2026-09-25 / 2026-09-25
- 影响面:
western-wheel-layout.ts、western-wheel-svg.tsx,不涉及引擎和接口。 - 用户现象:拥挤盘行星压住 ASC/MC;窄屏文字和点击区过小。扩大命中圆后,一颗星可见文字的末尾可能选中邻星;窄宫宫号也会相碰。
- 触发条件:轴点附近星群、移动端343px实际盘宽、真实非等宫头,后绘制透明命中圆覆盖前一标签末尾。
- 根因:旧布局只松弛行星,不含固定轴点;17单位字号和r22命中圆未扣页面内边距;字框将本地符号字体当成1em,
middle基线不居中;扩大圆时仅保护标签中心,未覆盖每字符点击归属。宫号固定同半径不足以容纳窄宫。 - 修复:固定ASC/MC、星座名和宫号作为障碍,行星两环近邻搜索;19单位字号、r34命中圆、central基线与2.2em符号字框;先画所有命中按钮,再画可见文字,保持每星唯一Tab/读屏按钮;装饰线不接收指针。宫号保留真实宫中角度,仅拥挤时径向换层。真实原始引擎响应存为golden,映射留在测试内。
- 验证:Node22本地相关28项通过;固定种子512盘、真实引擎64盘所有标签两两检查无碰撞;Chrome实际字体129盘所有字框碰撞0,逐圆中心及逐字符hit-test错误0,实测字号12.53px、命中直径44.85px。最初真实字体碰撞与独立复核字符点击失败均留账于PROGRESS,未删失败样本或放宽1%阈值。
- 防复发:原三项测试及11°间距/宫中角度/骨架断言不降级;扫描锁真实字号、完整标签数、所有标签对、命中分层顺序及无重复键盘按钮;浏览器逐字符检查不能以圆心距离替代。
- 相关记录:BUG-906、BUG-1016、BUG-1017;BUG-1025旧入口文档本轮按历史与现状分开更正。
- 复发自:西洋盘改版的布局覆盖不足。旧单盘golden缺少轴点附近拥挤情形,估算字框未覆盖真实字体。
- 修复版本:
codex/chart-western-fix-20260925工作树,未提交、未推送、未部署。
BUG-1034 | 西洋盘标签为避让离开行星所在星座
- 状态:resolved(门禁 run 2904、迁移 run 2905、部署 run 2906 成功,
/api/healthgitCommit=e84d6eb8(含d08ffdc0),/login 200、匿名 /api/account 401;3000 张真实盘标签偏移全部 ≤30°,残留 0.73% 盘轻微重叠为已知 P3,产品未要求继续处理) - 首次发现 / 最近更新:2026-09-25 / 2026-09-25
- 影响面:
western-wheel-layout.ts、western-wheel-svg.tsx。不改黄经、宫头或接口。 - 用户现象:标签只写星座内度数。避让把标签推到另一个星座扇区后,读者会按标签位置读错星座。验收抽样里 48% 的盘有标签偏离行星超过 30°,最大 115°。
- 触发条件:
4d801e53起,两圈放不下时在 ±180° 内搜索位置,并用长引线连回行星。 - 根因:字形碰撞框、同环最小间距和命中圆中心距叠在一起,搜索又没有偏移上限。
- 修复:显示角与真实黄经相差不超过 30°,且必须落在本星座或相邻星座。两圈放不下时使用更内侧的第三圈。文字盒和命中圆中心仍要避开已占位置。相交的点击圆按最近圆心归属。
- 验证:Node 22 定向测试里,1990-01-01 夹具、64 张真实引擎虚构盘重叠为 0,且每张标签偏移 ≤30°、不越过相邻星座。固定种子分散盘重叠率 ≤1%。45° 内塞进 11 颗星的几何压力盘不再要求零重叠。
- 防复发:渲染结果上的
data-longitude/data-display-longitude被测试断言 30° 与相邻星座。真实引擎 64 盘仍要求重叠为 0。 - 相关记录:BUG-1033。
- 复发自:BUG-1033 的避让搜索没有偏移上限。
- 修复版本:
codex/round-0925-followup-20260925,未部署。
BUG-1035 | 删除当前人物的对话后打开另一个人的对话
- 状态:resolved(门禁 run 2904、迁移 run 2905、部署 run 2906 成功,
/api/healthgitCommit=e84d6eb8(含d08ffdc0),/login 200、匿名 /api/account 401) - 首次发现 / 最近更新:2026-09-25 / 2026-09-25
- 影响面:会话删除回退、首页当前会话推导、咨询保存失败回退、人物列表合并。
- 用户现象:当前看的是 B 时,删掉 B 的最后一条对话,会打开 A 的对话。
- 触发条件:内存里的会话列表按人保留、侧栏按人过滤,但回退选择取的是整份列表里第一条非校正会话。
- 根因:三处回退没有用
sessionMatchesSubject。列表合并只增改,服务端已删除的行会留在内存里。 - 修复:回退先按当前人物过滤。这个人没有其他会话时新建一段空对话。某个人的列表被完整刷新时,服务器不再返回的行从内存移除;未保存的空草稿和其他人的行保留。
- 验证:定向测试锁住回退过滤、首页第二条查找,以及完整刷新会丢掉该人物已不在返回里的有内容会话。
- 防复发:源码合同要求回退调用带
sessionMatchesSubject的过滤。 - 相关记录:BUG-1030。
- 修复版本:
codex/round-0925-followup-20260925,未部署。
BUG-1036 | 人物列表接口失败时整页聊天不可用
- 状态:resolved(门禁 run 2904、迁移 run 2905、部署 run 2906 成功,
/api/healthgitCommit=e84d6eb8(含d08ffdc0),/login 200、匿名 /api/account 401) - 首次发现 / 最近更新:2026-09-25 / 2026-09-25
- 影响面:首页会话加载、人物目录、顶栏切换器。
- 用户现象:
/api/chart-profiles失败后,首页进入可恢复错误屏,历史和聊天都打不开。同一次打开里/api/account被请求两次。 - 触发条件:会话列表先读账户,再串行读人物目录;人物目录失败被当成整个列表失败。
- 根因:
loadSubjectCatalog失败会抛出,会话列表把它写成session_list_unavailable。账户读取没有共享。 - 修复:人物目录失败时按本人加载历史和对话,不进错误屏。切换器写「暂时读不到其他人」,只列出本人。目录恢复后自动刷新。同一次加载里的账户请求共享同一个进行中的结果。
- 验证:会话列表生命周期测试里,人物目录 503 不再产生
session_list_unavailable,账户仍在,且不是登出。 - 防复发:该用例改为断言降级后的 boot,不再把目录故障当成整页失败。
- 相关记录:BUG-1032。
- 修复版本:
codex/round-0925-followup-20260925,未部署。
BUG-1037 | 年运命令里的年主辅助函数调用了不存在的名字
- 状态:待验收(死代码已删除,计算路径未改,未部署)
- 首次发现 / 最近更新:2026-09-25 / 2026-09-25
- 影响面:
scripts/jyotish_engine.py的cmd_tajika。年运命令的实际返回仍走calc_year_lord。 - 用户现象:没有用户可见变化。未定义的
calc_panchadhikari_year_lord包在从未被调用的_resolve_tajika_year_lord里,外面的except Exception会在一旦被接上时吞掉异常。 - 触发条件:该辅助函数没有调用点。基线即如此。
- 根因:上游辅助函数被嵌进来,但依赖的函数不在本仓,调用又被宽泛异常吞掉。
- 修复:删除这段没有调用点的辅助函数。
year_lord结果仍由calc_year_lord产生。 - 验证:源码中不再出现
calc_panchadhikari_year_lord与_resolve_tajika_year_lord。未改年运命令的返回字段。 - 防复发:BUG 记录写明这是死代码删除,不是年运算法变更。
- 相关记录:BUG-1028。
- 修复版本:
codex/round-0925-followup-20260925,未部署。
BUG-1038 | 回首页落到上一次生时校正:标题是旧校正、输入框「正在打开生时校正…」,中间却是新对话开场
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2911 成功、已部署,
/api/healthgitCommit=6a22626d;登录态真机清单 A1–A6 由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:首页启动落点(
home-bootstrap-run.tsresolveLanding、home-cloud-sync.tsresolveLookupBootstrap、chat-session-url.ts)、/people「和 TA 对话」、只读侧栏的次级页链接与账户页脚、首页内「新建对话」。 - 用户现象:产品 09-26 iPhone Safari:从星盘档案页回到首页后,标题栏是上一次的「生时校正 · 时段」,输入框占位「正在打开生时校正…」且发送禁用,页面中间却是新对话的开场问候和两个入口按钮,一直不打开。
- 触发条件(本机 Chrome 与真实 React 生命周期测试均复现):先打开一条生时校正会话 → 点侧栏次级页链接(星盘 / 星历 / 我的报告 / 星盘档案都会把当前
?c=存进 sessionStorage 的登录返回存根)→ ① 在/people让当前人物变成另一个人(选中他人后「看星盘」「和 TA 对话」或顶栏切人),再经不带新建意图的/回首页(只读侧栏账户页脚、刷新):画面与产品截图逐项一致,永久停住;② 在/people对本人点「和 TA 对话」:启动按存根落到旧校正并把?c=写回地址栏,后台自动打开旧校正,约 2 秒后从新对话翻回旧校正界面。侧栏「新建对话」(/?new=1)在本机浏览器 4 种变体(默认 / 慢网 / 慢打开 / 只有校正会话)和 lifecycle 测试的四个次级页 + 手机抽屉下都没有复现:新建分支本来就忽略并清掉存根。产品描述的入口与复现入口不同,需产品真机确认。 - 根因:跨页存活的是登录返回存根
sessionStorage["jyotisha.session-url-return"](侧栏次级页链接的leaveChat()→persistLoginSessionReturn()写入)。三处缺口:1)/people「和 TA 对话」用第二套意图?newChat=1,由首页里挂载的NewChatDeepLink在启动之后才解析,启动的 new-chat 分支从未生效,存根照常胜出(违反 BUG-1015 红线 3);2) 存根 id 不在当前人物已加载的列表里时,启动走 lookup,找到后返回urlAction: "keep"——这个语义只为地址栏已有?c=设计,存根来源时地址栏没有?c=,shouldAutoOpenRectificationSession永远不成立,校正会话成了活动会话却不打开,composerLocksAsRectification锁住输入框;存根也不区分人物,别人的会话照样被拿来落地;3) 首页内新建对话不清存根,之后回裸/仍可能落回旧会话。 - 修复:删除
NewChatDeepLink与?newChat,「和 TA 对话」改走newChatHref()(人物由 current-subject 存储带过去,启动的新建分支按当前人物建本地空咨询);bootstrapSelectionFromLookup增加来源参数:存根来源找到时用replace-selected(写回?c=,校正会话照常自动打开),存根会话不属于当前人物时(other-subject)落默认并清存根;resolveLanding把当前人物判定传给 lookup;startNewChat/openChatBoundToProfile清存根。?c=深链的keep、BUG-989 首问前不落库、BUG-705 lookup 均不变。 - 验证:新增
frontend/tests/new-chat-from-people-lifecycle.test.tsx(挂载真实Home+ 真实AppSidebar+ 真实PeoplePage,与(app)/layout.tsx同构,客户端导航换路由、文档加载重置模块状态保留 storage):四个次级页 + 手机抽屉的侧栏新建、「和 TA 对话」本人 / 他人、他人存根不落锁死页、同人存根带 URL 打开、首页内新建清存根,共 10 条;另加 2 条纯函数 / 源码合同。修复前在origin/staging上 4 条行为测试 + 2 条合同失败(锁死页那条正是占位「正在打开生时校正…」),修复后全绿。本地next start+ Chrome 151(CDP 拦截/api/*返回虚构数据)修复前后截图见docs/testing/new-chat-from-people-20260926-*.png。全量与失败名单对照见 PROGRESS。 - 防复发:lifecycle 测试锁「先开校正 → 客户端导航到次级页(写存根)→ 回首页」的组合;源码合同锁「只剩一套新建意图」。BUG-1015 当时的测试只执行 URL 纯函数和启动函数(参数优先级、reserved 恢复),没有真实组件生命周期,也没有把次级页链接写存根、第二套
?newChat入口、存根 lookup 分支连起来,所以没拦住。 - 相关记录:BUG-1015(同一新建意图链路)、BUG-705(lookup 分支)、BUG-989(首问前不落库)、BUG-1030 / BUG-1035(不打开别人的会话)。
- 复发自:BUG-1015 的新建意图链路,组合路径遗漏;不是同一根因。
- 修复版本:
0a8350cc,已在 staging,deploy-staging run 2913(6a22626d)部署(2026-09-26 对账)。
BUG-1039 | 星盘档案页 /people 没有任何样式
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2908、迁移 2909、部署 2910 成功,
/api/healthgitCommit=0ab061b9;登录态真机清单docs/testing/people-ephemeris-ui-20260926.md由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
/people星盘档案页(列表、详情、添加/编辑、删除确认)。同轮顺带改星历页可读性(非 Bug,见任务书 D5–D10)。 - 用户现象:iPhone Safari 上名字、「本人」、另一人的名字和时间挤在一行,「添加一个人」悬在中间,详情字段是裸文字。
- 触发条件:打开
/people,任何账户、任何宽度。 - 根因:
frontend/src/components/people/people-page.tsx用了people-archive、people-archive-list、people-archive-detail、people-archive-row等类,frontend/src/app/globals.css里没有一条对应规则。实现 BUG-1030 那一轮没写样式;验收只有源码断言和组件探针,没有登录态,这一页从未在真实浏览器里看过。 - 修复:按原型补齐
/people样式与布局:小于 1024px 列表 → 详情(同路由切视图,顶部「‹ 返回」,并压一条?person=历史记录让浏览器返回回到列表);1024px 及以上左列表右详情。列表行是首字头像 + 名字(本人加「本人」标签)+ 等宽一行「出生日期 · 排盘时间 · 城市」+「›」,当前人物用强调色边与浅底;底部虚线「+ 添加一个人」与「还可以保存 N 个人」。详情是头部 + 两格时间卡(有校正结果时排盘卡走成功色并标「已校正」)+ 出生日期与中文岁差名 + 三个快捷入口;本人底部灰字「本人不能删除」,他人的删除挪进编辑视图底部,删除确认与计数逻辑不变。头像颜色按 id 取元素色 token。 - 验证:
tests/people-archive-view.test.tsx(行结构、当前人物高亮、时间卡与已校正、本人无删除、删除只在编辑视图、组件里每个people-archive*类在globals.css都有规则、触控 44px、1023/1024 切换);本地next start+ 临时宿主页(虚构数据,未提交)在 Chrome 151 375 / 390 / 1280、亮 / 暗下截图,并用 CDP 验证列表 → 详情 → 浏览器返回 / 「‹ 返回」、无横向滚动、可见按钮均不低于 44px。 - 防复发:新增「组件用到的每个
people-archive*类都必须在globals.css有规则」的合同测试,这类「写了类没写样式」的回归会在测试里失败。验收流程缺口:无登录态页面只做源码断言等于没看过页面;本轮起此类页面至少用本地宿主页在真实浏览器里截一次图,写进 PROGRESS。 - 相关记录:BUG-1030(本页由其引入)。
- 修复版本:
2981da6f,已在 staging,deploy-staging run 2910(0ab061b9)部署(2026-09-26 对账)。
BUG-1040 | 从其他页面回首页每次都重放加载动画、要等好几秒
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2914、迁移 2915 成功并已部署,
/api/healthgitCommit=7475eb6e;登录态真机清单docs/testing/home-warm-return-20260926.md由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:首页启动(
app/(app)/page.tsx的hydrated/bootstrapPhase初值与启动 effect、lib/home-bootstrap-run.ts)、新增lib/home-warm-snapshot.ts/lib/home-warm-start.ts/lib/client-navigation-target.ts、AppLink、DailyStarlanguageBinder、useSessionManagement.ensureSessionMessages、useProfileOnboarding(初始资料、退出清快照)、SessionListProvider(401 / 换账户清快照)、redirectToLogin。 - 用户现象:产品 09-26 真机:第一次进首页有加载动画是预期;从星盘 / 星历 / 我的报告 / 星盘档案点「新建对话」回首页,每次都要再看一遍加载动画、等很久。
- 触发条件:同一次打开里,任何客户端导航回
/(侧栏新建对话、历史会话行、账户页脚、浏览器返回)。 - 根因:首页的「一次等待一次揭幕」按组件挂载计,不是按这次打开网页计。客户端导航会卸载重挂
Home,hydrated=false、bootstrapPhase="account"从头来:重新fetchModelCatalog+fetchActiveConsultationStatus、prepare 阶段再拉入口摘要与今日星语、揭幕预算最多 4 秒。本地 Chrome 基线实测(所有/api/*人为延迟 1.5 s):/chart→ 新建对话 5530 ms 才可交互且出现加载环。BUG-966 已给星盘 / 星历 / 报告做了模块级首屏缓存,首页没有同等机制。 - 修复:第一次冷启动成功后,把模型目录、校正入口摘要、会话分页游标、今日星语(按人物 + 出生资料指纹 + 日期键)写进模块级暖快照(按账户隔离、只存内存);账户、资料、会话列表本来就在布局层
SessionListProvider里跨页存活,不再复制。Home重挂时useState(() => …)初始化函数读快照:快照齐全 + 列表已就绪 + 资料完整 + 落点可在内存里算出时,hydrated/bootstrapPhase初值即就绪态,落点按冷启动同一套函数同步算出(resolveBootstrapSessionSelection/resolveStarterHomeLandingSessionId,?new=1建本地空对话、?c=保持、登录返回存根与其人物范围同 BUG-1038);启动 effect 改为提交落点后后台刷新模型目录(下线模型沿用原提示并 PATCH)、账户、后台回答恢复(与冷启动共用resolveReservedConsultation),入口摘要与今日星语走各自既有 effect,结果静默替换。缺任何一项、落点需要 lookup、或客户端导航目标未知一律回冷启动。Next 先渲染新页面、后在 insertion effect 里写地址栏,挂载时location还是来源页,所以AppLink/navigateAppPath在点击时记下目标 href(client-navigation-target.ts),首页只信 10 秒内的一次记录。账户变化、退出、任一 401(redirectToLogin、provider 401)清空快照。Home()的 useState 33 / useRef 37 不变,新增一个只写快照的 effect。 - 验证:新增
frontend/tests/home-warm-return-lifecycle.test.tsx(13 条,真实Home+AppSidebar+SessionListProvider,按 Next 的顺序先渲染、后在 insertion effect 写 URL):四个次级页 → 新建对话首帧无加载环、标题「新对话」、首帧提交前 0 个请求、所有接口挂起也不回加载态;报告页 → 历史会话;整页刷新仍放一次加载环;快照缺项回冷启动;模型下线提示 + PATCH;后台回答恢复(落到该会话 / 不抢新建对话);入口摘要静默替换;存根校正会话带 URL 打开;换账户清快照。关掉暖启动时其中 11 条失败;去掉目标记录时新建对话与历史会话两条失败。frontend/tests/home-warm-snapshot.test.ts(12 条纯函数 / 源码合同)。本地next start+ Chrome 151(CDP 拦截/api/*、全部延迟 1.5 s):/chart→ 新建对话 18–23 ms 可交互、0 次加载环、可交互前 0 个请求返回;/reports→ 历史会话 18–19 ms;整页刷新 22 ms 出加载环。全量 3953 条 / 61 失败,失败名单与基线 3928 / 61 逐条一致。 - 防复发:lifecycle 测试锁「客户端返回首帧即就绪、任何接口都不被等待」与「整页加载仍放一次」;测试 harness 按 Next 的「先渲染后写 URL」顺序导航,防止再出现只在测试里成立的 URL 读取。BUG-966 的缓存规则(模块级、组件卸载不丢)推广到首页。
- 相关记录:BUG-966(次级页模块缓存先例)、BUG-479(一次等待一次揭幕)、BUG-1021(不半揭幕,快照缺项回冷启动)、BUG-1038(新建意图与存根规则,暖路径沿用)、BUG-936(首屏兜底,
data-hydrated不变)。 - 复发自:无。
- 修复版本:
638b6a60,已在 staging,deploy-staging run 2916(7475eb6e)部署(2026-09-26 对账)。
BUG-1041 | 首页校正入口摘要解析读错键名:已校正账户仍显示首次文案、未完成的校正从不提示
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2917、迁移 2918 成功并已部署,
/api/healthgitCommit=62d4c9c4;真机清单docs/testing/starter-home-polish-20260926.md由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
frontend/src/lib/rectification-entry.tsentrySummaryFromResponse(首页page.tsx启动 effect 与useRectificationSurface.refreshRectificationEntrySummary共用),进而影响首页生时校正入口的可访问名(开始新的生时校正/再次校正)与入口下方提示。 - 用户现象:产品 09-26 真机截图:账户已经校正过,首页入口下方仍写首次文案「不确定出生时间时,用记得住的经历一步步缩小范围」;按钮可访问名也一直是「开始新的生时校正」,从未出现「再次校正」。
- 触发条件:任何有未完成或已完成校正 Case 的账户打开首页。
- 根因:
GET /api/rectification/cases/entry-summary返回getRectificationEntrySummary投影后的 camelCase 对象(hasResumableCase/hasTerminalCaseWithTime/latestTerminal.caseId…),只有底下的 RPC 行是 snake_case;而浏览器端entrySummaryFromResponse只读 snake_case 键(has_resumable_case等),所以每一个真实响应都被解析成「什么都没有」。resolveRectificationEntryAction恒为start,rectificationCardAction === "restart"与hasResumableCase分支从未命中。自 2026-08-11ca748466(BUG-163 入口改造)起存在。测试没拦住:rectification-v9-entry-routing.test.ts、home-warm-return-lifecycle.test.tsx、new-chat-from-people-lifecycle.test.tsx的 entry-summary mock 都是手写的 snake_case,不是路由真实输出;rectification-v9-case-service.test.ts只测到服务端投影为止,两段之间没有往返合同。 - 修复:
entrySummaryFromResponse以路由真实的 camelCase 形状为准,同时仍读 snake_case(RPC 形状不会再静默塌成空)。首页小字按 TASK-starter-home-polish-20260926 D2 改为只读 summary 的hasResumableCase与当前人物,不再依赖派生的restart。 - 验证:
frontend/tests/starter-home-polish-20260926.test.tsx「BUG-1041: the homepage parser reads what the entry-summary route actually returns」:用getRectificationEntrySummary(假 RPC)产出、JSON往返后交给entrySummaryFromResponse,断言hasTerminalCaseWithTime、latestTerminal、restart与未完成提示;修复前该条失败(解析结果全 false)。本地next start+ Chrome 151,/api/*以 camelCase 形状回放:已校正账户可访问名「再次校正」、无小字;未完成账户显示「上次那次校正还没完成,可以在历史对话里接着做。」。全量 3960 条 / 61 失败,失败名单与基线逐条一致。 - 防复发:往返合同测试锁住「路由输出 → 首页解析」一段;新的首页测试 fixture 用路由真实的 camelCase 形状。旧的 snake_case mock 仍能被解析,未改动。
- 相关记录:BUG-163(入口摘要由服务端 Case 驱动)、BUG-1040(暖快照缓存的正是解析后的 summary,快照结构不变)。
- 复发自:无。
- 修复版本:
b5a02ca8+f04da103,已在 staging,deploy-staging run 2919(62d4c9c4)部署(2026-09-26 对账)。
BUG-1042 | 最后一轮回答与点赞 / 踩 / 复制 / 重试之间空出大半屏
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2920 成功并已部署,
/api/healthgitCommit=509987b9;真机清单docs/testing/latest-turn-actions-gap-20260926.md由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
frontend/src/app/globals.css末轮留白规则;普通咨询.message-entry(chat-transcript.tsxMessageEntry)与生时校正.rectification-message-wrap(rectification-agentic-chat.tsx+rectification-message-entry.tsx)两处最后一轮。 - 用户现象:产品 09-26 iPhone Safari 生时校正会话:助手正文结束后,点赞 / 踩 / 复制 / 重试一排被推到屏幕下部,中间空出大半屏。普通咨询同样存在,回答越短空白越大。
- 触发条件:本轮经
pinLatestTurn钉顶(发问、校正点选项 / 开场)后,最后一轮回答短于一屏。 - 根因:BUG-930 的钉顶留白写成
.conversation .message-list > :last-child .message-assistant { min-height: calc(var(--conversation-viewport) - var(--latest-turn-head-height)) },加在助手行本身;而.message-actions(以及普通咨询的追问建议、校正的交付卡)是助手行之后、同一个:last-child容器里的兄弟节点,于是被顶到留白之后。实测(无头 Chrome 151,虚构数据)正文末行到操作行的距离:普通咨询 375px 526px / 1280px 608px;生时校正 375px 452px / 1280px 554px。 - 为何 BUG-930 验收没发现:当时的 Chrome 骨架页只验证「本轮开头钉在视口顶部」(S1–S4),骨架里没有操作按钮行,也没人看按钮落在哪里;合同测试只锁选择器字符串,不看操作行的位置。
- 修复:留白改加在整轮外层
.conversation .message-list > :last-child:has(.message-assistant),计算式与两个 CSS 变量口径不变(useConversationScrollAnchor逻辑未改,只更新了turnHeadHeightForSpacer的注释);外加align-content: start,防止生时校正外层(display: grid)把多出来的高度分摊到各行、把间距又撑开。头行与末行同一元素时(BUG-931)仍写0px:外层从这条助手行开始,最小高度等于整个视口正好把它留在顶部。 - 验证:
frontend/tests/chat-notice-and-scroll-contract.test.ts「turn spacer uses conversation viewport variables and is not sticky」(选择器三栏改写 + 断言align-content: start)与新增「the turn spacer trails the actions row…(BUG-1042)」(任何规则不得把--conversation-viewport写在.message-assistant上;两处 DOM 中操作行与助手行同在末轮外层且在其后)。本地next build --webpack+next start+ Chrome 151 CDP、/api/*虚构数据:短回答正文末行到操作行 23px(即.message-actions自身间距),375 / 1280、普通咨询 / 生时校正四种组合一致;本轮开头仍停在滚动容器顶部下方约 10px;普通咨询长回答「跳到最新」出现、点击后落底并继续跟随。截图docs/testing/latest-turn-actions-gap-20260926/。全量 3961 条 / 61 失败,失败名单与基线逐条一致(新增 1 条通过)。 - 防复发:留白只加在末轮外层;新增合同测试禁止把视口留白写回助手行。改末轮 DOM(给外层加兄弟、把操作行挪出外层)必须同时看操作行位置。
- 相关记录:BUG-930(钉顶留白来源)、BUG-931(头行即末行时写 0)、BUG-932(闲置不写留白);验证中另查出 BUG-1043、BUG-1044(均为既有问题,与本次改动无关,前后表现一致)。
- 复发自:无(BUG-930 的遗漏)。
- 修复版本:
83cce7ad,已在 staging,deploy-staging run 2922(509987b9)部署(2026-09-26 对账)。
BUG-1043 | 直接打开普通咨询会话后,滚动锚的监听从未挂上
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2925、迁移 2926 成功并已部署,
/api/healthgitCommit=f1d16405;真机清单由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
frontend/src/hooks/use-conversation-scroll-anchor.ts的挂载逻辑(scroll 监听、ResizeObserver 跟随);首页page.tsx的加载闸门之后才挂载的.conversation。生时校正面不受影响(组件在揭幕后才挂载,容器从首帧就在)。 - 用户现象:刷新页面、经
?c=深链或启动落点直接进入一个已有的普通咨询会话后:没有落到最新一条(停在历史中段);发一个短问题,回答很短也会出现「跳到最新」;上下滚动该按钮不消失;点它之后回答继续变长也不会跟随。 - 触发条件:首页揭幕时当前会话就是一条有消息的普通咨询(不是揭幕后再切换过去)。先开
/?new=1再点侧栏进入同一会话则正常。 - 根因(已证实):hook 的两个 effect 依赖
[active, container, resetKey]。加载闸门期间page.tsx早退渲染.app-loading,.conversation未挂载,两个 effect 因container.current为空直接返回;揭幕那次提交里active(!rectificationSurfaceOpen && !starterHomeVisible)与resetKey(activeSessionId)都没变,container是同一个 ref 对象,effect 不重跑,于是 scroll 监听、ResizeObserver 与「打开会话先落底」三件事都没发生。pinLatestTurn走 rAF 不依赖 effect,所以钉顶本身正常,但latestBelowFold停在钉顶那一刻(平滑滚动尚未开始时)算出的值。证据:新增的真实 React 生命周期测试(先渲染加载屏再揭幕)中 3 条 BUG-1043 用例对改前代码全部失败(scroll 监听 0、点「跳到最新」后不跟随、容器重挂载后旧元素仍挂着监听);无头 Chrome 插桩改前.conversationscroll 监听 0、ResizeObserver 观察 0、打开后距底 1819px。 - 修复:挂载改由「滚动容器元素本身」驱动:一个每次提交后运行的 effect 比较
container.current、active、resetKey与上次挂载时的记录,三者都没变就什么也不做;元素出现、换了、active翻转或换会话时先卸下旧的,再对新元素挂 scroll 监听 + ResizeObserver/MutationObserver;卸载时另一个空依赖 effect 负责清理。调用处与 hook 签名不变(page.tsx、rectification-agentic-chat.tsx未改)。选这一路而不是 callback ref,是因为两个调用处都把同一个RefObject另作他用,改 callback ref 必须改校正面组件(本轮并行任务在改);也不用「effect 里 setState 记录元素」,那会触发 react-hooks 编译器规则。 - 验证:
frontend/tests/conversation-scroll-anchor-lifecycle.test.tsx(createRoot + act 真实生命周期,宿主节点只伪造几何与事件):「加载屏 → 揭幕」后 scroll 监听 1 个、ResizeObserver 有观察、落到最新、重复渲染不重复挂载、短回答平滑滚动落定后不显示「跳到最新」;长回答显示按钮、点击后继续跟随;同一会话下容器重挂载会迁到新元素、旧元素监听被卸下;active关开会卸下 / 重挂。无头 Chrome 151 + 生产构建 + 虚构数据(?c=直接打开、打开后刷新、先?new=1再点侧栏三种进入方式):改后 scroll 监听 1、观察 1、打开后距底 0;短回答「跳到最新」不出现(改前出现);长回答按钮出现,点击后落底,再插入 400px 仍距底 0(改前 400)。全量前端测试失败名单与基线逐条一致。截图docs/testing/scroll-anchor-hook-fixes-20260926/consult-reload-short-375-{before,after}.png。 - 防复发:hook 不得再用只依赖
active/resetKey的 effect 挂监听;生命周期测试锁住「加载屏 → 揭幕」路径。 - 相关记录:BUG-930、BUG-932、BUG-1042、BUG-1044。
- 复发自:无。
- 修复版本:
da2613ff,已在 staging,deploy-staging run 2927(f1d16405)部署(2026-09-26 对账)。
BUG-1044 | 生时校正长回答钉顶后又被拉到底部
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2925、迁移 2926 成功并已部署,
/api/healthgitCommit=f1d16405;真机清单由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
useConversationScrollAnchor的holdUnpin解除条件;生时校正面(钉顶后静止距底 94px)。普通咨询静止距底约 198px,未触发,但同一逻辑也适用。 - 用户现象:生时校正里发一句话,回答超过一屏时,本轮开头先被钉到顶部,回答一到视口就跳到最后一个字(BUG-930 想解决的现象)。
- 根因(已证实):钉顶后
holdUnpin为真,measure()在任何 scroll 事件里若distanceFromBottom ≤ 96就当作「读者回到底部」,解除holdUnpin并把anchored翻回真,随后 ResizeObserver 贴底跟随。钉顶平滑滚动自己产生的 scroll 事件落定时,校正面距底恰为 94(全视口留白 +.message-list底部的跳到最新让位 56+12px − 钉顶间距),命中阈值。另一条同类路径:钉顶后任何向上的非用户滚动(内容收缩夹紧)也会解除holdUnpin,再由几何距离翻回贴底。 - 修复:钉顶后只有读者自己的滚动能解除钉住:hook 在滚动容器上记
wheel、touchmove、按在容器本身(滚动条)的pointerdown,在 window 上记文本框 / 按钮之外的滚动键(方向、翻页、Home/End、空格),measure()仅当距上次这类手势 1 秒内(conversationGestureWindowMs)才解除holdUnpin,之后照旧:回到底部 96px 内恢复跟随,上滑则停在读者的位置。pinLatestTurn在钉顶时清掉手势时间戳,发问那一下点按 / 回车不会解除自己的钉顶。消息里的按钮点按(复制、展开思考)不算。阈值 96 与留白口径都不改。 - 验证:
conversation-scroll-anchor-lifecycle.test.tsx:静止距底 94 且收到无手势 scroll 事件后回答撑长,仍钉在开头、latestBelowFold为真(改前被拉到底);滚轮滚到底后恢复跟随;触摸上拉解除钉住但不跟随(BUG-930 语义);点按消息内按钮不算手势;滚动键过滤单测。无头 Chrome 151:375 宽改前开头位置 −1323px(落底),改后 10px 且 2 秒后仍是 10px、再插入 400px 仍是 10px(「跳到最新」出现);随后真实滚轮(CDPmouseWheel)滚到底 → 距底 0,再插入 400px 仍距底 0(恢复跟随);触摸拖动(CDPdispatchTouchEvent)同样到底后跟随。1280 宽改前 −482px,改后 10px,同样通过。截图docs/testing/scroll-anchor-hook-fixes-20260926/rectification-long-375-{before,after}.png。 - 防复发:钉顶的解除只能来自用户手势;生命周期测试用 94px 静止距离锁住。
- 相关记录:BUG-930、BUG-931(同症状、留白坍塌触发)、BUG-1042、BUG-1043。
- 复发自:无(BUG-931 同症状、不同触发条件)。
- 修复版本:
da2613ff,已在 staging,deploy-staging run 2927(f1d16405)部署(2026-09-26 对账)。
BUG-1045 | 生时校正打字回答后,同一轮里下一题出现两次(刷新后只剩一遍)
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2925、迁移 2926 成功并已部署,
/api/healthgitCommit=f1d16405;真机清单由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
frontend/src/lib/rectification-snapshot-messages.tsmergeTurnQuestions(rectification-agentic-chat.tsx的send/submitStructuredChoice结算后快照合并);服务端answer-choice.tspersistApplied/persistCollectDenialTurn的确定性回复正文(本轮不改)。 - 用户现象:产品 09-26 staging 真机:打字回答一道待答选择题后,助手这一轮正文是「已记录,范围没变;…领先,…落后。」+ 下一题题干,下面又是同一句加粗题干和四个选项;刷新后只剩一遍。
- 触发条件:确定性回复路径(打字回答命中待答选择题
action=message+ choice 分支;或采集拒答),且下一题是可渲染选择题。两条路径都不经模型 runner,BUG-585 在agent-run.ts的stripQuestionSentences不执行。 - 根因:BUG-969 修复第③项(
824ecff0)为防「卡片没挂上时看不到题」,让persistApplied/persistCollectDenialTurn用composeCollectSpokenAssistantText把题干拼进assistant_message并作为一个answer.delta下发,声称「挂卡后去重」。去重只存在于 GET 组装的attachQuestionsToTurns;客户端结算后保留流式原文,mergeTurnQuestions只拷question/candidateOffer、不动text,rectification-message-entry先画正文再画问题块 → 两遍;刷新走 GET 才去重,所以刷新后消失。 - 复发自:BUG-585(修复
9d1c08ca,防复发写明「不得恢复把题干拼进assistant_message」)。BUG-969 绕过了这条:它有意恢复拼接,用「挂卡后去重」代替原防线,但只在刷新路径实现了去重。为什么测试没拦住:account-dialog-inert-20260918.test.ts的 BUG-969 用例只断言persistCollectDenialTurn流文本与attachQuestionsToTurns(刷新路径)结果,没有走「流式结算 → 快照合并」的实时路径;同一提交把rectification-answer-choice.test.ts的断言从「正文不含题干」改成「正文含题干」(三栏注释写了原因,但没有对应的客户端去重断言);BUG-585 的防复发只锁在模型 runner 的agent-run.ts,确定性回复路径没有源码合同。 - 修复:D1(产品授权保留 BUG-969 的服务端兜底文本):
mergeTurnQuestions在快照回合带问题时,对消息正文执行与 GET 相同的stripQuestionSentences(text, question.prompt),幂等;实时对话与刷新读到同一段正文。服务端文本不改。 - 验证:
frontend/tests/rectification-dup-question-20260926.test.tsx挂载真实RectificationAgenticChat、假fetch走实时路径:打字回答命中选择题(流 =composeCollectSpokenAssistantText(ack, stem))与采集拒答(流 = 真实persistCollectDenialTurn输出)两条,各断言可见文本里题干只出现 1 次、4 个选项可点;修复前两条都是 2 次。浏览器同场景:修复前可见题干 2 次(A-before-1280.png),修复后 1 次(A-after-1280.png)。rectification-answer-choice.test.ts、account-dialog-inert-20260918.test.ts在服务端断言后追加客户端合并断言。浏览器:docs/testing/rectification-dup-question-20260926/。 - 防复发:确定性回复若把题干拼进正文,客户端结算后的快照合并必须去掉同一句——回归测试走「流式 → 结算 → 合并」而不是只测 GET 组装。任何改「题干是否进正文」的提交必须同时有实时路径断言。
- 相关记录:BUG-585、BUG-969、BUG-488、BUG-525。
- 修复版本:
e4c1c7a3,已在 staging,deploy-staging run 2927(f1d16405)部署(2026-09-26 对账)。
BUG-1046 | 点选一道选择题后,同一道题又完整画了一张(未选中)
- 状态:resolved(Claude 2026-09-26 独立复验通过;门禁 run 2925、迁移 2926 成功并已部署,
/api/healthgitCommit=f1d16405;真机清单由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
rectification-agentic-chat.tsxsubmitStructuredChoice失败分支、send失败分支、persisted_question兜底块;rectification-snapshot-messages.tsappendUnseenAssistantTurns/placePersistedQuestion。 - 用户现象:产品 09-26 staging 真机:第 N 轮点选「B 发生过但程度较弱」(高亮),下面是操作按钮、「目前范围 14:35-15:05,还在核对」,再下面又出现完全相同的题干和四个选项(未选中)。
- 触发条件与根因:
- H1(虚构数据已复现):
submitStructuredChoice先markQuestionAnswered本地标 B 再 POST;!response.ok/catch只移除占位、choiceNonce++、setError,不撤回已答标记、不重读快照。最新助手消息的题变成「已答」→liveQuestionOnMessages为假;快照里的current_question仍是这道题 → 缺口判定persisted_question→ 独立问题块再画一张可点卡。409stale_question与网络错误两种失败在修复前都得到「题干 2 次、两张卡」。打字回答失败(send的!response.ok/ 网络错误)同型:typed标记也不撤回。 - H2(虚构数据已复现机制):点选成功、
willContinue分支只跑mergeTurnQuestions,不跑appendUnseenAssistantTurns(BUG-685 在requestTieBreak修过同型缺口);send()结算后同样只合并。服务端另写的、承载下一题的回合进不了对话区,兜底块替它把题画出来。若题目完全相同还意味着选题重复:BUG-540(pickProbe先probe_id→semantic_key)、BUG-559 / 592(sameYearProbeAsked、same_year_asked硬排除)守卫已核对仍在代码里;本单未改选题。 - 兜底块条件过宽:BUG-678 的意图是「只在没有助手消息时兜底」,但闸门没强制;BUG-635 又规定旧消息上的同
focus_id不算仍在显示,于是旧消息一张 + 兜底一张。
- H1(虚构数据已复现):
- 修复:D2:选择题提交失败撤回本次本地标记(题回到 active、无
answer_option),卡片 key 随之变化重新挂载,重读快照(applySnapshotTurnsToMessages),错误行统一「这次没提交上,请再点一次。」(VOICE 已加);打字回答失败同样撤回typed标记。D3:placePersistedQuestion把快照当前题挂到最新一条已结算助手消息上,并摘掉其它消息上同focus_id的副本;独立问题块只在没有助手消息、或最新助手消息已挂着另一道题(不同 focus,不会重复)时出现;缺口状态机本身不改。D4:willContinue分支在占位行之前并入未见回合,send()成功结算后改用applySnapshotTurnsToMessages。 - 验证:
frontend/tests/rectification-dup-question-20260926.test.tsx:409stale_question与网络错误各一条(一张卡、0 个已选、4 个可点、错误文案、快照重读、可再次提交);打字失败一条;旧消息同焦点挪到最新消息一条;无助手消息时兜底一次一条;willContinue未见回合并入且位于跟进行之前、send()结算并入额外回合且不重复各一条。本文件 10 条在origin/staging(509987b9)代码上 9 条失败(「无助手消息兜底」一条本来就对)。浏览器(next start+ Chrome 151 无头,CDP 回放虚构接口):点 B、POST 回 409stale_question,修复前可见题干 2 次、2 张卡、B 仍高亮、兜底块 1 个、错误行是服务端原句——与真机截图同形(B-before-1280.png);修复后 1 张卡、0 个已选、4 个可点、错误行「这次没提交上,请再点一次。」(B-after-1280.png)。 - 防复发:任何先改本地状态再发请求的动作,失败分支必须撤回并重读;同一
focus_id在对话区只能画一张卡;新写回合的前端动作一律并入对话区(BUG-685)。 - 相关记录:BUG-675、BUG-678、BUG-685、BUG-635、BUG-917、BUG-540、BUG-559、BUG-592。
- 复发自:BUG-678(兜底块「只在没有助手消息时」没有闸门);D4 同型于 BUG-685。
- 修复版本:
e4c1c7a3,已在 staging,deploy-staging run 2927(f1d16405)部署(2026-09-26 对账)。
BUG-1047 | 生时校正打字回答后要等很久才有动静:开流前无反馈、分类无超时、线上看不出时间花在哪
- 状态:resolved(Claude 2026-09-26 用 Node 22 独立复验通过;门禁 run 2928、迁移 2929 成功并已部署,
/api/healthgitCommit=8a409434;真实各段耗时待用新埋点日志复核,真机清单由产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
POST /api/rectification/agent(route.ts的 message 分支)、turn-intent-classifier.tsclassifyTurnIntentWithRetry、agent-run.tsstreamAttempt的RectificationRunDiagnostic、turn-exit.tsfinalizeSuccessfulTurnExit、engine-client.tsreadV9EngineScoringIdentity、校正面rectification-agentic-chat.tsx的 live 行。 - 用户现象:产品 09-26 staging 真机:打字回答后,活动区一直是「正在分析 / 正在处理…」,要等很久(估计 25–90 s)才出正文,期间没有任何变化。
- 触发条件:
action=message。一轮串行 5 次开思考的模型调用:开流前的意图分类(classifyTurnIntentWithRetry,每次尝试无超时、最多 2 次;discriminator 分支之后还会再分类一次)+ Agent 四步。分类在new ReadableStream之前,所以分类期间浏览器收不到任何字节。 - 根因:串行多步 + 每步深度思考 + 开流前无超时空等 + 无进度反馈;埋点里
inputTokens/reasoningTokens恒为 null、stepCount实为"用过几种工具"、分类耗时只在失败时记录,线上无法核实各段耗时。收尾persistNextInterviewIfIdle在agent-run.ts与路由出口各调一次;/v5/versions每轮被读多次。不是回归:09-15 性能审计(BUG-721~726)处理的是引擎与档案读取,没有覆盖模型链路与开流顺序。 - 修复(产品 09-26 决策 D1–D5):D1 收尾两步(set-focus 改写与最终正文)不动。D2 分类保持会话模型与思考,只给每次尝试加 10 s 上限(
RECTIFICATION_CLASSIFIER_ATTEMPT_TIMEOUT_MS),超时走既有重试 →classifier_unavailable路径。D3 message 先建流,第一行推turn.progress received(「收到,正在对照你的档案…」,客户端发送同一帧就显示),分类与确定性回复都在流内;写经历 →「正在记下这件事…」、引擎重算 →「正在重新对照盘面…」、经历写完 / 定下一问 →「正在准备下一个问题…」,由既有工具事件与引擎调用推动(AsyncLocalStorage 回合作用域),只往前走;进度句只在 live 行,不进assistant_message、不进 phase 回执。流内的预检拒绝改为turn.rejected(原状态码、code、文案),客户端按原非 2xx 处理(撤回打字标记、402 / 资料不完整分支不变)。D4RectificationRunDiagnostic增加每步起止、供应商给出的 token(含推理)、分类耗时(成功也记,另有RectificationClassifierDiagnostic)、每次引擎调用路径 / 耗时 / 结果;确定性回复另记RectificationTurnDiagnostic;均不含用户原文、出生资料或模型文本。D5/v5/versions进程内 30 s 记忆(只存完整身份、按 fetch 实现 + 引擎地址分域);Agent 自己的收尾调用已发现下一题处于 active 时,路由出口跳过第二次persistNextInterviewIfIdle(第二次只会重读同一焦点并重复同一幂等链接)。 - 未做:D5 第一项「refresh 分支探针只算一遍」。两遍用的候选集不同(refresh 列 / 全网格),合成一遍会改变
candidate_contrast_opportunities输出,违反"逐位不变";改为复用候选静态上下文里已算好的 Vimshottari / Narayana 表的替代方案同机 A/B 7 个场景逐字节一致、refresh 61 候选 2.33 → 1.84 s,但event_probes.py属于冻结评分身份(scripts/research/sealed_holdout_rerun.pyPRODUCTION_FILES),改动会让校正验证完整性门禁 4 条失败,需按研究协议重新冻结 sealed holdout / reported offset 记录,本单未提交该改动(补丁与 A/B 脚本在进度记录列明)。 - 验证:
frontend/tests/rectification-latency-20260926.test.ts(17 条:挂起分类 10 s 中止、只重试一次、结果classifier_unavailable;首试超时次试成功;成功耗时日志不含原文;阶段只前进、ReadableStreamstart 继承作用域;turn.progress/turn.rejected过白名单;工具事件映射;Agent 一轮的阶段序列、完整诊断、进度句不落库;/v5/versionsTTL 内一次、失败与残缺不记忆;第二次空闲调用是纯重复、出口跳过后写操作集合不变)、rectification-latency-20260926.test.tsx(真实组件:发送后服务端零字节时已显示第一句、按turn.progress换句、工具名与「正在分析…」不再顶替、结算后无进度句;turn.rejected与旧 HTTP 拒绝同效)、rectification-latency-route-20260926.test.ts(真实路由 + 模块替身:分类未返回时首字节即turn.progress,8 ms;回复在出口门之后;预检 409 走turn.rejected;Node 22 通过,Node 20 为既有mock.module环境缺口)。浏览器(next start+ Chrome 151,CDP 虚构接口 + 本地延迟流):第一句在回车后 13–17 ms 可见,阶段切换与服务端事件同步(±35 ms),全程未出现「正在处理… / 正在分析…」live 行,结算后无进度句;截图docs/testing/rectification-latency-20260926/。 - 防复发:会产生模型或引擎等待的请求必须先建流、首字节是确定性进度;开流前只做鉴权与绑定校验。任何模型调用都要有单次上限。耗时埋点必须覆盖分类、每步与引擎调用,且不得含用户原文。改冻结评分文件(
PRODUCTION_FILES与 datasetfrozen_scoring.files)的性能优化,即便输出不变,也要把重新冻结验证记录列入任务书。 - 相关记录:BUG-721、BUG-722、BUG-723、BUG-724、BUG-725、BUG-726、BUG-388、BUG-1046。
- 复发自:无。
- 修复版本:
ab0f01a9,已在 staging,deploy-staging run 2930(8a409434)部署(2026-09-26 对账)。
BUG-1048 | 出题闸门 _boundary_windows 两处绕过:跨 1 月 1 日的边界不卡最小间隔,列表错位时比的是不对应的边界
- 状态:investigating(离线研究确认了代码行为,未确认对真人作答的影响;未改代码)
- 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
scripts/rectification/event_probes.py_boundary_windows,经_union_boundary_dates影响所有带日期的自动点选题(source = dasha_boundary),初始与刷新两阶段;Vimshottari 与 Narayana 两条轨道都走这个函数。event_probes.py属于冻结评分文件(ERR-110)。 - 用户现象:无直接用户报告。离线研究(公开 AA 开放集 v4 与虚构时刻)发现,文档所说的「候选间大运边界差 < 45 天(刷新 30 天)不出题」并没有对所有配对生效。
- 触发条件:(1)两个候选的同一条边界分别落在 12 月底和次年 1 月初:代码条件是
one.year == two.year and 差 < 阈值才跳过,跨年的配对即使只差几天也放行。(2)两个候选的边界列表按位置zip:某条边界跨出[出生+5, 出生+80]的年份边缘时,两边列表长度差一到两个元素(MD 起点与其第一个 AD 起点是同一天)。此后每一对比较的都是不对应的边界,间隔很大,于是全部放行。 - 根因:闸门只实现了同年分支;按位置配对假设两边列表逐项对应,而 Vimshottari 的真实关系是整体平移同一天数。
- 已确认事实:20 分钟虚构配对(MD+AD)有 10.7% 错位;v4 刷新阶段(含 PD)线上配对有 35–44% 错位。研究补丁把闸门改为对所有配对生效、错位时对齐后:±10 初始阶段没有带日期题的例子从 4 例变成 8 例,刷新阶段从 3 例变成 4 例,带日期题均值 4.85 → 4.10;±30 / ±60 基本不变。窄窗上现有的一部分带日期题依赖这两个漏洞。
- 未确认:这些题问的是「几个候选的边界只差几天」的那个月份,按月精度回答能否可靠区分,没有评估;修掉漏洞会让窄窗更早出现「没有带日期题」,是否可以接受需要产品决定。
- 修复:未修。任何修改都要按 ERR-110 重新冻结 sealed holdout / reported offset 记录。
- 验证:
python3 scripts/research/representative_pairs_probe.py(strict_gate对照)、python3 scripts/research/dasha_shift_per_minute.py(repo_raw_zip_misaligned_share)。 - 防复发:待定。改闸门时,同时用同年、跨年、列表错位三类虚构配对做回归。
- 相关记录:BUG-689、BUG-740、BUG-1047;ERR-110。
- 复发自:无。
- 修复版本:未修。
BUG-1049 | 生时校正开场没有例子、满是行话,用户不知道该答什么
- 状态:resolved(已部署 staging:门禁 run 2952(前端 4081 / 0 fail / 0 skip,Python 948 passed)、deploy run 2954,
/api/healthgitCommit =e8195783;真机清单待产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
frontend/src/lib/rectification-agentic/user-copy.ts(GENERIC_COLLECT_QUESTION、OPENING_COLLECT_DOMAINS、openingSpokenBody、isAcceptableOpeningBody)、v9/collect-prompt.ts(stripQuestionSentences/isQuestionSentence)、v9/agent-run-messages.ts(buildOpeningBrief)、v9/agent-run-attempt.ts(开场正文验收)、v9/method-followup.ts(spokenFollowupForUser)、mastra/rectification-v9-tools.ts(rectification-set-focus)、mastra/agentic-rectification.ts(开场说明)、Skilljyotish-birth-time-rectification§4 OpeningPolicy 与references/conversation-strategy.md§2。 - 用户现象:产品 09-26 staging(
e53052a2)真机:新建生时校正,开场只有「眼下按 04:45–05:15 来核对,用你记得的经历对照大运和盘面变化。最后给区间和代表分钟,不给精确到秒。」和题干「先说你最容易想起的一两件,年月大概就行。」——没有任何例子,不知道「一两件」指什么,还有大运、盘面、代表分钟、精确到秒这些没解释过的词。 - 触发条件:零证据开场(
collect:other:*,source=method_coverage)。模型写的开场正文带例子句时先被剥掉、验收不过、换成兜底正文;兜底正文的例子句随后在 GET 组装(attachQuestionsToTurns)与客户端结算合并(mergeTurnQuestions,BUG-1045 起)里被再剥一次。 - 根因:两步叠加。①
aa7ccb30(09-09,BUG-604)把六个例子从题干GENERIC_COLLECT_QUESTION挪进开场正文第三句,题干改成不带例子的「先说你最容易想起的一两件,年月大概就行。」;②dd8f35f7(09-11,BUG-646~648)把正文第三句改写成「先说你最容易想起的一两件,比如……,年月大概就行。」,开头 12 个字与题干完全相同。BUG-585 引入的去重规则(4e0db55f起,isQuestionSentence)把「以题干前 12 个字开头」的正文句一律当成重复题干删除,例子句因此被删。09-11 起刷新后看不到例子;BUG-1045(e4c1c7a3)把同一去重搬到客户端后,实时也看不到。行话来自 BUG-604 / BUG-648 的三句模板本身。 - 复发自:BUG-504(防复发「开场题文案必须带具体例子」,验证「零证据开场 spoken prompt 含『比如』」)。为什么没拦住:
aa7ccb30把 BUG-504 的断言改成了反向(rectification-server-focus.test.ts与agent-voice-copy-contract.test.ts断言题干不含「比如」,注释写「领域清单在开场正文」),把「必须带例子」的保证挪到了正文;而正文的检查(isAcceptableOpeningBody、openingSpokenBody至少五类)只测正文字符串本身,没有一条测试把正文和题干一起走过stripQuestionSentences/attachQuestionsToTurns/mergeTurnQuestions看用户最终看到什么;dd8f35f7改写第三句时也没有这样的渲染断言。 - 修复(产品 2026-09-26 决策 D1):题干回到带例子并加示例回答:「先说一两件你记得的大事,比如上大学、第一份工作、搬到别的城市、谈恋爱或结婚、家里添丁、生病住院。说个大概年月就行,例如「2015 年夏天换了工作」。」(示例年份是固定文案,不从生日推;只在题干,正文仍不写年份)。开场题干改为服务端固定:
rectification-set-focus对零证据开场仍校验模型的spokenPrompt,校验通过后存服务端题干,模型改写不进问题块。正文改两句大白话(兜底与 brief 同一意思):「我们来把你的出生时间缩小到更准的范围,现在先在 HH:MM–HH:MM 之间找。做法很简单:你说几件人生里的大事和大概年月,我拿去和星盘对照。」;模型正文须说到星盘和年月、带当前窗口、不含大运 / 盘面 / 分盘 / 候选 / 区间 / 代表分钟 / 精确到秒 / 年份 / 问号、最多提两个例子,否则换兜底。开场不再说「最后给区间和代表分钟,不给精确到秒」;交付卡的「这只是代表性候选,不是已确认的唯一出生分钟。」不动。去重改为整句比较:正文句与题干整体或题干任一句相同(去空白与句末标点)、或字符二元组 Dice 相似度 ≥ 0.8 才算重复;只共享开头几个字的句子保留;以问号结尾的句子照旧删除(BUG-585)。Skill 10.0.30 → 10.0.31(§4 OpeningPolicy 与 conversation-strategy §2 改写),10.0.30 标 deprecated、快照保留,旧会话照常打开。 - 验证:
frontend/tests/rectification-opening-plain-20260926.test.ts(零证据开场题干含「比如」「例如」与六个例子、唯一年份是示例里的 2015;有带日期经历或重问时不用该题干;模型改写spokenPrompt时 set-focus 仍存服务端题干;服务端拼接、GET 组装、客户端合并三条路径正文两句都在、题干只出现一次;09-26 staging 实际存下的旧正文例子句在新去重下保留;逐句复述题干仍被去掉;近似改写一两个词仍算复述;正文无行话、旧兜底正文与一句招呼不被接受)、frontend/tests/rectification-opening-plain-20260926.test.tsx(挂真实RectificationAgenticChat,自动发开场、流式结算、快照合并后:正文两句在、题干与六个例子各 1 次、开场这一轮无行话)。三条旧断言按三栏注释恢复为 BUG-504 口径。截图:docs/testing/rectification-opening-plain-20260926/。 - 防复发:开场题干必须带例子(BUG-504 口径恢复),且由服务端固定,不由模型改写。任何改开场正文或题干的提交,必须同时有「正文 + 题干走过 GET 组装与客户端合并后用户看到什么」的断言,不能只测单个字符串。去重不得再按前缀判定。
- 相关记录:BUG-504、BUG-604、BUG-646、BUG-648、BUG-585、BUG-1045、BUG-969、BUG-621。
- 修复版本:
8fc1a5bd,随e8195783部署 staging(run 2954)。
BUG-1050 | 生时校正步骤回执写内部名,同一步显示两次
- 状态:resolved(已部署 staging:门禁 run 2952(前端 4081 / 0 fail / 0 skip,Python 948 passed)、deploy run 2954,
/api/healthgitCommit =e8195783;真机清单待产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
frontend/src/lib/rectification-activity-labels.ts(完成名、进行中句、点选活动句、慢步骤句、完成列表)、frontend/src/lib/rectification-timeline-adapter.ts(rectificationTimelineRows)。 - 用户现象:同一次开场,回答上方写「已完成 3 步 / 读取校正记录 / 设置对话焦点 / 设置对话焦点」:步骤名是内部说法,「设置对话焦点」出现两次。
- 触发条件:Agent 一轮里对同一工具调用两次(开场先写问题、再改写问题,各调一次
rectification-set-focus),或调用多个工具。 - 根因:实时时间线(
startActivityTraceStep)每次工具调用追加一行,结算后保留这份实时轨迹,rectificationTimelineRows原样映射,「已完成 N 步」数的是调用次数;刷新后走持久化回执(按工具去重)才变成两行。步骤名直接取自工具语义(校正记录、对话焦点、事件证据、候选稳健性),没有按用户口吻写。 - 修复(产品 2026-09-26 决策 D2):
rectificationTimelineRows对已完成的步骤按显示名去重(保留第一行,进行中与失败行不动),「已完成 N 步」随之等于看得见的行数;rectificationCompletedTrail同样去重。十四个工具的完成名与进行中句、点选活动句全部改大白话(对照表见frontend/docs/VOICE.md「生时校正开场与步骤名」),进行中句沿用 BUG-1047 已批准的「正在准备下一个问题…」「正在记下这件事…」「正在重新对照盘面…」;慢步骤句改为「还在 + 进行中句」;失败行改为「在做什么 + 未完成」(「重新对照盘面未完成」),不再是过去式完成名加「未完成」。BUG-1047 的阶段句顺序与行为不变。 - 验证:
frontend/tests/rectification-opening-plain-20260926.test.ts(read-case + set-focus ×2 → 两行「看了你的资料」「准备好下一个问题」、「已完成 2 步」;两个共用名字的工具只一行;全部步骤名不含对话焦点 / 校正记录 / 事件证据 / 候选 / 稳健性 / 焦点)、frontend/tests/rectification-opening-plain-20260926.test.tsx(真实组件开场回执「已完成 2 步」、两名各 1 次);agent-activity-progress、rectification-adopt-flow-20260902、rectification-agentic-entry、rectification-timeline-adapter、rectification-latency-20260926.test.tsx的旧文案断言按三栏注释更新。 - 防复发:步骤名面向用户,改名先对照 VOICE;时间线任何按调用次数追加的地方,展示前都走同一个去重投影。
- 相关记录:BUG-1047、BUG-1049、BUG-725。
- 复发自:无。
- 修复版本:
8fc1a5bd,随e8195783部署 staging(run 2954)。
BUG-1051 | 普通咨询回答写到一半被掐断,仍按完成扣点、不提示未完成
- 状态:resolved(已部署 staging:门禁 run 2952(前端 4081 / 0 fail / 0 skip,Python 948 passed)、deploy run 2954,
/api/healthgitCommit =e8195783;真机清单待产品走) - 首次发现 / 最近更新:2026-09-26 / 2026-09-26
- 影响面:
POST /api/consult的三条 agentic 路径(本命、申报时段、无出生分钟);frontend/src/lib/stream-agent-response.ts的consumeAttempt/continueCurrentAnswer/finishPass4/ 结算段;frontend/src/mastra/consultation-tools.ts时间常数;frontend/src/lib/agent-observability.ts;frontend/src/app/api/consult/route.ts的composeAnswer/continueAfterLength/retryForAnswer。 - 用户现象:staging
e53052a2(2026-09-26 21:24 CST)一次普通咨询,回答停在一个二级标题之后的半句上;活动区写「已完成 7 步」,没有出现「回答未完成,已保留现有内容;本次不会扣点。」,会话按完成保存并扣点。 - 触发条件:本命路径。工具循环(思考、读 Skill、约 31 秒/领域的计算,共 7 步)先用掉 110 秒闸门的大部分,写回答(compose)只剩几秒。
- 根因:三层。
- 共用闸刀:
route.ts只建一个AbortSignal.timeout(AGENT_TIMEOUT_MS)(110 秒),工具循环、composeAnswer、续写和回答重试全都展开同一个streamOptions。BUG-944 的防复发写的是「每个 stream 自己的预算」,但那一轮只取消了分段写作,一次成文的 compose 仍然共用这把闸刀。 - Mastra 1.50.1 超时不抛错:signal 触发时先入队
{ type: "abort" },再发finish(reasontripwire),然后正常关流(@mastra/core/dist/chunk-OE4IEL7C.js约 27360 / 27452 行)。stream-agent-response.ts的mapChunk忽略abort,abort 运行步只在catch里记,continueCurrentAnswer只认length,于是流程走到onComplete(扣点并按完成落库)和run.completed;finishPass4还把 Pass 4 缓冲里的半句当最后一句发了出去。 - 同一缺口也覆盖其他非
stop的结束:content-filter、tool-calls(compose 是toolChoice: "none"、1 步)、other、error,以及供应商没发 finish(实测 Mastra 仍补一个 reason 为空的finish,归一为unknown)。只要有可见正文,都会被当成完成。
- 共用闸刀:
- 为什么 BUG-305 的测试没拦住:BUG-305 的超时回归(
consultation-agentic-runtime.test.ts「a timeout after partial visible text…」)手工throw new DOMException("…", "TimeoutError")。这不是 Mastra 的真实流形状(违反 AGENTS §7.4):真实超时根本不抛错,永远进不了catch,所以测试一直绿,线上照样扣点。BUG-944 的防复发只写在文字里,没有测试锁住「compose 不与工具循环共用 signal」。BUG-612 在 staging 日志里已经见过同一形状(modelFinishReason=tripwire、墙钟约 110 秒),当时按「模型改领域、分段再调工具」处理,没有追到 Mastra 的 abort 不抛错。 - 修复(产品 2026-09-26 决策 D1–D3):
- D1 写回答阶段自有时钟:新增
CONSULTATION_COMPOSE_TIMEOUT_MS = 70_000与createConsultationAnswerClock()(第一次调用时才开始计时,同一轮后续的续写、Pass 4 重写、回答重试共用这一个 signal)。composeAnswer、三条路径的continueAfterLength与retryForAnswer都改用它;工具循环和工具本身仍用 110 秒的agentAbortSignal。最坏总等待 110 + 70 = 180 秒;路由maxDuration120 → 240(自托管node server.js不执行该值,只作上限说明)。70 秒的依据见 PROGRESS。 - D2 结算只认
stop:每次 attempt 记录自己的 finish reason 与是否收到 Mastraabort块;写回答的最后一次 attempt 不是stop(abort/tripwire、content-filter、tool-calls、other/unknown/error、没有 finish)而正文非空时,不调用onComplete、不发run.completed,改为run.failed/answer_truncated,保留已流出的正文,账务走cancel,客户端沿用既有提示。Mastraabort块记kind: "abort"运行步(工具循环tool-abort、写回答compose-abort),另记answer-truncated校验步,回执不再显示全部成功。length仍先续写一次;续写本身也被掐或仍停在length时同样按截断处理。被掐的流不再把 Pass 4 缓冲里的半句冲出去。 - D3 观测:
[agent-observability]新增composeFinishReason(封闭枚举 +missing)、composeAborted(布尔)、answerVisibleChars(计数),只有枚举和数字,不含正文。公开回执仍不带modelFinishReason(BUG-305 规则)。
- D1 写回答阶段自有时钟:新增
- 验证:新增
frontend/tests/consult-answer-truncation-20260926.test.ts(15 条,全部用真实 MastraAgent+ 假模型产生的流):共享闸刀在正文中途触发 →answer_truncated、onComplete未调用、有compose-abort步、半句不外发;工具循环的 signal 已过期时 compose 用自有时钟照常完成(对照组:沿用过期 signal 则不完成);答案时钟首用才起算、全阶段共用;content-filter/tool-calls/other/unknown/error、供应商无 finish、流无 finish 块各一条 → 截断;length续写后stop→ 完成并扣点;续写被掐 → 截断;正常stop→ 完成并扣点一次;观测字段通过严格 schema 且不含正文;路由源码合同(compose、3 处续写、3 处回答重试都用答案时钟,110 + 70 ≤ 180 且小于maxDuration)。修复前 11 / 15 条失败。BUG-305 旧用例保留并加三栏说明、补 abort 步断言;11 条手造「无 finish」的既有 fixture 补上 Mastra 真实流必有的finish(stop)(断言未改)。数字(全量、Python 门、构建、gzip)见docs/tasks/PROGRESS-consult-answer-truncation-20260926.md。 - 未做:活动区标题「已完成 N 步」按时间线行数计,截断回复上仍会这样写(真正的信号是输入框上方的未完成提示);改它属于 UI 改动,未在本单范围。真实供应商下 compose 的实际耗时与
composeFinishReason分布需部署后用新埋点复核。 - 防复发:结算只认
finish = stop,任何新的写回答流都要经过同一判定。流的超时 / 中止回归一律用真实 MastraAgent+ 假模型产生的流(abort 块 +finish(tripwire)),不得手工throw。每个新加的模型流要有自己的时间预算,并用源码合同锁住它不与工具循环共用 signal。 - 相关记录:BUG-305(原记录)、BUG-944(「每个 stream 自己的预算」未落到 compose)、BUG-612(同一 tripwire / 110 秒形状)、BUG-280(回答重试没有独立时间预算,本单一并挂到答案时钟上)。
- 复发自:BUG-305(半截回答被当成功并扣点;原防线只拦「抛出的超时」与
length,没拦 Mastra 不抛错的 abort)。 - 修复版本:
1530a0dd,随e8195783部署 staging(run 2954)。
BUG-1052 | 登录后 / 打开裸 / 落在上一次对话里(真机:以为在首页提问,其实在旧生时校正会话里)
- 状态:resolved(已部署 staging:门禁 run 2959(
560d4fc2)通过,deploy run 2961,/api/healthgitCommit =560d4fc2(其后仅文档提交);真机清单待产品走) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:首页整页加载落点(
frontend/src/lib/chat-session-url.tsresolveBootstrapSessionSelection/bootstrapSelectionFromLookup、frontend/src/lib/home-bootstrap.tsresolveStarterHomeLandingSessionId/starterHomeLandingNeedsConsultation、frontend/src/lib/home-bootstrap-run.tsresolveLanding与暖返回commitWarmLanding/ 恢复段、frontend/src/lib/home-warm-start.tsresolveWarmLanding、frontend/src/lib/home-cloud-sync.tsredirectToLogin/resolveLookupBootstrap)、侧栏次级页与「去登录」链接(app-sidebar.tsxleaveChat)、首页内新建对话(use-session-management.tsstartNewChat/openChatBoundToProfile)。 - 用户现象:staging
e8195783真机:用户登录后在他以为的首页输入「我和父母关系如何」,实际是在上一次的生时校正会话里,Agent 回答说不能用父母这件事去核对高考那一条。 - 触发条件(两条路径,代码逐条核实):
- 登录返回存根:同一标签页里停在
/?c=<校正会话>时发生任何 401(redirectToLogin()),或点侧栏次级页 / 「去登录」链接(leaveChat()),都会把当前?c=写进sessionStorage["jyotisha.session-url-return"]。邮箱验证码登录成功后email-otp-login.tsx跳successPath = "/",启动时resolveBootstrapSessionSelection在没有?c=时读存根 →replace-selected→ 写回?c=并自动打开这条校正会话。真机「落在旧校正里」只能来自这条路径。 - 默认落点:没有
?c=也没有存根时取会话列表第一条(最近一条),resolveStarterHomeLandingSessionId只在它是生时校正时换成「空咨询或任意一条咨询」,只有一条咨询都没有时才新建空白首页。所以裸/(登录、地址栏、书签、刷新裸/)通常打开最近那条旧咨询,而不是空白首页。 暖返回(BUG-1040resolveWarmLanding)逐条镜像同一套规则(含存根与人物范围),同样会落回旧会话。
- 登录返回存根:同一标签页里停在
- 根因:BUG-1038 为「从次级页回来能回到原会话」保留并强化了登录返回存根(找到即写回
?c=并打开),BUG-599 只规定裸/不自动打开生时校正、却默认落最近一条普通咨询。两条规则叠加,使「登录后」和「裸/」都被当成「回到上次」,而产品期望的是空白首页。诊断与任务单一致,无更正;补充一点:列表行消息未加载(messagesHydrated: false)时messages也是空数组,旧的「空咨询」判定只看messages.length === 0,理论上会把这类已保存会话当成空草稿,本轮一并收紧。 - 决策记录(产品负责人 2026-09-27,推翻 BUG-1038 的登录返回存根与 BUG-599 / BUG-1038 以来「裸
/默认落最近会话」的落点):- D1 登录后、以及任何不带
?c=打开/(地址栏、书签、历史、刷新裸/)一律落空白首页(空咨询 + 开场问候),不落旧对话;已有同一人物的空草稿就复用,不重复新建(与?new=1/startNewChat同一个去重函数,首问前不落库沿用 BUG-989)。 - D2
/?c=<id>仍打开该对话(分享链接、侧栏链接、站内跳转不变);?new=1不变。 - D3 删除登录返回存根:401 / 重新登录不再回到旧对话;删掉不再使用的写入与读取,只在启动时清一次旧浏览器残留的值;人物档案的范围规则(空白首页绑定当前人物)保持。
- D4 在对话里刷新:地址栏已有
?c=(writeSessionUrl写入),刷新留在该对话——核实仍成立。 - 执行方解读(请验收时确认):裸
/启动时若有一条旧咨询的回答仍在后台生成,按 D1「不落旧对话」处理成与 BUG-1015?new=1相同:恢复照常在后台进行,但不抢空白首页的落点;只有地址栏点名了会话(?c=keep / lookup)时恢复才会切过去。
- D1 登录后、以及任何不带
- 修复:
resolveBootstrapSessionSelection去掉storedReturnId入参与clearStoredReturn字段:?new=1→ new-chat;?c=→ keep / lookup / replace-clear;其余 → none。replace-selected只服务存根,随之删除;bootstrapSelectionFromLookup去掉来源参数与other-subject。- 落点:
resolveStarterHomeLandingSessionId对 none / replace-clear 只选当前人物的空草稿(新isStarterHomeDraft:consultation、未归档、消息已加载且为空),否则starterHomeLandingNeedsConsultation返回 true → 本地新建空咨询;冷启动用replaceUnsavedEmptyConsultations前插(不留重复草稿)。暖返回同一套函数。 - 恢复:新
landingYieldsToRecovery(urlAction),冷、暖两处把「new-chat 不被恢复抢落点」推广到所有非 keep / lookup 落点。 - 存根:删除
persistLoginSessionReturn/readLoginSessionReturn;redirectToLogin、leaveChat不再写;startNewChat/openChatBoundToProfile不再清;冷启动resolveLanding无条件clearLoginSessionReturn()清旧值;暖返回为存根准备的resumeRectification依赖一并删除(page.tsx少一行,Home()useState / useRef 数不变)。
- 验证:
frontend/tests/new-chat-from-people-lifecycle.test.tsx新增 5 条真实 React 生命周期用例(真实Home+AppSidebar+SessionListProvider,文档加载重置模块状态、保留 storage):裸/最近是有消息的普通咨询 → 空白首页;最近是生时校正 → 空白首页且不开 Case;/?c=打开并刷新保留;校正会话内刷新保留;校正会话内 401 → 重新登录/→ 空白首页。改写 1 条为「旧存根被忽略并清除」。home-warm-return-lifecycle.test.tsx新增冷启动裸/空白首页、改写暖返回裸/空白首页(首帧无加载环)。纯函数:home-bootstrap-reveal(未加载消息的行不算草稿、恢复让步规则)、home-warm-snapshot(暖落点复用本人空草稿、不复用他人草稿)、new-chat-recovery(裸/不被恢复抢、?c=仍被恢复接管)。新增 / 改写的生命周期用例在origin/staging代码上 12 条失败,修复后全绿。全量、Python、构建、gzip 与本地 Chrome 截图见docs/tasks/PROGRESS-home-landing-blank-20260927.md。 - 防复发:生命周期测试锁「登录 / 裸
/→ 空白首页」「?c=与刷新保留」;源码合同反向锁persistLoginSessionReturn/readLoginSessionReturn/storedReturnId/replace-selected/stored-return不再出现、leaveChat只关抽屉、redirectToLogin不写存储。BUG-1038 的测试把「回到原会话」当成要守的行为(存根写入、找到即写回?c=),BUG-599 的测试锁「裸/打开所选普通咨询」,所以没有任何测试会拦「登录后落在旧会话」;这两处断言已按三栏说明改为新行为。 - 未改:站内浏览器「返回」退到一个不带
?c=的历史条目(popstate,applySessionPopStateRef)仍按原规则选列表里第一条非校正会话;这不是整页打开/,不在 D1 列举范围内,如需同样落空白首页另开单。 - 相关记录:BUG-1038(登录返回存根与
bootstrapSelectionFromLookup来源参数,本条推翻其存根部分)、BUG-599(裸/不自动开校正、默认落咨询,本条收紧为空白首页)、BUG-1015(?new=1新建意图与恢复不抢落点,规则被推广)、BUG-1040(暖返回,同规则、首帧无加载环不变)、BUG-989 / BUG-929 / BUG-1001(首问前不落库、空草稿不进列表)、BUG-705(?c=lookup 不变)、BUG-936(首屏兜底不变)。 - 复发自:无(产品规则变更 + BUG-1038 / BUG-599 规则叠加的组合路径)。
- 修复版本:
d2551437,随560d4fc2部署 staging(run 2961)。
BUG-1053 | 普通咨询用户看到的回答没看过星盘:本命路径由看不到计算结果的第二个流来写
- 状态:resolved(已部署 staging:门禁 run 2959(
560d4fc2)通过,deploy run 2961,/api/healthgitCommit =560d4fc2(其后仅文档提交);真机清单待产品走) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:
POST /api/consult本命路径(runAgenticConsultation的 natal 分支,含首页「深入看今日」入口);frontend/src/lib/stream-agent-response.ts(consumeAttempt/publishFindings/composeOnce/finishPass4);frontend/src/lib/consultation-thinking-plan.ts(consultationComposePrompt);frontend/src/mastra/consultation-tools.ts(时间常数)。申报时段与无出生分钟路径没有 compose,不受本缺陷影响,但一并改为同一套取答规则与时钟。生产(7b620c7a,08-16)没有这条路径,不受影响。 - 用户现象:staging 上本命提问的回答与盘面对不上。gpt-5.6-luna 直接写「没有父母主题的盘面证据」;DeepSeek 写得像模像样,但上升、月亮、宫位等「盘面事实」并非来自本轮计算。2026-08-23 以来 staging 上 DeepSeek 的本命回答可能引用过没有根据的盘面事实。
- 触发条件:任何一轮本命咨询(每轮都会触发,与问题内容无关)。
- 根因:三段式里的第三段是瞎写的。
- 主工具循环调用
run-jyotish-consultation后,下一步模型看得到约 4 万 token 的计算结果,也写了回答,但这段正文在consumeAttempt里被drainSpoken整段丢弃。 - 随后
composeAnswer另开一个agent.stream,输入只有[...baseMessages, { user: consultationComposePrompt() }](toolChoice: "none"、maxSteps: 1)。本命路径的baseMessages只有历史 + 本轮问题,从不追加工具结果;Agent 在两次 stream 之间没有记忆。 interpretFindings只回思考计划的 id、没有文本,所以 compose 提示词里的「判断依据」恒为空,却写着「服务器计算已经完成」。用户看到的回答就是这个没看过星盘的流写的。continueAfterLength同样只拿历史 + 已写正文,续写也是盲的;只有保留工具的retryForAnswer能重新取回同请求缓存。- 顺带:「深入看今日」入口的用户轮要求三节(今日趋势 / 适合推进 / 一个行动),compose 提示词却强制本命四标题,两条指令互相矛盾。
- 主工具循环调用
- 引入:
04463e9a(2026-08-23,「sliced compose」:compose 前丢弃主循环正文);BUG-944(94c1e81f)把分段写作改为一次成文时保留了这个结构。 - 为什么测试没拦住:compose 相关测试全部是手造的生成器流,没有一条断言「写回答的那次模型调用看到了什么」。
consultation-agentic-runtime.test.ts的「composeAnswer drains leftover first-stream text and writes one body」反而把「丢掉看过证据的正文」当成期望行为锁住。BUG-1051 的回归只看结束方式,同样不看 compose 的输入。 - 修复(产品 2026-09-27 决策 D1–D3):
- D1 删掉第三段:删除
composeAnswer/interpretFindings/publishFindings/composeOnce/drainSpoken、consultationComposePrompt/consultationSectionPrompt(后者自 BUG-944 起已是死代码)、AGENT_SLICE_MAX_STEPS、ThinkFinding。主循环在工具结果之后的那一步写回答,正文保留。 - 取答边界:新选项
stepScopedAnswer(本命、申报时段)。按 Mastra 的step-start/tool-call/step-finish分步:一步里的正文先扣住,出现 Markdown 标题或满 160 字才放出;这一步如果调用了工具,扣住的正文整段丢弃;计算结果到手之前的正文沿用原规则(不进回答、只留作降级材料)。所以工具前后的过程说明都进不了回答。 - 写作要求搬家:原 compose 提示词里的「开场无标题、一次写完四个二级标题、不写统一参数与技法审计表、不写思考过程」改由主循环看到的用户轮携带(
natalAnswerShapeInstruction();「深入看今日」入口dailyAnswerShapeInstruction()),并加一句「拿到本轮计算结果后直接写回答,只用结果里的盘面事实」。系统提示里的 VOICE §7 开场形态与四标题规则不变。 - 续写有据:
streamAgentResponse记下计算工具的结果,continueAfterLength(output, evidence)由consultationContinueMessages()把它作为一条用户消息放在已写正文之前(申报时段已预计算的包已在baseMessages里,不重复)。 - Pass 4 整篇被拒:原先的 compose 重写改为带
PASS4_RETRY_HINT的retryForAnswer(保留工具、命中同请求缓存);已放出过句子时仍就地丢弃、不重写(BUG-950)。 - 时间预算:
createConsultationRunClock()一轮两只钟。工具与申报时段预计算仍用 110 秒的toolSignal(每领域 31 秒、领域墙钟 65 秒不变);模型循环的loopSignal在计算结果到手之前跟随工具阶段,到手后(onAnswerPhase)交给 70 秒答案时钟(CONSULTATION_ANSWER_TIMEOUT_MS,由CONSULTATION_COMPOSE_TIMEOUT_MS改名),续写与回答重试共用同一只答案钟。计算刚好在 110 秒前完成、消费端还没看到时,由answerReady判断交接而不是掐断。最坏仍是 110 + 70 = 180 秒,maxDuration240 不变。 - BUG-1051 保持:结算只认写回答那一步的
stop。Mastra 循环在一步以other/unknown/ 无工具调用的tool-calls结束后会再跑一步,所以结束原因取「最后一个写出正文的步」而不是整条流的finish;length→ 续写;其余非stop且有正文 →answer_truncated、不扣点、记compose-abort(名字保留,现指写回答那一步)。 - D2 观测:
composeFinishReason/composeAborted/answerVisibleChars名字不变,含义改为「写回答那一步」,仍只有枚举 / 布尔 / 计数。
- D1 删掉第三段:删除
- 验证:新增
frontend/tests/consult-single-pass-answer-20260927.test.ts(15 条,真实getJyotishAgent+ 真实 skill 绑定 + 真实计算工具 + 记录提示词的假模型,计算数据来自fixtures/consultation-workflow-report-blocked-repairs-golden.json):写回答的调用提示里有工具结果与 golden 盘面数值;工具结果之后只有一次模型调用;工具前、工具之间的过程说明不进回答;四标题与无标题开场保留;length续写提示里有计算结果;content-filter/tool-calls/other/unknown截断不扣点;答案钟掐断 → 截断、compose-abort;工具阶段到期不掐写回答;空回答走带工具的回答重试且不重复宣布计算;Pass 4 整篇被拒带提示重试;路由源码合同。修复前用同一套真实 Agent 复现:3 次模型调用,只有第 2 次提示里有工具结果,用户看到的是第 3 次写的。既有 compose 用例改写并附「原值 / 新值 / 原因」(consultation-agentic-runtime6 条、consult-answer-truncation-20260926全文件、consultation-thinking-plan2 条、consultation-workflow-contract3 条、consultation-stream-recovery1 条、application-billing-contract1 条)。数字见docs/tasks/PROGRESS-consult-single-pass-answer-20260927.md。 - 未做(D3 范围外):约 4 万 token 的工具结果瘦身与按领域选技法、家庭拆父母 / 子女、43 行审计表。真实供应商下写回答步的耗时、
composeFinishReason分布与盘面引用是否对得上,需部署后按docs/testing/consult-single-pass-answer-20260927.md复核。 - 防复发:用户可见正文必须由看过本轮证据的那次模型调用写出。新增任何写回答的流,都要有「这次调用的提示里有工具结果」的真实 Agent 回归(记录提示词的假模型),不得只断言结束方式或正文形状。续写、重试这类新流必须显式带上证据或保留工具。
- 相关记录:BUG-612(分段写作再调工具,同一 compose 结构)、BUG-942 / BUG-943 / BUG-944(三通道重做,保留了丢弃主循环正文的结构)、BUG-1051(写回答时钟与结算规则,本单沿用并搬进主循环)、BUG-305、BUG-937(主循环开 thinking,不得用非
auto的 toolChoice)。 - 复发自:无(新缺陷)。
- 修复版本:
eef0cb74,随560d4fc2部署 staging(run 2961)。
BUG-1054 | 咨询投影把当前 Narayana 段和子运日期裁成空
- 状态:resolved(已部署 staging:门禁 run 2962 通过,deploy run 2964,
/api/healthgitCommit =cd4dde9d;真机清单待产品走) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:
frontend/src/mastra/consultation-workflow.ts的toModelOutput/projectAllowlistedTree(timingKeys);普通咨询写答案时看到的 timing 卡 - 现象:引擎已经算出当前 Narayana 段和 Vimshottari 子运(pratyantar)起止日期,投影给模型的 timing 卡里
current_dasha是空对象,子运日期整段不在。 - 触发条件:有出生分钟的本命咨询走
toModelOutput。2026-09-27 数据卡调研用 3 张公开 AA 盘 × 10 种问法,30/30 都是这个形状。 - 根因:timing 投影只保留 allowlist 里的键。Narayana 当前段写在
current_dasha.md/ad/pd下,md不在 allowlist 里,于是对象被留成空。子运日期的键是pratyantar_dasha_timeline,也不在 allowlist 里,整段被丢掉。大运和子运(antardasha)的日期键在 allowlist 里,所以那两段还在。 - 修复:
timingKeys放行md/ad/pd、它们的嵌套标量years/start_age/end_age、remaining_years与pratyantar_dasha_timeline;其余白名单、深度上限(4)与条数上限(24)不变。 - 验证:新增
frontend/tests/consult-projection-timing-20260927.test.ts(4 条),fixture 是真实引擎对 3 张公开 AA 盘的 family 路由输出(frontend/tests/fixtures/consult-evidence-card-golden.json,由scripts/research/capture_consult_evidence_card_golden.py生成,只按键裁剪、不改值)。逐盘断言引擎的 Narayana 当前 md / ad / pd 的星座、主星、年数、起止年龄与remaining_years,以及 pratyantar 当前段与下一段的主星和起止日期,原样出现在模型可见的 timing 卡里;大运、子运日期不变。修复前 2 / 4 条失败。 - 防复发:timing 卡的回归断言值而不是键;数据卡(同一分支 T3)从这张投影复制当前 Narayana 段与 PD 起止,卡的逐字测试再锁一层。
- 相关记录:BUG-287(同一条 allowlist 曾经把分盘和审计表整段挡住;这次是嵌套键还没放行,不是「没算」复发)
- 复发自:无
- 修复版本:
75a1844c,随cd4dde9d部署 staging(run 2964)。
BUG-1055 | 生时校正证据轮:服务端范围句被裁句裁掉,旧范围反而落库
- 状态:resolved(已部署 staging:门禁 run 2965 通过,deploy run 2967,
/api/healthgitCommit =261d7b2e;真机清单待产品走) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:
frontend/src/lib/rectification-agentic/v9/agent-run-attempt.ts(结算段)、agent-run-finish.ts(证据轮裁句)、agent-run-support.ts(composeSpokenWithServerFacts)、新增spoken-grounding.ts;rectification-record-evidence-batch返回;系统提示agentic-rectification.ts。 - 用户现象:说一件带年月的事,范围句「范围从 A 变为 B。」在流式时闪出一下又被撤回,落库只剩模型的第一句;模型第一句写了开轮时的旧范围时,旧范围落库。「这次没有重新比较」「候选比较这次没跑成」同样会被裁掉。
- 触发条件:证据轮模型正文 ≥ 2 句(加上范围句超过两句即触发「只留首句」),或模型正文带时刻 / 百分比。诊断用真实
runV9AgentTurn复现(两句正文 → 落库只剩首句;首句带旧范围 → 旧范围落库)。 - 根因:服务端事实句与模型正文共用「先拼后裁」的管线:attempt 把范围句接到正文末尾并先流式放出,finish 再按
trimEvidenceTurnBody(超过两句只留首句)裁剪并replace。batch 工具返回里没有重算后的范围,模型唯一的范围来源是开轮读到的旧值;系统提示还鼓励「有收窄就说进度」。证据轮与交付轮正文没有任何数字校验。 - 修复:attempt 不再流式放出事实句,只把结构化事实(比较失败 / 未重算 / 开轮范围 / 结算范围)与当轮数字白名单交给 finish;finish 先对模型正文执行 P3 数字白名单(带时刻、时刻区间或百分比且不等于当轮服务端事实的句子整句丢弃,正则只当门不在句中删词;一句不剩时用 batch 回执复述代替),再按 BUG-606 / BUG-615 裁句,最后接服务端事实句,一次
replace与落库一致。batch 返回range_after_rescore(重算后可信范围、代表分钟、吻合率、delivers_range_this_turn),回执指纹仍按旧形状计算。系统提示改为「范围由系统说,除出牌轮外不写时刻和百分比」,出牌三句只在 batch 说本轮交付时写。 - 验证:
frontend/tests/rectification-grounding-20260927.test.ts(9 条:两句正文保住范围句且流式终态 = 落库;三句正文裁成一句范围句完整;首句旧范围整句不落库;编造吻合率被丢、服务端吻合率保留;重算后范围与代表分钟可复述;正文被白名单清空时用 batch 复述;「这次没有重新比较」「候选比较这次没跑成」在多句正文下保留且不被撤回;白名单单元)。前 8 条在未改动基线上全部失败。rectification-grounding-projection-20260927.test.ts锁 batchrange_after_rescore与回执指纹不变。BUG-588 原测试(rectification-probe-replay-loss-20260908.test.ts)未改、仍通过。 - 为什么旧测试没拦住:BUG-588 的回归只用单句正文(「记下了。」「接下来我们继续。」),加上范围句正好两句,不触发裁句;源码合同只锁「先剪问句再接范围句」的顺序,没有一条用多句正文跑真实
runV9AgentTurn看落库。 - 防复发:服务端事实句不得早于正文最终态流式放出,也不得再进入任何裁句 / 过滤;证据轮落库正文里的时刻、区间、百分比必须等于当轮服务端事实。新增旁白规则必须用多句正文与带数字正文跑真实
runV9AgentTurn。 - 相关记录:BUG-588、BUG-594、BUG-606、BUG-615、BUG-585、BUG-1053(同为「写正文的步看不到事实」一族)。
- 复发自:BUG-588(证据轮范围句,本次在裁句一侧丢失)。
- 修复版本:
0e0baaa7,随261d7b2e部署 staging(run 2967)。
BUG-1056 | 生时校正「重试(重新生成)」盲写且不校验就落库
- 状态:resolved(已部署 staging:门禁 run 2965 通过,deploy run 2967,
/api/healthgitCommit =261d7b2e;真机清单待产品走) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:原
POST /api/rectification/cases/[caseId]/turns/[turnId]/regenerate、v9/regenerate-turn.ts、getRectificationV9RegenerationAgent、createRectificationV9ReadOnlyTools;校正消息操作栏(rectification-message-entry.tsx、rectification-agentic-chat.tsx、rectification-chat-view.ts、rectification-surface-state.ts、rectification-chat-message-actions.ts);ChatMessageActions。 - 用户现象:点校正回答下的「重新生成」,新正文可以写出与卡片不符的范围和吻合率并直接替换旧正文(诊断复现:写出「范围收在 09:00–09:05,吻合率 90%」原样落库,实际范围 08:54–09:24)。
- 触发条件:生时校正里点最近一条助手回答的重新生成。
- 根因:重新生成走 V5 之前的自由重写:
agent.generate只拿旧正文 + read-case(且 read-case 在 9 个候选时已清空对话,见 BUG-1057),Skill 未注入,输出不剪问句、不补范围句、不查数字与禁用词,直接调regenerate_agentic_rectification_turn落库。 - 修复:产品 P1 决定删除入口(多余入口宁可删除也不修):删掉重新生成路由、服务端模块、重新生成 Agent 与只读工具集、客户端重新生成动作与状态、
regenerating/canRegenerate属性;ChatMessageActions不传onRegenerate时不画重新生成按钮,普通对话不变。数据库函数regenerate_agentic_rectification_turn本单不动(AGENTS §7.6 两轮规则),待另开单退役。 - 验证:
frontend/tests/rectification-grounding-regenerate-20260927.test.ts(校正回答只有赞 / 踩 / 复制;普通对话操作条仍有「重新生成回答」;路由与服务端代码已删、src不再引用该函数、迁移里的函数仍在)。既有断言按三栏改写(见 PROGRESS);rectification-v9-regenerate.test.ts整份删除(测的是已删除的接口)。 - 防复发:生时校正里任何写入助手正文的新路径都必须经过与主链相同的剪问句、事实句与数字白名单,不得再有只读自由重写。
- 相关记录:BUG-1055、BUG-1057、BUG-1053。
- 复发自:无。
- 修复版本:
84eb2315,随261d7b2e部署 staging(run 2967)。
BUG-1057 | 生时校正 read-case 超 6KB 时先清空对话与证据(候选多时「失忆」)
- 状态:resolved(已部署 staging:门禁 run 2965 通过,deploy run 2967,
/api/healthgitCommit =261d7b2e;真机清单待产品走) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:
frontend/src/lib/rectification-agentic/v9/turn-decision.ts(projectTurnDecision、enforceTurnDecisionBudget、新增modelVisibleInference);compare / offer 给模型的inference_state。 - 用户现象:候选多(约 7 个及以上的常见窗口)时,用户说「那年」「刚才那件事」,助手像没听过前文。
- 触发条件:turn-decision 投影超过 6KB。公开 AA 盘 30 分钟窗 9 个候选、2 条短对话即触发(5,639 字节,
truncated: true,对话与证据摘要都是空数组)。 - 根因:模型消息里没有历史(只有 bootstrap + 本轮用户话),read-case 的
recent_turns与relevant_evidence_summary是唯一对话记忆;预算裁剪却最先砍它们,保留每个约 458 字节的候选定位元数据(窗口序号、偏移、区间明细),candidate_summary.candidates还与inference.candidates重复。现有测试只断言不超上限。 - 修复:给模型看的候选只留 time / score / status / cluster_range(上一轮淘汰与分差改按时刻列出),删去重复的
candidate_summary.candidates;超限顺序改为:先去掉 cluster_range → 只留最好的 6 个候选(未淘汰优先,注明省略数)→ 缩短对话与证据 → 最后才清空(truncated)。落库的推断状态与回执不变。 - 验证:
frontend/tests/rectification-grounding-read-case-20260927.test.ts(真实引擎 fixture 9 个候选 + 6 条对话:对话 6 条、证据 3 条都在、候选只有四个字段、无重复列表、≤6KB;超限时先砍候选明细再动对话;第一步只去 cluster_range)。体量:9 个候选 2 条对话 5,639 → 3,022 字节(对话 0 → 2 条、证据 0 → 3 条);6 条对话 3,420 字节。 - 防复发:read-case 的预算裁剪不得先动
recent_turns/relevant_evidence_summary;新增投影字段要按候选数量级核算体量。 - 相关记录:BUG-1056(重新生成读到的就是被清空的 read-case)、BUG-1055。
- 复发自:无。
- 修复版本:
13d93020,随261d7b2e部署 staging(run 2967)。
BUG-1058 | 点选题答案里顺手说了带年月的经历:范围变了没人说
- 状态:resolved(已部署 staging:门禁 run 2965 通过,deploy run 2967,
/api/healthgitCommit =261d7b2e;真机清单待产品走) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:
frontend/src/lib/rectification-agentic/v9/agent-route-typed-message.ts(点选焦点的deferFollowup分支)、agent-route-support.ts(RectificationAgentTurnState.rangeBeforeTurn)、agent-route-agent-turn.ts、agent-run.ts/agent-run-attempt.ts(rangeBeforeTurn选项)。 - 用户现象:对着点选题打字「有,2023 年 3 月换了工作」,答题让范围收窄了,但这条回复里没有任何范围句。
- 触发条件:分类器判为回答当前点选题且带新的带日期事件(
has_new_dated_event),走deferFollowup交给 Agent 轮。 - 根因:
applyRectificationChoice在 Agent 轮之前已写入答题后的新推断,deferFollowup时它的旁白(含范围句)不落库;随后 Agent 轮 prepare 读到的「开轮范围」已是答题后范围,结算时前后相同,也不写范围句。两边都不说。 - 修复:二选一取「把答题前范围交给随后的 Agent 轮」:typed-message 在
deferFollowup时把答题前的credible_range写进turnState.rangeBeforeTurn,Agent 轮用它做开轮范围。理由:只有一条服务端范围句、走 BUG-1055 的同一条事实句通道(不裁、不撤回),答题与经历两次变化合成一句;不把applied.narration并入正文,避免同一条消息出现两句范围。 - 验证:
frontend/tests/rectification-grounding-route-20260927.test.ts(真实POST /api/rectification/agent+ typed-message + agent-turn +runV9AgentTurn,模块替身只替掉答题写入、分类器、Agent 流、计费与出口门):答题收窄、经历不再变 → 落库有且仅有「范围从 04:50–05:10 变为 04:55–05:05。」;经历再收窄 → 一句从答题前算到最终。去掉修复后两条都失败。 - 防复发:任何「先确定性写入、再交给 Agent 轮」的路径,都要把写入前的事实传给 Agent 轮,不得让 prepare 读到的状态充当开轮状态。
- 相关记录:BUG-588、BUG-569、BUG-1055。
- 复发自:无(BUG-588 / BUG-569 同族,新路径)。
- 修复版本:
5a808449,随261d7b2e部署 staging(run 2967)。
BUG-1059 | 调工具那一步没写完的半句过程说明,会粘到下一步正文开头
- 状态:resolved(已部署 staging:门禁 run 2962 通过,deploy run 2964,
/api/healthgitCommit =cd4dde9d;真机清单待产品走) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:
frontend/src/lib/stream-agent-response.tsconsumeAttempt(按步取答stepScopedAnswer);本命与申报时段两条带工具的咨询路径。 - 现象:数据卡实现(T5 补取工具)写测试时发现:模型在调用工具前写了一句不以句号 / 问号 / 换行收尾的过程说明(如「我先排一下盘:」「再看一眼 D60 分盘,」),这半句不会在那一步被丢掉,而是被原样接在下一步回答的第一句前面放给用户。用同一套真实 Agent + 假模型在修复前复现:回答以「我先排一下盘:」开头。
- 触发条件:带工具的咨询轮,模型在调工具那一步写的过程说明最后一句没有句末标点。以句号收尾的过程说明(BUG-1053 测试用的样例)不受影响,所以 BUG-1053 的回归没拦住。
- 根因:
createVisibleTextTransformer按句子边界放字,没收尾的半句留在它的缓冲里;这个缓冲按整次 attempt 共享、跨步保留。BUG-1053 的按步取答只丢掉「这一步里已经交给取答逻辑的文字」,调工具时没有把转换器里压着的半句一并结算,于是它在下一步第一次push时被冲出来,算作下一步(写回答那一步)的正文。 - 修复:遇到
tool-call块时先visible.finish("")结算这一步压着的半句:计算结果到手前的交给原来的「未签约文字」缓冲(降级材料不变);这一步已经放出过正文的(回答中途补取)当作正文续上,保证已放出的句子不被截断;其余都是过程说明,随这一步丢掉。 - 验证:
frontend/tests/consult-evidence-lookup-20260927.test.ts「BUG-1059: narration without closing punctuation before a tool call never leaks into the answer」(真实getJyotishAgent+ 记录提示词的假模型 + 公开 AA 盘真实引擎 golden),修复前失败、修复后通过;同文件「an open clause of released answer text before a lookup goes out whole」锁住另一面(回答中途补取时,已放出正文的半句照样完整放出)。BUG-1053 的consult-single-pass-answer-20260927.test.ts全部仍通过。 - 防复发:按步取答的回归必须包含「过程说明不以句末标点收尾」的样例;任何在步边界丢弃文字的逻辑,都要连同
createVisibleTextTransformer的缓冲一起结算。 - 相关记录:BUG-1053(按步取答,本条补它漏掉的转换器缓冲)、BUG-1051(结算只认写回答那一步的
stop,不变)。 - 复发自:无(BUG-1053 引入按步取答时留下的缺口)。
- 修复版本:
cb3ee558,随cd4dde9d部署 staging(run 2964)。
BUG-1060 | 双重过运的九分盘「第 N 宫」目标实际检查的是九分盘上升
-
状态:resolved(已部署 staging:门禁 run 2974 通过,deploy run 2976,
/api/healthgitCommit =7a06aa00;图表缓存 15 分钟后生效) -
首次发现 / 最近更新:2026-09-27 / 2026-09-27
-
影响面:
scripts/jyotish_engine.pycmd_double_transit_pac的 D9 层;凡调用它的路径(全读 / 报告的double_transit_pac,以及普通对话数据卡的时运、婚恋双重过运结论,经scripts/consultation_native_layers.py)。D1 层与月亮上升(CL)层不受影响。 -
现象:数据卡 v2 接入双重过运(TASK-consult-evidence-card-v2-20260927 T1)时核对 PAC 结果发现:目标名写「D9_7宫(<D9 第 7 宫星座>)」,但用来判定同宫 / 相位 / 合相的经度是 D9 上升星座的中点。用一张公开 AA 盘复现:土星对「D9_7宫」判为「10 宫相位」,按标签所写的 D9 第 7 宫算应无相位,按 D9 上升算才是 10 宫相位。
-
触发条件:
house≠ 1 时的任意一次调用;house = 1时两者恰好相同。 -
根因:
d9_event_house_lon = (d9_asc_idx * 30) + 15用了 D9 上升索引,应为 D9 第 N 宫的星座索引d9_event_si(同函数 D1 层用的是event_si)。审计同函数其余 D9 目标时另查出一处同类错配:「D9_<宫主>(宫主)」(D9 第 N 宫的宫主)取的是该星的 D1 度数,而 D9 层把过境星按 D1 星座叠到 D9 盘、宫位从 D9 上升数,D1 度数放进这个坐标系里既不是 D1 宫也不是 D9 宫。 -
D9 目标逐条决定(依据
references/marriage-timing-comprehensive-techniques.md的 KN Rao 规则「木星 / 土星关联事件宫 / 宫主 / 宫主 D9 星座」与strict-workflow-router.md的 Double Transit 行):目标名 修复前参与计算的值 决定 理由 D9_{N}宫(<星座>)D9 上升星座中点 改为 D9 第 N 宫星座中点 标签写的就是 D9 第 N 宫;D1 层同名目标用的是第 N 宫星座 D9_{宫主}(宫主)该星 D1 黄经 改为该星 D9 星座中点 D9 层所有目标都在 D9 盘的星座上;与同层 {星}_D9目标取法一致{D1 第 N 宫主}_D9(<星座>)该星 D9 星座中点 保留 正是 KN Rao「宫主 D9 星座」,标签与值一致 {上升主}_D9(<星座>)该星 D9 星座中点 保留 标签与值一致 目标名(字典键)一个不改,下游按名字取值的地方不受影响。
-
修复:D9 目标的构造抽成
_double_transit_d9_targets(asc_deg, natal, event_house, event_lord, ll_name)(返回 D9 上升索引与目标表),cmd_double_transit_pac调用它;只改上表两行的取值,D1 / CL 层与跨层判定代码未动。 -
验证:
- 新增
tests/test_double_transit_d9_targets.py(7 条,已进快速门CORE_PYTEST_TARGETS),三张公开 AA 盘真实引擎运行:house2–12 时 D9 第 N 宫目标 = D9 第 N 宫星座中点且 ≠ D9 上升中点;house = 1仍等于 D9 上升中点;D9 宫主目标 = 该星 D9 星座中点;命令实际按目标表逐个做 PAC;复现盘 7 宫的土星「10 宫相位」消失、木星「同宫(7宫)」出现。在基线代码上跑这份测试,6 条失败、只有 D1/CL golden 通过。 - D1 / CL 层逐字节不变:
tests/fixtures/double_transit_d1_cl_golden.json在基线5671039a上用PYTHONHASHSEED=0生成,修复后重生成与之逐字节相同(3 盘 × 12 宫)。另用 4 组输入(3 张公开盘 + 报告 golden 的虚构输入)× 12 宫共 48 次直接比对:d1、cl、summary48/48 不变;d947/48 变化;double_transit条目 2/48 变化(均为新增 D1+D9 跨层条目)。 - 数据卡 golden
frontend/tests/fixtures/consult-evidence-card-golden.json用采集脚本重生成:与原文件忽略键序后唯一差异是一张盘双重过运结论 9 → 10 条(家庭 / 年运 / 时运三路相同,新增 10 宫Saturn(D1)Venus(宫主) + Jupiter(D9)D9_Venus(宫主))。
- 新增
-
对报告的影响:报告里「双重过运」一节的 D9 行会变(目标命中与否、D1+D9 跨层条目数);报告摘要句本轮 48 个样本中无一变化。
frontend/tests/fixtures/report-density-fictional-reader.json是 09-23 的冻结快照、没有采集脚本、测试不拿它与现引擎比对,本轮未重生成;按现引擎它的「双重触发条目=2」会变成 4。 -
部署注意:API 的 chart 缓存(
scratch/local/api_chart_cache,15 分钟 TTL,键不含代码版本)在部署后最多 15 分钟内可能仍返回旧的 D9 结果。 -
防复发:接入原生函数时逐个核对目标名与实际参与计算的值是否一致;D9 目标集中在一个可单测的函数里。采集数据卡 golden 用
JYOTISH_API_CHART_CACHE_TTL_SECONDS=0 PYTHONHASHSEED=0:chart 缓存按排序后的键存盘,热缓存会改变字典与列表顺序(值不变),09-27 的 golden 正是冷热混合顺序,已写进采集脚本说明。 -
相关记录:BUG-1061(同函数跨层 D1+D9 判定按数字拼接比对,本轮未修;09-27 已另行修复)
-
复发自:无
-
修复版本:
f6fa367f,随7a06aa00部署 staging(run 2976)。
BUG-1061 | 双重过运跨层(D1+D9)判定按目标名里的数字比对,D9 前缀的「9」会误配也会漏配
-
状态:resolved(已部署 staging:门禁 run 2980 通过,deploy run 2982,
/api/healthgitCommit =d9c9e714;图表缓存 15 分钟后生效) -
首次发现 / 最近更新:2026-09-27 / 2026-09-27(BUG-1060 审计时发现;产品 2026-09-27 同意修复)
-
影响面:
scripts/jyotish_engine.pycmd_double_transit_pac「跨层 Double Transit」段;报告「双重过运」一节(全读固定house = 7)与普通对话数据卡的双重过运结论(scripts/consultation_native_layers.py跑 1–12 宫)。D1 / D9 / CL 各层命中结果与层内 overlap 条目不受影响。 -
现象:跨层配对用
''.join(c for c in name if c.isdigit())比较 D1 与 D9 目标名。D9 目标名都带「D9」,数字串总含「9」:D9_7宫(...)→ 「97」,永远配不上 D1 的「7宫」(本意的同宫号跨层配对从不发生);而house = 9时 D1 的「9宫(...)」→「9」会与任何{星}_D9(...)/D9_{星}(宫主)配上。实证:数据卡 golden 里一张公开盘 9 宫有一条Jupiter(D1)9宫(Cancer) + Saturn(D9)Mars_D9(Capricorn),这里的Mars_D9是上升主的 D9 星座目标,与 D1 第 9 宫不是同一目标,配上只因两边数字都是「9」。 -
触发条件:任意调用都漏配同宫号;
house = 9时误配。 -
根因:按展示用的目标名字符串抽数字判断「同一目标」,未排除层前缀。修复前实际生效的只有第二条规则(两边目标名都含 D1 第 N 宫主的星名,子串匹配),数字规则只产生过 9 宫误配。
-
修复:新增
_double_transit_target_meta(给每个 D1 / D9 目标结构化元数据:layer、kind、house、planet;名字与目标表逐字相同,{星}_D9两键重合时同名多条)与_double_transit_target_identities(把元数据映射成身份集合);跨层两段循环改为「D1 与 D9 目标身份有交集才配对」,不再解析名字。目标名(字典键)、D1 / D9 / CL 各层结果、条目格式、摘要文案都不变。 -
配对规则(逐对决定;依据
references/marriage-timing-comprehensive-techniques.mdKN Rao「木星 / 土星关联事件宫 / 宫主 / 宫主 D9 星座」与strict-workflow-router.mdDouble Transit 行;跨层条目回答「木星在一层、土星在另一层是否同时激活第 N 宫这件事的同一个征象点」):D1 目标(kind) D9 目标(kind) 决定 理由 N宫(house)D9_N宫(house)配 同一宫在两层;原数字规则的本意,修复前从不发生 N宫任何星体目标 不配 宫位与星体不是同一目标;修复前 house = 9时误配{宫主}(宫主)(house_lord){宫主}_D9(lord_d9_sign)配(保持) KN Rao「宫主 / 宫主 D9 星座」,同一颗星 {宫主}(宫主)D9_{星}(宫主)(d9_house_lord)仅当这颗星就是 D1 第 N 宫主时配(保持) 同一颗星才是同一征象点;另一颗星担任的 D9 第 N 宫主不在 KN Rao 三点里。试算过「两层第 N 宫主互配」:4 组输入 × 12 宫跨层条目 31 → 59、摘要句变 14 处,属扩大技法口径,未采纳 {上升主}(LL)/{对宫主}(对宫主)(lagna_lord / opposite_lord)指向 D1 第 N 宫主的 D9 目标 仅当这颗星就是 D1 第 N 宫主时配(保持) 与修复前子串规则等价 {上升主}(LL){上升主}_D9(lagna_lord_d9_sign)上升主不是第 N 宫主时不配(保持) 与第 N 宫无关;试算过同星互配(连同上一行的两层宫主互配一起):同一对 Saturn(LL) + Saturn_D9会在 12 个宫的调用里重复出现,跨层条目 31 → 89、摘要句变 24 处,未采纳身份集合只有两种:
('house', N)与('event_house_lord', N)(目标的星 = D1 第 N 宫主)。 -
前后对照(3 张公开 AA 盘 + 报告 golden 的虚构输入,各 12 宫,
PYTHONHASHSEED=0、chart 缓存 TTL 0):跨层条目 31 → 38(奥巴马 10 → 10、伊丽莎白·泰勒 9 → 10、乔布斯 6 → 9、虚构 6 → 9);变化只有两类:新增 8 条 D1 第 N 宫 ↔ D9 第 N 宫(泰勒 6/12 宫、乔布斯 2/6/7 宫、虚构 1/2/8 宫),删除 1 条 9 宫误配(泰勒 9 宫);所有宫主类条目 48/48 不变。d1、d9、cl、层内条目、stats48/48 不变;摘要句 7/48 变化(6 处「❌ 无」→「⚠️ 跨层间接」,泰勒 9 宫反向)。 -
验证:
- 新增
tests/test_double_transit_cross_layer_pairing.py(8 条,已进快速门CORE_PYTEST_TARGETS),三张公开 AA 盘真实引擎运行:元数据名与命令目标名逐字一致;身份规则单测;乔布斯 7 宫Saturn(D1)7宫(Pisces) + Jupiter(D9)D9_7宫(Cancer)出现;泰勒 9 宫两个命中仍在但不再配对;每条跨层条目都是同宫或同为 D1 第 N 宫主;凡同宫两层都命中必成条目(公开盘恰为 5 处);修复前的宫主类配对全部保留。在基线引擎上跑:3 failed / 4 errors(新函数不存在)/ 1 passed(快速门登记)。 - D1 / CL golden
tests/fixtures/double_transit_d1_cl_golden.json按其采集命令重生成,逐字节不变。 - 数据卡 golden
frontend/tests/fixtures/consult-evidence-card-golden.json用采集脚本(JYOTISH_API_CHART_CACHE_TTL_SECONDS=0 PYTHONHASHSEED=0)重生成,两次逐字节相同;与原文件相比只有双重过运子树变化:乔布斯结论 6 → 9(新增 2/6/7 宫同宫条目,house_7_summary由「❌ 无」变「⚠️ 跨层间接」),泰勒 9 → 10(删 9 宫误配、加 6/12 宫),奥巴马不变;family / annual / timing 三路相同。
- 新增
-
对报告 / 数据卡的影响:报告只跑 7 宫,所以只会多出「D1 7 宫 + D9 7 宫」这一类条目(公开盘中乔布斯在参考日命中),9 宫误配从未进报告;报告 golden 虚构输入 7 宫条目数不变(4)。
frontend/tests/fixtures/report-density-fictional-reader.json仍是 09-23 冻结快照,未重生成。数据卡按 12 宫汇总,结论条数与「有双重过运的宫」会按上面的对照变化(上限 24 条未触及)。 -
部署注意:chart 缓存(15 分钟 TTL,键不含代码版本)部署后最多 15 分钟内可能仍返回旧跨层条目。
-
防复发:跨层配对不解析展示用名字;目标的语义放在结构化元数据里,新增目标必须同时登记 kind,
test_meta_names_are_the_command_target_names会拦住名字与元数据不一致。 -
相关记录:BUG-1060
-
复发自:无
-
修复版本:
0eeab769,随d9c9e714部署 staging(run 2982)。
BUG-1062 | 服务角色读不到「采用日期」三列:账户保存与本人报告 worker 在自托管 PostgreSQL 上会 42501
- 状态:resolved(门禁 run 2977 真实 PostgreSQL 通过:
service_role can select every profiles column the account PATCH and the report worker read、account PATCH accepts gender alone…;全量 4204 / 0 fail / 0 skip;migrate run 2978、deploy run 2979,/api/healthgitCommit =9aa37197) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:
PATCH /api/account(并发保护读、写后 RETURNING 读)、报告 worker 的本人资料读取(loadSubjectBirth→ACCOUNT_BIRTH_SELECT),两者都经createAdminSupabaseClient()→set local role service_role。 - 现象(推断,未在真实库复现):新账户首次保存称呼 / 出生资料返回
500 {"error":"暂时无法核对现有出生资料"};本人报告 worker 读资料失败按可重试处理。 - 触发条件:
20260920020000_adopted_birth_date.sql部署后任何经服务角色读取profiles.active_birth_date / active_birth_timezone_offset / active_birth_provenance的请求。 - 根因:该迁移加了三列,只给函数授了
service_role执行权,没有列级SELECT。20260718060000起profiles对service_role是逐列授权,新列默认无权限;PostgreSQL 对未授权列返回42501 permission denied for table profiles,这句不含column,账户路由的缺列回退不会生效。开工时用迁移全集静态比对:账户 PATCH 与ACCOUNT_BIRTH_SELECT读取的列里,只有这三列没有service_role的SELECT授权。 - 修复:新增迁移
20260927020000_profile_adopted_birth_service_role_select.sql,只授SELECT(两条路径都不直接写这三列;采用 RPC 是 SECURITY DEFINER)。加法、幂等、不动数据。 - 验证:新增静态合同
frontend/tests/profile-service-role-grants-20260927.test.ts(去掉新迁移时红、加上后绿);frontend/tests/database-profile-gender.test.ts在真实 PostgreSQL 上走PATCH /api/account(门禁 DB job 跑,本机无 Docker)。 - 防复发:静态合同把「服务角色读取的 profiles 列 ⊆ 迁移里授给 service_role 的 SELECT 列」锁住,以后加列漏授权会直接红,不再依赖人记得 BUG-600 的防复发句。
- 相关记录:BUG-039、BUG-600(同类列级授权缺口第三次)、BUG-1031(worker 改走
loadSubjectBirth) - 复发自:BUG-600。当时的防复发只写成一句规则和针对
ayanamsa的单列断言,没有通用合同,所以20260920020000加列时没有拦住。 - 修复版本:
3abae68f(迁移20260927020000_profile_adopted_birth_service_role_select.sql),随9aa37197部署 staging(run 2979)。
BUG-1063 | 他人报告 worker 用服务角色读 chart_profiles,但服务角色对这张表没有任何权限
- 状态:investigating(静态证据;本轮不修,授权范围需要产品 / 安全决定)
- 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:报告 worker 为「星盘档案里的其他人」生成报告(
personal-report-worker-subject.ts→loadSubjectBirth(admin, …)→from("chart_profiles").select(CHART_SUBJECT_SELECT))。 - 现象(推断,未在真实库复现):选别人生成的报告,worker 读人物资料失败,按
subject_unavailable→profile_incomplete(可重试)处理,报告可能一直排队或最终失败退款。 - 触发条件:BUG-1031 让 worker 改走
loadSubjectBirth之后(4d801e53起)的他人报告。 - 根因(已确认的事实):
20260718100000_repair_missing_chart_profiles.sql对service_role执行revoke all on table public.chart_profiles,之后没有任何迁移把它授回;admin 客户端在自托管模式下是set local role service_role(admin-client-core.ts)。BYPASSRLS 只绕过行级策略,不给表权限。未在真实库验证 staging 是否另有手工授权。 - 修复:未修。可选做法是给
service_role授chart_profiles的逐列SELECT(只列CHART_SUBJECT_SELECT的列),或让 worker 走 SECURITY DEFINER 的只读函数;两者都扩大服务端能读到的他人出生资料范围,需要决定。 - 验证:—(建议先在 staging 库只读执行
select has_table_privilege('service_role','public.chart_profiles','select')确认) - 防复发:新增的服务角色读取路径必须有真实 PostgreSQL 用例,mock 客户端测不出表权限。
- 相关记录:BUG-1031、BUG-1062
- 复发自:无
- 修复版本:待定
BUG-1070 | 普通对话替用户和当事人说心思:反差写到人身上、是非题被改写成「你是否需要」
- 状态:resolved(代码与提示词已改、假模型与合同测试通过;真实模型输出待部署后按
docs/testing/consult-answer-the-question-20260927.md复核) - 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:普通咨询(本命 / 无出生分钟 / 申报时段共用的口气层
productConversationVoice)。 - 现象:真机首轮父母题的正文出现「她问的是结果,你要的是被听」「你自己这一端本来也是收着的」;产品转述另一轮「我问是不是 X,他答你是否需要 Y」。用户的感受是「他在揣测我为什么这样问」。
- 触发条件:问的是别人(父母、伴侣)或一句是非题;模型按开场形状找「表面 / 底下」的反差。
- 根因:三处提示词合力。① OPENER SHAPE 第 1 步「表面 A,底下 B」没有限定 A、B 必须是盘面结构,问人时最省事的反差就是当事人的行为和动机;② PERSONA 段「问题含糊时……写『这句我按『……』理解了,不对你纠正我』」鼓励先改写问题再答,「是不是 X」被改写成「你是否需要 Y」;③ 合同没有任何一条禁止替用户陈述动机 / 需求 / 感受。
- 修复(
frontend/src/mastra/product-voice.ts):反差的 A、B 限定为星 / 宫 / 大运 / 分盘,问人时写那个人在盘上对应的宫位与代表星;删「这句我按『……』理解了」,改成只有两种实质不同读法时才写一句「我按 A 答」、单义问题直接答;新增「不揣测」段(六个禁写短语 + 禁止替用户或当事人陈述动机 / 需求 / 感受)与是非题答法(第一句是 / 不是 / 看情况,第二句盘上依据);合同 Never 段加「不写人要什么,写盘把人放在哪」;Good / Bad 各加一组虚构盘面的是非题样例。frontend/docs/VOICE.md同步。 - 验证:
frontend/tests/consultation-voice-contract.test.ts新增「the voice answers the sentence asked」用例(六个禁写短语、反差限盘面、是非题答法、样例);原「这句我按『……』理解了」断言按三栏改成 doesNotMatch。tsc 0、lint 0 error。 - 防复发:禁写短语与反差限定由 voice 合同测试钉住;真机清单第 2 条检查追问的第一句。
- 相关记录:BUG-1071、BUG-1072、BUG-1073;口气定稿
84b293fb(TASK-agent-voice)。 - 复发自:无
- 修复版本:staging
9d01757f(2026-09-28 部署核对:/api/healthdeployment.gitCommit= 9d01757f,未登录/api/account= 401;Claude 验收门禁见 PROGRESS;真机 2026-09-28 产品用 deepseek-v4-flash 走清单第 1~4 步通过:首轮三节无开场行动、无重复;「是不是」首句「不是。」约 120 字无标题;「那我爸呢」短答;「完整看一下我的事业」回到开场 + 三节含行动节。第 5~8 步未走)
BUG-1071 | 追问轮重开 400 字开场 + 整套标题:一句是非题得到整份领域解读
- 状态:resolved
- 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:普通咨询本命路径(agentic 与 grounded 兜底两条),
daily_starlanguage入口不受影响。 - 现象:会话里第二个问题起(「那我爸呢」「我妈是不是不太在意我」),回答仍是口语开场加四个二级标题的完整解读,重述上一轮已经写过的宫位;用户感受是「太宽泛」。
- 触发条件:同一会话已有一轮本命解读后的任何追问。
- 根因:
jyotishInstructions「Call run-jyotish-consultation before answering every turn, including short follow-ups」、natalSpokenReportContract末句「One natal … question still uses the opener-plus-skeleton above」、每轮用户轮的natalAnswerShapeInstruction()「一次写完这四个二级标题」三处都不区分首轮与追问轮。 - 修复:
consultation-thinking-plan.ts新增followUpAnswerShapeInstruction()(≤ 200 字、第一句答问的那句、是非题先答是 / 不是、最多三处盘面事实、不写二级标题、不写行动清单、不重述上一轮、不解释用户为什么问;明确换领域或要求完整看才回首轮形状)、hasPriorAssistantAnswer(history, { summaryText })(存量历史里已有非空、且不是寒暄回复(responseKind: "smalltalk")的 assistant 正文即追问轮,会话摘要存在也算;不看问句字面。Claude 验收补修:子代理首版只看 role,「你好」的寒暄回复也存成 assistant 消息,会把其后第一个正式问题当追问;历史窗口现在把responseKind透传到 tail)与natalUserTurnShape()(daily 不变 / 追问 / 首轮);route.ts用户轮按它切指令,grounded 兜底同样切换(同一份legacyNatalInstruction);思考栏与工具上下文带followUpTurn。合同末句改为「首轮用开场 + 骨架,追问轮按用户轮里的指令」。工具仍每轮调用,扣点与结算不变(D7)。 - 验证:新文件
frontend/tests/consult-answer-the-question-20260927.test.ts:真实 Agent + 记录提示词的假模型 + 公开盘 golden,追问轮 → 写作步的提示词含「这是追问轮 / 不超过 200 字」、不含三标题指令,工具跑 1 次、模型 2 次,无标题短正文(< 160 字)整段放出并结算扣点 1 次;首轮对照用例保持开场 + 三标题。consultation-thinking-plan.test.ts覆盖hasPriorAssistantAnswer/natalUserTurnShape四种情况(空历史、只有用户轮、空白 assistant、有回答;daily 不受历史影响)。验收补修加:寒暄回复后仍是首轮形状、摘要存在即追问、空白摘要不算;consultation-session-history.test.ts加responseKind透传用例。 - 防复发:D6 硬红线——追问判定只用历史事实,禁正则识别意图(沿用 BUG-976/977);route 合同测试断言
natalUserTurnShape({ entrypoint: consultEntrypoint, history })与followUpTurn的接线。 - 相关记录:BUG-1070、BUG-1073、BUG-1053(答案由看过工具结果的步写)、BUG-976/977(禁正则)。
- 复发自:无
- 修复版本:staging
9d01757f(2026-09-28 部署核对:/api/healthdeployment.gitCommit= 9d01757f,未登录/api/account= 401;Claude 验收门禁见 PROGRESS;真机 2026-09-28 产品用 deepseek-v4-flash 走清单第 1~4 步通过:首轮三节无开场行动、无重复;「是不是」首句「不是。」约 120 字无标题;「那我爸呢」短答;「完整看一下我的事业」回到开场 + 三节含行动节。第 5~8 步未走)
BUG-1072 | 步骤栏第一节写着「先抓住你真正在问的事」
- 状态:resolved
- 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:普通咨询步骤栏(思考计划第一节
title,经 run timeline 显示)。 - 现象:用户每轮先看到系统在找他「真正在问的事」,再看到答案换了题(BUG-1070),两者互相印证成「揣测」。
- 触发条件:任何本命咨询轮。
- 根因:
natalConsultationThinkingPlan第一节title: "先抓住你真正在问的事"。 - 修复:改为
OPENER_THINKING_TITLE = "先回答你问的这件事";首轮步骤「用盘上的反差回答这个问题」,追问轮步骤「直接回答这句话」。该节没有自己的正文标题,heading 与「盘里支持这个判断的地方」共用(第一个正文 H2 出现即收口)。frontend/DESIGN.md同步。 - 验证:
consultation-run-timeline.test.ts(三栏)、consultation-thinking-plan.test.ts、consultation-agentic-runtime.test.ts(三栏,改断 title)。 - 防复发:合同测试断言 JSON 化的计划里不含「先抓住你真正在问的事」。
- 相关记录:BUG-1070、BUG-1071
- 复发自:无
- 修复版本:staging
9d01757f(2026-09-28 部署核对:/api/healthdeployment.gitCommit= 9d01757f,未登录/api/account= 401;Claude 验收门禁见 PROGRESS;真机 2026-09-28 产品用 deepseek-v4-flash 走清单第 1~4 步通过:首轮三节无开场行动、无重复;「是不是」首句「不是。」约 120 字无标题;「那我爸呢」短答;「完整看一下我的事业」回到开场 + 三节含行动节。第 5~8 步未走)
BUG-1073 | 首轮解读同一判断写三遍、三条行动给两遍(约 1,500 字)
- 状态:resolved
- 首次发现 / 最近更新:2026-09-27 / 2026-09-27
- 影响面:普通咨询本命首轮(agentic 与 grounded 兜底)。
- 现象:真机首轮父母题:口语开场(判断 + 三条行动)→
## 先回答你的问题再讲一遍母亲 / 父亲两头 →## 盘里支持这个判断的地方六行表第三次列出 →## 这周可以做的一件事三条行动与开场几乎原句重复。全文约 1,500 字,用户感受是「说了很多没多说什么」。 - 触发条件:任何本命首轮。
- 根因:2026-09-17 口气定稿(
84b293fb)把「口语开场(含三条行动)」叠在原有四标题骨架之上,没有从骨架里减掉重合部分:natalSpokenReportContract要求开场后再写## 先回答你的问题,natalAnswerShapeInstruction()「先用口语回答问题;然后一次写完这四个二级标题」,开场结尾三条行动与最后一节三条行动是同一份清单。 - 修复(产品 2026-09-27 拍板 D8):
REPORT_HEADING去掉question,正文只剩「盘里支持这个判断的地方 / 时间怎么看 / 这周可以做的一件事」;开场不带行动,行动只在最后一节出现一次;依据节只写开场没说过的,表格与正文二选一;首轮全文 ≤ 900 字。四处同步:natalSpokenReportContract、natalAnswerShapeInstruction()、skill-binding.ts的NATAL_REPORT_SKELETON、index.ts的写作句;route.tsgrounded 兜底两处内联指令合并为一份legacyNatalInstruction;consultationContinuePrompt与consultationSpokenHeadingRule("natal")的标题清单同步。申报窗口模式的## 先回答你的问题保留为WINDOW_OPEN_HEADING(不在本单范围)。shortenAnswerHeading的旧缩写保留给历史消息。 - 验证:
consultation-thinking-plan.test.ts(标题表、拆分、进度、续写、指令,三栏)、consultation-voice-contract.test.ts(合同三标题、开场无行动、≤ 900 字、只引支撑字段,三栏)、五个用## 先回答你的问题做正文夹具的测试改成从开场直接到「盘里支持」(三栏)。 - 防复发:voice 合同测试
doesNotMatch(/## 先回答你的问题/);Object.values(REPORT_HEADING).length === 3。 - 相关记录:BUG-1070、BUG-1071、BUG-1072;
84b293fb。 - 复发自:无
- 修复版本:staging
9d01757f(2026-09-28 部署核对:/api/healthdeployment.gitCommit= 9d01757f,未登录/api/account= 401;Claude 验收门禁见 PROGRESS;真机 2026-09-28 产品用 deepseek-v4-flash 走清单第 1~4 步通过:首轮三节无开场行动、无重复;「是不是」首句「不是。」约 120 字无标题;「那我爸呢」短答;「完整看一下我的事业」回到开场 + 三节含行动节。第 5~8 步未走)
BUG-1074 | 普通对话发出后到服务端分类完成之前整行空白,一两秒没有任何反馈
- 状态:resolved
- 首次发现 / 最近更新:2026-09-28 / 2026-09-28
- 影响面:
frontend/src/components/chat-message-row.tsx(awaitingClassification静默)、frontend/src/components/consultation-run-timeline.tsx(QUEUED_TIMELINE_ROW)、frontend/src/app/api/consult/route.ts(开流顺序)、frontend/src/lib/stream-first-response.ts(新)、frontend/src/lib/consultation-agent-events.ts(activityphasereceived、run.failed的request_rejected)、frontend/src/hooks/use-consultation-run.ts(事件而非响应头判寒暄与拒绝)。 - 现象:产品 2026-09-28 staging 真机(
deepseek-v4-flash):按下发送后一两秒内没有「思考中」也没有步骤栏,之后才一起出现。 - 触发条件:每一轮普通对话(agentic 运行时),无论最终是咨询还是寒暄。
- 根因:两层叠加。R1 客户端:BUG-976 寒暄快路(
539d4dae)为了让「你好」的回复不带步骤栏,让消息行在服务端首事件到达前quiet(不渲染时间线与活动面板),把 BUG-475 为「首事件前无反馈」加的「正在处理…」排队行挡掉了。R2 服务端:route.ts在建 Response 之前依次做会话读取、prepareConsultationRoute(资料 / 区间 / 时区)、reserve_consultation_usage、append_consultation_question,再用会话模型跑一次classifyConsultationTurn(3 秒 fail-open),分类完才streamAgentResponse/streamSmalltalkResponse建流,浏览器此前收不到任何字节。校正面 BUG-1047 已改「先建流、首帧确定性进度句」,普通对话没有同步。不是回归:两条各自按当时目的是对的,叠加后才成空白。 - 修复(产品 2026-09-28 拍板 D1、D2):
- D1 客户端:
quiet只由responseKind === "smalltalk"决定;排队行文案改为「收到,正在看你的问题…」(CONSULTATION_RECEIVED_LABEL),从发送那一帧起可见;服务端判定寒暄后客户端收到run.completed的responseKind: "smalltalk"即转quiet。 - D2 服务端:鉴权、请求体、会话与会话模型仍在开流前答 HTTP 状态;其余整段搬进
runTurn,agentic 运行时由streamFirstResponse先返回 NDJSON 流、首帧同步写activity(phasereceived,同一句文案),runTurn的 Response 原样中继:NDJSON 逐字节透传(咨询与寒暄流都不改),JSON 拒绝转成一条run.failedrequest_rejected(带原status、按 recovery → message → error 取的句子、原code作reason)。客户端删响应头判寒暄,request_rejected按原状态语义处理(402 开充值、4xx 回滚、session_full/session_not_consultation沿用)。legacy 运行时(CONSULTATION_AGENTIC_RUNTIME=legacy/ canary 非成员)保持原顺序,因其 text/plain 回复不能走 NDJSON。 - 观测:
consultation-stream-first-v1记first_byte阶段(请求进入到首帧写出的毫秒),不含原文。
- D1 客户端:
- 验证:
frontend/tests/consult-first-frame-and-pacing-20260928.test.tsx:① 分类挂起时首帧已写出(onFirstFrame毫秒数);② 真实streamSmalltalkResponse在流内完成,免费结算调用 1 次,事件为 activity → answer.delta → run.completed(smalltalk),无响应头;③ 402 / 409(session_full) / 503(recovery) 三种 JSON →request_rejected事件字段逐一对照,schema 只允许request_rejected带status/reason且必须带status;④ 咨询 NDJSON 首帧之后逐字节一致;断连传给内层 reader;run抛错 → 通用run.failed+ 日志回调;route 源码:开流前 / 流内两张清单、shouldUseAgenticRuntime分流、first_byte;客户端源码:无响应头、事件判寒暄、request_rejected的 402 / 状态 / code 处理。consultation-smalltalk-ui.test.tsx、chat-stream-settle-contract.test.ts、consultation-smalltalk.test.ts三处断言按三栏改。真实 PostgreSQL 的寒暄结算测试本机无 Docker 未跑(结算 RPC 与调用点未改,见BLOCKED.md)。 - 防复发:会产生模型或数据库等待的对话请求必须先建流、首字节是确定性进度句(同 BUG-1047 防复发);消息行的静默只能由服务端明确的回复类型触发,不得由「还没收到事件」触发;新的开流前失败必须能表达为
request_rejected(带 status),不得让它变成静默空回复。 - 相关记录:BUG-475、BUG-976、BUG-977、BUG-1047、BUG-1053;
TASK-consult-first-frame-and-pacing-20260928。 - 复发自:无(BUG-475 的排队行仍在,只是被 BUG-976 的静默挡住)。
- 修复版本:staging
79c5e566(2026-09-28 部署核对:/api/healthdeployment.gitCommit= 79c5e566,未登录/api/account= 401;Claude 验收门禁见 PROGRESS;真机清单 7 条与first_byte耗时待产品)。
BUG-1075 | 普通对话正文一下全出来:短回答在流结束时一帧显示,首轮前 160 字一坨
- 状态:resolved
- 首次发现 / 最近更新:2026-09-28 / 2026-09-28
- 影响面:
frontend/src/lib/stream-frame-buffer.ts(放字节奏与settle)、frontend/src/hooks/use-consultation-run.ts(结算与显示层分离)、frontend/src/lib/chat-message-view.ts(settlingChatMessageView)、frontend/src/components/chat-message-row.tsx(写出期间时间线不 live)。 - 现象:产品 2026-09-28 staging 真机:回答没有流式输出的动画,整段一下出现。
- 触发条件:追问轮(正文 ≤ 200 字、无标题)几乎必现;首轮开场前 160 字成一坨、之后一块一块跳。
- 根因:R3 服务端按步截留(BUG-1053 取答边界,
ANSWER_RELEASE_CHARS = 160):一步里的正文出现标题或满 160 字才放出,不到就扣到该步结束整段放,追问轮正文因此在流尾才到。R4 客户端stream-frame-buffer.ts:正常每帧放max(2, 积压/12)字,一坨 160 字约 12 帧吐完;settle()把剩余文字一帧全放,use-consultation-run.ts读完流立即settle()。R3 + R4 = 整段一帧出现。160 字阈值是过程话防线,产品拍板不动(D4)。 - 修复(产品 2026-09-28 拍板 D3):
- 每帧上限
STREAM_RELEASE_MAX_CHARS = 4(≈ 240 字/秒),积压超过STREAM_RELEASE_CATCHUP_CHARS = 600才回到按 1/12 追赶(重连、后台标签页)。 settle({ paced: true })按节奏写完剩余(每帧max(4, 剩余/90),即 1.5 s 内),最后一帧才settled: true,返回 Promise;无参settle()保持旧行为(一帧全放),用于停止、失败、截断,也是校正面rectification-chat-turn-run.ts两处调用的契约(它在 settle 后同步合并快照,本单不动;执行时首版把默认改成按节奏,校正面 5 条测试立刻失败,改回 opt-in);页面隐藏照旧一帧全放;写出期间dispose()延到写完。- 显示层与数据层分开:流结束后 hook 先
setStreamingReply打上settling: requestId,随后照常入库、persistSession、结算、completeConsultationInterface(它不再清掉正在写出的回复);每个写出帧只在回复仍属于本请求时更新(函数式更新,新一轮发送会接管显示),最后一帧清空。latestAssistantView/chatMessageViews在loading为假、释放文本是已存回复的严格前缀时,用同一个renderKey渲染前缀(settling: true,时间线不 live);不是前缀(被替换的回复)就直接显示全文。滚动跟随不新写:仍是useConversationScrollAnchor观察内容增长。
- 每帧上限
- 验证:
stream-frame-buffer.test.ts:160 字一坨 40 帧写完;600 以上仍 12 帧追赶;剩余 300 字的 settle 在 90 帧内、单调、最后一帧 settled;immediate 一帧;dispose 延后。consult-first-frame-and-pacing-20260928.test.tsx:前缀覆盖的 view / 行渲染(无 spinner、无「正在分析」)、非前缀 / loading / 全文不覆盖;hook 源码:完成走节奏、停止 / 截断 / 失败立即、completeConsultationInterface不清 settling、函数式更新只认本请求。既有「settle 同步放完」断言按三栏改。 - 防复发:普通对话完成路径必须走
settle({ paced: true })(源码断言只允许一处);任何流尾一次性到达的正文都要经同一个帧缓冲写出;显示层的延迟不得阻塞入库与结算;校正面若要同样的写出节奏,先改它 settle 后的同步快照合并,另立单。 - 相关记录:BUG-473、BUG-1053、BUG-1074;
TASK-consult-first-frame-and-pacing-20260928。 - 复发自:无
- 修复版本:staging
79c5e566(2026-09-28 部署核对:/api/healthdeployment.gitCommit= 79c5e566,未登录/api/account= 401;Claude 验收门禁见 PROGRESS;真机清单 7 条与first_byte耗时待产品)。
BUG-1076 | 星盘页分盘表格显示的是本命数据:切到 D9 / D10 只换了表头
- 状态:resolved(已部署 staging
6a4e312c(门禁 run 2992 / 迁移 2993 / 部署 2994 全绿,/api/healthdeployment.gitCommit=apiGitCommit=6a4e312c);真机走查待产品,清单docs/testing/chart-types-and-report-buttons-20260928.md) - 首次发现 / 最近更新:2026-09-28 / 2026-09-28
- 影响面:
frontend/src/components/chart-page/chart-vedic-tab.tsx(表格)、frontend/src/lib/chart-view-contract.ts(chartViewVargaSchema)、frontend/src/lib/chart-view-mapper.ts(buildVargas)。线上/chart自 09-15 首版起即如此。 - 现象:产品转述真机反馈「下面的表中的数据也要根据星盘不同显示不同的表中数据」。实测切到 D9 时表头写「D9 · 配偶/灵性」,每一行星座、度数、宫位、星宿仍是 D1。
- 触发条件:星盘页选任意非 D1 分盘。
- 根因:表格
<tbody>渲染的是view.vedic.ascendant/view.vedic.planets(planetRows(chart)只读/api/chart本命数据),只有<caption>用了varga.id;契约里分盘只有画盘用的chart,没有表格行。原测试只断言 caption 随 chip 变,没断言行内容。 - 修复:契约给 D2 起的分盘加
ascendant与rows(分盘星座、分盘内度数、从分盘上升数的宫位、本命星座、庙旺、同位 / Vargottama、本命顺逆);分盘表改六列「行星 · 分盘星座 · 宫位 · 本命星座 · 庙旺 · 同位」,删掉星宿 / 宿主 / 足(那是本命经度的属性,放进分盘是错数据)。庙旺用frontend/src/lib/vedic-chart-tables.json,表与jyotish_engine的EXALTATION / DEBILITATION / SIGN_LORDS和_get_dignity_level的判定顺序由tests/test_chart_page_vedic_tables_contract.py逐格钉住(不在 Python 响应里加字段,是为了不改被报告、咨询、golden 共用的varga_full形状)。D1 表不变。 - 验证:
frontend/tests/chart-varga-table.test.tsx先写先红(基线5dc977ef上 D9 太阳行是「双子 1°32′」,断言要「天秤」),修后绿;D1 表与基线渲染逐字节相同(fixtures/chart-d1-table-baseline.html由基线提交渲染);D9 土星「自宫 + 同位」、太阳「落陷」;每张非 D1 分盘 9 行。chart-view-varga-dignity.test.ts钉判定顺序。 - 防复发:分盘表的行必须来自该分盘自己的
rows;新增盘型的测试必须断言行内容,不能只断言标题。 - 相关记录:BUG-704~706(星盘页首版)、BUG-1016(骨架);
TASK-chart-types-and-report-buttons-20260928T3。 - 复发自:无
- 修复版本:
ab6ee94e,随6a4e312c部署 staging。
BUG-1077 | /api/bhava_chalit 缺 MC 时 Sripati 静默退化成等宫
- 状态:resolved(已部署 staging
6a4e312c(门禁 run 2992 / 迁移 2993 / 部署 2994 全绿,/api/healthdeployment.gitCommit=apiGitCommit=6a4e312c);线上 Bhava 耗时与 429 待观察) - 首次发现 / 最近更新:2026-09-28 / 2026-09-28
- 影响面:
scripts/jyotish_api_server.py_compute_bhava_chalit(直接调用该端点的任何调用方)。报告链jyotish_engine._build_natal_foundation_modules自己用swe.houses_ex(..., FLG_SIDEREAL)取 MC,不受影响;CLIbhava_chalit.cmd_bhava_chalit也自己算 MC,不受影响。 - 现象:不传
mc_lon时,12 宫span_degrees全是 30.0,Bhava 宫与星座宫完全相同,「换宫」恒为 0。即使请求里带齐出生年月日时与经纬也一样——端点只给 placidus / koch 算 JD。 - 触发条件:
house_system为 sripati(默认)或 porphyry,且请求不带mc_lon。 - 根因:
mc_lon = self._normalize_degree(body, 'mc_lon', (asc_lon + 270) % 360):缺省 MC 取上升减 90°,四个象限各恰好 90°,三等分后就是等宫。 - 修复:新模块
scripts/bhava_chalit_mc.pyresolve_bhava_mc_lon:显式mc_lon优先;否则有出生时刻与地点就用swe.houses_ex(jd, lat, lon, b'R', FLG_SWIEPH | FLG_SIDEREAL)在请求岁差下取恒星黄道 MC(与报告链同一调用);再否则 sripati / porphyry 直接 400,不猜。不需要 MC 的宫制(equal / whole_sign / placidus / koch)行为不变。端点只改了既有一行的位置(净增 0 行,类方法数不变)。星盘页 Bhava 层另有一道防线:mapBhava遇到宫制回退、缺 MC、或 MC 恰等于asc + 270一律返回 unavailable,不画等宫。 - 验证:
tests/test_api_server_security.py三条新测试先写先红(基线上「缺 MC 与出生时刻应 400」未抛出、「带出生时刻时跨度不全为 30」得到[30.0, 30.0, …]),修后绿;MC 与独立的houses_ex调用差 < 1e-3°,同一调用的上升与引擎本命上升差 < 0.01°;equal 宫制无出生时刻仍可用。真实响应存为frontend/tests/fixtures/chart-view-bhava-golden.json(星盘页 golden 的虚构盘:太阳、水星、土星换宫)。frontend/tests/chart-bhava.test.tsx断言占位 MC 与宫制回退都不出盘。 - 防复发:任何取 Bhava 宫头的新调用方都经
bhava_chalit_mc.resolve_bhava_mc_lon,不得再给 MC 写几何默认值。 - 验收补丁(Claude 2026-09-28):修复后技法浏览器
_technique_example_payloads里的/api/bhava_chalit样例只带星体与上升、无出生时刻,会被新规则拒为 400;样例改为{**base, **birth, …}(改既有行,api_server 净增 0),新增test_technique_example_bhava_chalit_runs_with_a_real_mc。 - 相关记录:BUG-320(Bhava 进换升表);
TASK-chart-types-and-report-buttons-20260928T5。 - 复发自:无
- 修复版本:
c35e0195+ 验收补丁4189750c,随6a4e312c部署 staging。
BUG-1078 | 我的报告:实心按钮标签继承正文色(浅色黑字深红底、深色浅字浅珊瑚底),禁用态半透明
- 状态:resolved(已部署 staging
6a4e312c(门禁 run 2992 / 迁移 2993 / 部署 2994 全绿,/api/healthdeployment.gitCommit=apiGitCommit=6a4e312c);真机深浅色走查待产品,清单docs/testing/ayanamsa-report-buttons-20260928.md) - 首次发现 / 最近更新:2026-09-28 / 2026-09-28
- 影响面:「我的报告」列表(
.report-center-*)、阅读页与分块导出对话框(.personal-report-reader、.report-export-*)、报告 loading / 状态 / error / not-found(.personal-report-state)上渲染为<button>的实心Button(「导出选中」「继续等待」等)与所有禁用按钮。同样的根因作用于全站所有渲染为<button>的实心Button,本单按产品决定只修报告范围,其它页面未修(见防复发)。 - 现象:产品转述真机反馈:导出按钮黑字配深红色背景,看着不舒服。
- 触发条件:浅色主题打开分块导出,「导出选中」是近黑字 #1d1d1f 在 #a9583e 上(3.33:1);深色主题是 #f2f0ea 在 #d78064 上(2.57:1)。未勾选时按钮再被整体淡到 45%。套餐选中态用
--color-action-soft,深色下是 #3a2620 深红褐。 - 根因:
globals.css顶部未分层的button { color: inherit; }与button:disabled { opacity: .45; }压过 Tailwind v4 放在@layer utilities里的text-primary-foreground与disabled:opacity-50(未分层声明总是赢分层声明,与选择器权重无关)。所以variant="default"的<button>标签颜色一直是继承的正文色,而不是设计的--color-on-dark。<Button render={<Link/>}>渲染为<a>,不受影响,所以「去登录」这类链接按钮一直是对的。实证:本地next build产出的 CSS + 无头 Chrome 取getComputedStyle,基线「导出选中 (3)」浅色color: rgb(29, 29, 31)、深色rgb(242, 240, 234)(样张与数值见 PROGRESS)。任务书起初判断为「深色主题下近黑字」,按实测更正。 - 修复(产品 2026-09-28 拍板:只改我的报告,不改全站):
globals.css新增一段只挂在四个报告根类下的按钮规则,实心按钮([data-slot="button"][class~="bg-primary"])自己声明底色与标签色:浅色 #fbfaf7 on #a9583e(4.85:1,hover 同原bg-primary/80);深色--color-ink#f2f0ea on--color-action-on-dark#8f4a33(5.77:1),hover 混 6% ink(5.1:1)。禁用态(任何变体)=--color-canvas-strong底 +--color-ink-tertiary字、opacity: 1。套餐选中态改--color-canvas-muted底 +--color-ink字 +--color-ink-secondary边。button.tsx、全局button规则与全部主题 token 未改。页头「生成报告」(SecondaryPageShell的actions槽,在四个根类之外)加报告自有类report-center-generate并入禁用规则,提交中「正在创建…」同样是灰底、不淡出;SecondaryPageShell未改。 - 验证:
frontend/tests/report-buttons-scope-20260928.test.ts(6 条,含「生成报告」挂钩):button.tsx变体 / 禁用淡出 /data-slot原样;三处主题块相关 token 原值;所有[data-slot="button"]覆盖规则都以报告作用域开头、两条深色路径各一处、样式表不定义.bg-primary;前后对比度(前 < 4.5、后 ≥ 4.5)且覆盖规则自己设color;套餐选中态不再用 action-soft。class-name-definition-contract、dark-theme-contract、design-token-contract、report-export-drawer仍绿。无头 Chrome 计算样式:新版浅色「导出选中 (3)」rgb(251, 250, 247)onrgb(169, 88, 62),深色rgb(242, 240, 234)onrgb(143, 74, 51);禁用 opacity 1;报告范围外对照按钮与基线一致。 - 防复发:报告页按钮颜色只在这段作用域规则里改。全站其它实心
<button>仍有同一问题(浅色 3.33:1、深色 2.57:1):要修应另立单,在全局button { color: inherit; }或Button组件层面解决,并逐页截图;不要把这段报告作用域规则挪成全局。 - 相关记录:BUG-738(action 两档对比度)、BUG-1018 / 1019(分块导出);
TASK-chart-types-and-report-buttons-20260928T7。 - 复发自:无
- 修复版本:
873fac8a+36fae8fa(分支原 SHA9f5b4f50/72b62759),随6a4e312c部署 staging。
BUG-1079 | 全站实心按钮:层外 button { color: inherit } 压掉组件文字色,.button-primary 浅色 3.14:1,禁用态整体淡化
- 状态:resolved(已部署 staging
dc640931:门禁 run 2995 / 迁移 2996 / 部署 2997 全绿,/api/healthgitCommit=apiGitCommit=dc640931;harness 对 staging 线上 CSS 复测:实心浅 4.85 / 深 5.77,禁用灰 opacity 1;真机清单docs/testing/site-button-contrast-20260928.md待产品) - 首次发现 / 最近更新:2026-09-28 / 2026-09-28
- 影响面:用户页面全部渲染为
<button>的实心<Button>(点数 / 订阅面板、星历、生时校正对话、输入框发送 / 停止键;报告页已由 BUG-1078 局部修过)、全部.button-primary(登录、首次引导、人物档案、首页、生时校正各卡、兑换码付费墙、星盘资料表单、首页错误屏,共 30 个)、日期选择器选中日;全部实心按钮的禁用态。后台管理路由不加载globals.css(只有字体、antd/dist/reset.css与admin.css),其中 52 个 shadcn<Button>本来就没有任何 Tailwind 样式,不在本条影响面内(见下「另发现」)。 - 现象:产品在 BUG-1078 验收后要求全站扫一遍。Claude 用 AST 列出 213 个按钮,并用 staging 线上 CSS 在无头 Chrome 实测:实心
<Button>浅色 #1d1d1f 在 #a9583e 上 3.33:1、深色 #f2f0ea 在 #d78064 上 2.57:1;.button-primary与发送键浅色 #fbfaf7 在 #cc785c 上 3.14:1;禁用态 45% 透明,1.49–2.30:1。 - 触发条件:任意用户页面上渲染为
<button>的实心主按钮;禁用时更明显。 - 根因:①
globals.css顶部未分层的button { color: inherit; }与button:disabled { cursor: default; opacity: .45; }压过 Tailwind v4@layer utilities里的text-primary-foreground/disabled:opacity-50(未分层声明总是赢分层声明,与权重无关);Tailwind preflight 在@layer base里本来就有button { color: inherit },这条是重复的。②.button-primary与发送 / 停止键底色用品牌珊瑚--color-action-strong(#cc785c),字用--color-on-dark(深色主题翻成 #241f1c),浅色 3.14:1 是 DESIGN 曾接受的已知缺口。③ 两套实心按钮各自取色,同一个主操作在不同页面两种颜色。④ BUG-1078 只在报告作用域覆盖,根因 ① 未动,所以其余 60 多处同病没被拦住。 - 修复(产品 2026-09-28 选 A:全站一种实心按钮,推翻 DESIGN「已知缺口」):删掉层外
button { color: inherit; };禁用淡化改为button:disabled:where(:not([data-slot="button"]))(原权重不变,只作用于原生<button>),<Button>的禁用由自身工具类决定,基础淡化改disabled:opacity-45(与旧全局值相同,非实心按钮外观不变)。新增按钮专用 token 对--color-action-fill/--color-on-action-fill(浅 #a9583e / #fbfaf7,深 #8f4a33 / #f2f0ea)及 hover、disabled 两对,登记进@theme inline;<Button>默认变体、.button-primary、输入框发送 / 停止键、calendar.tsx选中日改用它;实心禁用 =--color-canvas-strong底 +--color-ink-tertiary字、opacity 1。.danger-primary显式保留--color-on-dark标签色(浅 7.13、深 6.04)。--color-primary映射不动(text-primary图标仍用它)。删 BUG-1078 报告作用域覆盖与.report-center-generate、birth-date-picker.tsx选中日text-primary-foreground!补丁。 - 验证:
docs/testing/button-contrast-harness.cjs(读源码类名 + 新 build CSS、无头 Chrome、深浅两主题,含悬停)前后对照:全部实心按钮 ≥ 4.5(浅 4.85、深 5.77、悬停 6.52 / 5.10),实心禁用 opacity 1 灰底;描边 / 幽灵 / 次要按钮及其禁用、悬停数值逐项相同;.danger-primary7.13 / 6.04 不变。frontend/tests/report-buttons-scope-20260928.test.ts改为全站合同(6 条);session-conversation-layout.test.ts停止键断言改为新 token(三栏见 PROGRESS);dark-theme-contract通过。 - 防复发:不得再写层外的
button元素颜色 / 禁用规则;实心主按钮只读--color-action-fill这组 token;harness 随仓库留在docs/testing/,改按钮样式后跑一遍(只读新 build 的 CSS,用户页默认跳过 antd reset)。 - 另发现(不在本条修复内):① 后台管理路由不加载 Tailwind,52 个 shadcn
<Button>渲染为 antd reset 下的无样式按钮;② 层外button, input, textarea, select { font: inherit; }同理压掉<Button>的text-sm,用户页<Button>实际是 16px。两条都写进BLOCKED.md待产品决定是否立单。 - 相关记录:BUG-1078(报告页局部修复,本条取代其覆盖)、BUG-738(action 两档对比度);
TASK-site-button-contrast-20260928。 - 复发自:BUG-1078(同一根因,1078 只修了报告作用域)
- 修复版本:
12aac87b+ 文档fa58df16,随dc640931部署 staging。
BUG-1080 | 新账号首次建盘:看不到群星过场,只看到「解锁完整咨询」兑换弹窗
- 状态:investigating(修复已在分支
codex/birth-sky-pacing-20260928,待部署 staging 后真机确认;过场没出现的确切原因未在真实环境取证) - 首次发现 / 最近更新:2026-09-28 / 2026-09-28
- 影响面:0 点数、无订阅的新账号,首页 onboarding 保存本人出生地之后的那一刻(
TASK-birth-sky-followup-20260928F4 群星过场)。 - 现象:产品在 staging 真机新建账号、走完 onboarding 后,没有看到群星汇聚过场,只看到「解锁完整咨询」兑换弹窗。
- 触发条件:
saveOnboardingPlace成功 →onOwnBirthPlaceSaved开始为过场取数(3 秒预算);同一刻首页变成 starter home,page.tsx里「starterHomeVisible且 0 点数且无订阅」的 effect 立即打开兑换弹窗,modalOpen使SidebarInset变inert,而过场<dialog>渲染在Home()里、也就是这棵 inert 子树内部。 - 已排除 / 已确认:
- 「兑换弹窗盖住过场」在 Chrome 里不成立(已用无头 Chrome 151 最小复现排除):
inert容器里先放兑换弹层(portal 到 body、z-index 40、输入框已聚焦),再对容器内的<dialog>调showModal():不抛错、:modal为真,截图里过场在最上层完整可见,elementFromPoint命中过场画布,CDP 真实鼠标按下与 Esc 都送达过场(pointerdown、cancel事件都触发)。与 BUG-968 不同:BUG-968 的账户弹窗是普通div,不进顶层,所以会被inert锁住;<dialog>.showModal()进顶层。复现页与截图:scratchpad/polish/bug1080/repro.html、inert.png、plain.png(会话 scratchpad,不进仓库)。Safari 未能验证。 - 仍可能的原因(未取证):① 3 秒预算内没拿齐数据(
/api/birth-sky冷启动、宋体切片等待最多 1.5 秒、过场 chunk 下载),过场被静默跳过,页面上只剩兑换弹窗;② Safari / iOS 对 inert 子树内顶层<dialog>的处理与 Chrome 不同;③ 产品当时的账号不是在首页 onboarding 里保存出生地(例如从设置或星盘档案补资料),本来就不触发。
- 「兑换弹窗盖住过场」在 Chrome 里不成立(已用无头 Chrome 151 最小复现排除):
- 修复(
TASK-birth-sky-polish-20260928P4,不依赖根因结论):① 删掉「进入 starter home 就弹兑换弹窗」的 effect;兑换弹窗改为本页第一次因点数不足发不出消息(前置余额检查或 402)时弹,之后照旧打开 billing(lib/redeem-paywall-once.ts+useConsultationRun),草稿留在输入框。建盘后首页不再有任何自动弹窗与过场抢位置。② 过场<dialog>改为createPortal(…, document.body),挂在SidebarInset之外,与账户弹窗、兑换弹窗同一做法,任何浏览器都不再受inert影响。③ 过场放慢到约 6.5 秒(产品同批要求)。 - 验证:
frontend/tests/birth-sky-polish-20260928.test.ts(兑换弹窗只由被拒的发送打开、第一次兑换弹窗第二次 billing、UI 预览不弹、402 后草稿退回、过场 portal 到 body);membership-page.test.ts改写的时机断言;全量前端测试失败清单与基线逐条相同。无头 Chrome 最小复现见上。 - 防复发:首页不得在「本人刚保存出生地」这一刻自动弹出任何弹窗;顶层
<dialog>也要 portal 到SidebarInset之外;过场是否真的放了,部署后按docs/testing/birth-sky-cover-checklist.md新账号条目真机走一遍(若仍看不到过场,优先查 3 秒预算内/api/birth-sky的耗时)。 - 相关记录:BUG-968(
SidebarInsetinert 与 portal 约定)、BUG-434(兑换弹窗样式);TASK-birth-sky-followup-20260928F4、TASK-birth-sky-polish-20260928P4。 - 修复版本:分支
codex/birth-sky-pacing-20260928(本条所在提交),未部署。
BUG-1081 | 全站没有任何地方能改本人的出生资料(星盘档案只给他人「编辑」)
- 状态:resolved(分支修复与回归测试已完成;真机待 staging 部署后按
docs/testing/self-edit-avatar-menu-20260928.md走) - 首次发现 / 最近更新:2026-09-28 / 2026-09-29
- 影响面:所有账户的本人出生资料(日期、时间与来源、地点、岁差、称呼);
/people星盘档案本人详情。 - 现象:产品在 staging 真机反馈「星盘档案里没法修改个人档案的信息」。本人详情只写「本人不能删除」,没有「编辑」;设置 → 个人资料只能改头像与性别;生时校正文案「请先到资料里改出生时间」指向一个不存在的入口。
- 触发条件:任何已完成建档的用户想改自己的出生资料。
- 根因:
ab6c55f8(星盘档案 P1,2026-09-25)用/people取代设置弹窗里的星盘资料块,只给role === "other"做了编辑视图,people-page.tsx注释写「本人 has no edit view here」;首页那块表单chart-library-panel.tsx已无引用,随d08ffdc0删除。首页 hook 的saveProfile从此没有任何 UI 调用。任务书 D1 写的是「统一列表、统一操作;户主不可删」,并没有「本人不可编辑」。 - 为什么没拦住:合同测试只断言「本人不能删除」与「他人有编辑」,没有任何测试断言「本人至少有一个出生资料编辑入口」;
settings-mvp-contract反而把「/people 不伪造已移除的本人编辑」写成了断言。 - 修复(
TASK-self-edit-avatar-menu-20260928S1):本人详情也有「编辑」,编辑视图与他人同一张ChartProfileForm(称呼、出生资料、岁差、性别),标题「编辑我的资料」,没有删除区。保存抽成lib/self-profile-save.ts:出生资料走PATCH /api/account(selfProfilePatchBody,带birth_time_source、不带gender),首页 onboarding 的persistProfile改用同一函数;性别变了再单独 PATCH{ gender }。保存后同步丢掉旧资料的副本:layout 的账户(setAccount)、首页 warm snapshot 与今日卡、星盘缓存(pinChartSnapshotIdentity)、星历缓存、人物目录。删除首页 hook 与page.tsx里无人调用的saveProfile。 - 验证:
frontend/tests/self-edit-people-20260928.test.tsx(本人详情有编辑、编辑视图无删除、PATCH 载荷带时间来源不带性别、失败透出路由文案、/people本人保存不走 chart-profiles 且清理缓存、守卫:本人至少有一个编辑入口);改写断言见docs/tasks/PROGRESS-self-edit-avatar-menu-20260928.md。 - 防复发:守卫测试锁住「本人出生资料至少有一个编辑入口,且走共享的
saveSelfProfile」;取代某个入口的改动必须在任务书里写明新入口。 - 相关记录:BUG-1030/1039(星盘档案)、BUG-018(账户出生资料声明与状态同写)。
- 修复版本:分支
codex/self-edit-avatar-menu-20260928,未部署。
BUG-1082 | 天空封面底部的出生城市对中国出生地一律印不出来
- 状态:resolved(代码与回归测试;真机待部署后按
docs/testing/birth-sky-cover-checklist.md城市条目复核) - 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:星盘页「那一刻的天空」图片、报告封面、首次建盘过场底部的 caption(
TASK-birth-sky-polish-20260928P2,stagingadbb2054起)。 - 用户现象:中国出生地只印「日期 时刻」,没有「· 城市」。
- 触发条件:出生地来自中国地点库(存储标签形如
中国 · 省 · 市 · 区)。 - 根因:
lib/birth-sky/caption.tsbirthSkyCity从第一段开始按「省 / 直辖市」识别,而存储标签由geoapify-location-service.ts生成、第一段固定是「中国」,于是每次都判为「取不到可靠城市」。测试夹具手写成不带「中国」的省 · 市 · 区,与真实存储格式不一致,所以单测与样张都显示了城市。执行方在下一单(TASK-self-edit-avatar-menu-20260928)读people-archive-view.ts时发现;Claude 验收 polish 时也只看了夹具样张,未对照真实标签格式。 - 修复:识别前先去掉开头的「中国」段,其余规则不变。
- 验证:
frontend/tests/birth-sky-caption.test.ts新增「stored China labels lead with 「中国 · 」」一条(省市区、省市、直辖市、只到省、只有「中国」、完整 caption);birth-sky-caption/birth-sky-route/birth-sky-draw22 pass。 - 防复发:解析地点标签的测试夹具必须取自
geoapify-location-service.ts的真实生成格式(AGENTS §7.4 golden 原则);新增的地名解析先对照people-archive-view.tspeopleCityLabel已有做法。 - 相关记录:BUG-1080
- 复发自:无
- 修复版本:
codex/self-edit-avatar-menu-20260928第 4 个提交
BUG-1083 | 手机上星盘页被撑出屏幕,北印度盘右侧与下方被裁掉
- 状态:resolved(CSS 修复 + 回归测试 + 无头 Chrome 390×844 修复前后实测;真机待部署后按
docs/testing/mobile-chart-confirmed-edit-20260929.md复核) - 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
/chart「星盘」Tab 下全部盘型(本命、D9、月亮、Bhava、D10 实测);桌面 1280 宽下右侧也被裁(参数卡被切、「≡」盘型按钮看不到)。大运 / 基础信息 / 西洋盘三个 Tab 在 390 宽下修复前就不溢出。 - 用户现象:iPhone 上星盘页的北印度盘远大于屏幕,只能看到左上一块,盘下的参数表与行星表看不到;电脑上出生地一行在右边被截断。
- 触发条件:任何视口宽度小于星盘页正文最小内容宽度(盘型条 23 个 chip 不折行 +
.chart-page-planet-table { min-width: 34rem })。 - 根因:
.secondary-page是display: grid,没有grid-template-columns,隐式列是auto;它的子项.chart-page-body保持默认min-width: auto,于是这一列按正文的最小内容宽度定宽。无头 Chrome(CDP 模拟 390×844、真实组件 + golden 夹具 SSR + 构建产物 CSS)逐层量:.secondary-page390px,.chart-page-body1101px,其下.chart-page-panel/.chart-page-vedic/.chart-page-vedic-layout/.chart-page-vedic-stage1069px,盘面 SVG 1043px——第一个超宽的祖先是.chart-page-body。内层.chart-page-planet-table-wrap、.chart-page-varga-table-block、.chart-page-type-strip各自的min-width: 0与横向滚动都在,但撑宽发生在它们之外的这一层。外层面板overflow-x: hidden把溢出直接裁掉而不是出现横向滚动条,所以页面看起来"只是很大"。 - 修复:
.secondary-page加grid-template-columns: minmax(0, 1fr),让唯一一列可以小于内容宽度;宽内容继续在自己的盒子里横滚。修在四个次级页共用的这一层,而不是只给.chart-page-body补min-width: 0,同一容器里的其他页面不再有同类风险。 - 验证:修复后 390×844 下本命 / D9 / Bhava / 月亮 / D10 五张:
documentElement.scrollWidth= 390,无任一祖先超宽,盘面 332px;1280×900 下两栏布局不变,盘面 549 → 480px、参数卡与「≡」完整可见。回归测试frontend/tests/chart-page-mobile-width-20260929.test.ts(锁.secondary-page的minmax(0, 1fr),以及内层表格 / 盘型条的横向滚动)。截图见docs/tasks/PROGRESS-mobile-chart-confirmed-edit-20260929.md。 - 防复发:次级页里的新 grid / flex 容器沿用「子项可收缩」规则;验收星盘页类改动时在 390 宽下量
scrollWidth与盘面宽度,不能只看桌面。 - 相关记录:BUG-1076~1078(盘型条与八列行星表把最小内容宽度推大,是本次的放大因素)
- 复发自:无
- 修复版本:
codex/mobile-chart-confirmed-edit-20260929第 1 个提交
BUG-1084 | 生时校正开场后一路邀请补经历,补来的经历不收窄范围
- 状态:resolved(代码与回归测试;真机待部署后按
docs/testing/rectification-futile-collect-stop-20260929.md复核) - 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
v9/collection-question-pool.ts(guidedWindowPool/guidedRetryPool/targetedCollectPool/rangeNarrowHint)、core/rectification-decision.ts::mayDeliverOnPrecision(经cardHoldingLinesExhausted/targetedCollectExhausted)、user-copy.ts::rangeDeliveryIndistinct、v9/step-state.ts、rectification-board-model.ts(右栏阶段句)、rectification-agentic-chat.tsx(输入框占位)、Skill §6。 - 用户现象:产品 staging 真机,31 分钟窗口,开场说 12 件带年份的经历、之后答约 10 张卡,界面「已对照 22 件」,范围始终是整窗;右栏和追问一直请用户再补经历。
- 触发条件:训练门已开后,交付卡仍等七条线定向题及其重问问完;采集来的经历只进引擎先验。
- 根因:打字经历只经
_relative_support正比例先验(BUG-560),差距 1–2 分,够不到淘汰线 8 分;buildInferenceState对classified_from === "evidence"的「是」跳过计分。能动分的只有点选卡 ±2。流程却按 09-16 / 09-26 决策继续索要经历。 - 已确认事实(虚构出生日 12 例,±15 分钟,事件形状同真机):12 件经历后 11/12 例范围 = 整窗;先验 9–11 分;原始分差一倍的例子换算后只差 8、剪 2 分钟;选择题全按真值作答后宽度中位 17 分钟,≤10 分钟 2/12。
- 修复(产品 2026-09-29 D1):
COLLECT_AFTER_TRAINING_GATE = false+spokenCollectClosed(evidence):训练门开后引导窗口、跳过线重问、定向七条线三个池返回空,于是它们不再被问、也不再挡卡(targetedCollectExhausted/cardHoldingLinesExhausted门开后为真),点选卡问完即交付。门开后的收窄提示只剩「现在还剩 … 个候选。」;右栏阶段句按precision_stage.current取前端文案(冻结文件refinement_packet.py不改);步骤条、输入框占位去掉补经历邀请。门未开时采集照旧;对照件(holdout)验证线不在 D1 范围。 - 离线回放(硬红线 1,2026-09-29 修订为「真值在区间每格不降」):
scripts/research/futile_collect_stop_replay.py,v4 开放集六格真值在区间全部 20/20;宽度中位 ±10 truth 14→15、±30 两格 34→33、±60 两格 53→56,truth / opposite 注入宽度几乎相同,判为无方向信息。 - 验证:
frontend/tests/rectification-futile-collect-stop-20260929.test.ts(七个领域表驱动:门开 → 三个池空、定向题 null、挡卡线视为问完;门关 → 重问池仍在、仍采集;收窄提示、右栏、步骤条、输入框无邀请);既有断言 20 个文件按三栏改(见 PROGRESS)。 - 防复发:门开后不得新增任何口述补事题源;要恢复只能改
COLLECT_AFTER_TRAINING_GATE一处,并先过TASK-rectification-typed-event-scoring-research-20260929的研究门。 - 相关记录:BUG-560(blocked)、BUG-654、BUG-747~752、BUG-689、BUG-1089
- 复发自:无(BUG-560 的用户可见后果,旧记录只处理了尺度,未处理流程)
- 修复版本:分支
codex/rectification-futile-collect-stop-20260929,未部署
BUG-1085 | 生时校正交付正文「对照了 N 件经历,事件吻合率 P%」误导
- 状态:resolved(代码与回归测试;真实模型正文待部署后复核)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
user-copy.ts::deliveryTurnNarration、mastra/rectification-v9-tools.ts::batchRangeAfterRescore、frontend/src/mastra/agentic-rectification.ts交付三句提示、Skill §7。 - 用户现象:范围一分没缩,正文写「对照了 22 件经历,事件吻合率 95%」,读起来像很有把握。
- 根因:N 含不参与收窄的口述经历(BUG-1084);P 是经历吻合度,与候选能否分开无关。
- 修复:第二句改「用了 M 道选择题」(
scoredChoiceCount:classified_from === "choice"且不是「说不好」),M=0 时省略;范围不比开场窄时加「这个窗口按现在的方法缩不下去。」。range_after_rescore去event_fit_percent,加choice_count/range_not_narrowed;Agent 提示与 Skill 10.0.32 同步。区分不开句改「这几个时刻按现在的方法区分不开。」 - 验证:
rectification-futile-collect-stop-20260929.test.ts交付正文与提示 / Skill 文本两条;agent-voice-copy-contract、rectification-delivery-ui-simplify-20260908、rectification-adopt-narration-20260904断言按三栏改。 - 防复发:交付气泡不得出现「吻合率」「对照了」「补一件」「再说一件」;吻合率只留在折叠验证报告里。
- 相关记录:BUG-1084、BUG-1055
- 复发自:无
- 修复版本:分支
codex/rectification-futile-collect-stop-20260929,未部署
BUG-1086 | 答题后中间一段被排除,旁白仍说「范围没变」
- 状态:resolved
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
core/candidate-window.ts::unionWindowIntervals、core/credible-range.ts、v9/probe-explain.ts、v9/choice-action.ts::composeChoiceNarration、v9/answer-choice.ts。 - 用户现象:中间某段已被排除,旁白仍是「已记录,范围没变;X 领先,Y 落后」。
- 根因(执行时更正任务书):
unionWindowIntervals按窗口段合并成首尾包络,同一段内中间簇掉出 8 分线时元组不变,explainRangeChange判「范围没变」;只有跨午夜多段时元组才为null(那时同样落到「范围没变 + 领先落后」分支)。诊断时写的「多段 → null」只覆盖后一种。 - 修复:
stillValidCandidates+excludedClusterRanges(before, after):答题前在范围内、答题后不在的簇(相邻合并)即被排除的段;首尾不变但有排除时旁白写「已记录,排除了 HH:MM–HH:MM。」;无排除时仍是「范围没变」。 - 验证:
rectification-futile-collect-stop-20260929.test.ts「an interior cluster leaving the range is narrated as 排除了, never 范围没变」(首尾不变、元组为 null、无排除三种)。 - 防复发:旁白判断「变没变」不得只比首尾元组;以候选集合为准。
- 相关记录:BUG-569、BUG-634
- 复发自:BUG-634 的旁白分支未覆盖「首尾不变、中间少一段」
- 修复版本:分支
codex/rectification-futile-collect-stop-20260929,未部署
BUG-1087 | 交付轮同时带出一道采集题(「再问下去也分不开了」+「身体这边再问一次」)
- 状态:resolved(D1 后路径不可达,回归测试保留)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
v9/answer-choice.ts::persistExhaustionCollect、persistNextInterviewIfIdle、v9/method-followup.ts::targetedCollectFollowup(经guidedRetryPool)、core/rectification-decision.ts::classifyStop。 - 用户现象:同一轮既显示交付状态与「再问下去也分不开了」,又在消息里挂一道健康线重问题。
- 触发条件:训练门已开;某条定向线被跳过一次(重问池里有它);多数点选答「记不清 / 跳过」→
classifyStop给user_uncertainty_too_high。 - 根因:
user_uncertainty_too_high直接deliverRange,不经mayDeliverOnPrecision(不看重问是否问完),决策与界面都是交付态;persistNextInterviewIfIdle走交付 / 耗尽分支进persistExhaustionCollect,它的下一问兜底是targetedCollectFollowup(不传窗口,但含跳过线重问池),于是把collect:targeted:health_pressure:retry写成活动焦点、挂在这一轮上。09-26 D2 只把引导窗口从交付后的计划里拿掉(method-followup.ts注释「Retry and targeted lines are unchanged」),重问与定向线仍会跟在交付卡下面。 - 修复:D1 后训练门开时重问池、定向池为空,这条路径不再可达;未另改交付分支。
- 验证:
rectification-futile-collect-stop-20260929.test.ts「incident shape: a delivering turn does not also ask the skipped-line re-ask」——同一用例在基线56722063上红(写入焦点collect:targeted:health_pressure:retry,旁白「…再对照几件经历会更准」),本分支绿。 - 防复发:交付态(
sessionOutcomeAllowsDelivery)下不得写采集焦点;若将来恢复门开后采集(COLLECT_AFTER_TRAINING_GATE),必须同时让交付分支不走targetedCollectFollowup。 - 相关记录:BUG-652、BUG-1084、BUG-503
- 复发自:无
- 修复版本:分支
codex/rectification-futile-collect-stop-20260929,未部署
BUG-1088 | 学业质量题一律问「那次上大学」,只记得年份的经历显示成「YYYY 年 1 月」
- 状态:resolved(代码 + 回归测试 + 重新冻结;未部署,真机待合入 staging 后复核)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
scripts/rectification/event_probes.py::_quality_user_meaning、_display_date_label(冻结计分文件,ERR-110)。 - 用户现象:用户说的是某年一次艺考,卡片问「YYYY 年 1 月那次上大学,更接近如愿、将就调剂…」。
- 触发条件:学业经历类型为 education_start / change / interruption,且出现质量题;年精度经历存为
YYYY-01-01。 - 根因:学业模板写死「上大学」,不看
event_kind;_display_date_label用_event_month取月,不看precision。 - 修复:学业分支按类型称呼(升学 / 学业变动 / 学业中断,兜底「学业经历」);
precision == "year"的标签只写「YYYY 年」。_event_month与 split hash 不变,计分身份不变。 - 验证:
tests/test_rectification_event_probes.py::QualityWordingTests(4 条);同机 A/B(scripts/research/quality_wording_ab.py,PYTHONHASHSEED=0)只有user_meaning/display_date_label文本变化,分数、候选决策、题目 id / split / outcomes 逐字节相同;按 ERR-110 新建冻结docs/research/sealed_holdout_rerun_quality_wording_2026_09_29.freeze.json、docs/research/reported_offset_quality_wording_2026_09_29.freeze.json,重放 20 / 900 次结果与 09-21 完全一致;验证完整性门禁 83 passed。 - 防复发:年精度经历不得在任何用户文案里补月份;新增学业经历类型时同步
EDUCATION_QUALITY_EVENT_NAME。质量题四个选项仍含「录取」字样,未改。 - 相关记录:ERR-110、BUG-1089
- 复发自:无
- 修复版本:分支
codex/rectification-typed-event-research-20260929,未合入
BUG-1089 | 带年月的打字经历不按选择题规则计分,还挡掉同领域相邻年份的选择题
- 状态:closed_by_design(2026-09-29 Claude 验收:研究复跑 JSON 逐字节一致,R1/R3 不过门;止血单 BUG-1084 已让门开后不再索要打字经历,本条不改计分)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
core/build-state.ts::buildInferenceState(evidence「是」跳过)、event_probes.py::_existence_blocked_years。 - 用户现象:同一件「某年某月工作变动」,点选卡答「是」动 ±2 分,自己打字说约等于 0;说了之后那一年附近的选择题也不再出。
- 已确认事实(虚构 12 例):12 件经历里去掉 4 件记得到月的,选择题 5/12 例变多,最终宽度中位 17 → 13 分钟。
- 根因:计分通道不对称,见
docs/tasks/TASK-rectification-typed-event-scoring-research-20260929.md。 - 修复:不修(研究不过门)。用户可见后果由 BUG-1084 处理:门开后不再索要经历,选择题问完即出卡。
- 研究结果(2026-09-29,
docs/research/rectification_typed_event_scoring_2026_09_29.md,v4 开放集 20 例,开放集回放不是准确率):按选择题同一判定函数给打字经历计分(R1),57 件日精度训练经历在 ±10 / ±30 / ±60 下只有 4 / 9 / 14 件能把候选分到两边,宽度中位三档不变,头名在 raw 口径下三档都降,判no_benefit;挡题缩到同领域同年(R3)宽窗头名 +0.10~0.15 但 ±10 头名 −0.15,判no_benefit;年月记错 ±1–3 月挤出真值 0–2.9%。结论:计分通道不对称在代码上属实,但打字经历本身多半分不开候选,改计分不划算;不立实现单。 - 防复发:不得在研究未过门时改先验尺度或淘汰线(BUG-560)。
- 相关记录:BUG-560、BUG-1048、BUG-1084
- 复发自:无
- 修复版本:未修
BUG-1090 | 生时校正评价集只有 20 例,所有打分判定都在噪声线上
- 状态:resolved(2026-09-29 Claude 验收:77 例 / 964 件;
holdout_v5_build.py --check两次一致;v4 子集 sweep 7 指标 × 3 档、cluster_width 12 字段 × 3 档、futile 六格与已发布数字逐格一致;抽样 6 例(新增 57 例的 10%)在 Astro-Seek 镜像页逐条核对出生日期 / 本地时间 / UT 偏移全部一致,astro.com 原页对自动访问返回拦截页;UTC+8 配额 5/6 为公开 AA 数据可得性缺口,接受) - 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
references/real_case_calibration/minute_rectification_holdout_v4.json(20 例)及所有以它判定"过门 / 不过门"的打分研究(minute_resolution_2026_09_14、cluster_width_2026_09_14、precision_gate_2026_09_14、rectification_offline_research_2026_09_26、rectification_typed_event_scoring_2026_09_29)。 - 用户现象:无直接用户可见变化;后果是 KP 计分(0.35→0.20)、精度分档闸门(0.80→0.75)、V1n 配权等被判
no_benefit时只差 1–3 例,结论不可靠。 - 触发条件:20 例上 1 例 = 5 个百分点。
- 根因:v4 继承 v3 的 20 例名单,只补了事件数与领域数,没有扩例数;没有一份 ≥60 例、事件与出处经核对、口径与本仓一致的公开评价集。
- 修复:新建 v5(
minute_rectification_holdout_v5.json):v4 的 20 例原样继承 + 57 例新增 Rodden-AA 公开名人,每例 ≥7 件带逐字引文的事件、≥4 领域;出生资料做时区(zoneinfo)与地名(Nominatim ≤60 km)双核对,不通过的拒收;构建脚本scripts/research/holdout_v5_build.py两次构建逐字节一致;基线脚本scripts/research/holdout_v5_baseline.py。v4 不动。 - 验证:
tests/test_holdout_v5_schema.py9 项通过;v5 中 v4 子集在三个回放脚本上的基线数字与 09-14 / 09-26 / 09-29 已发布数字逐格一致(docs/research/holdout_v5_build_2026_09_29.md§3);v5 全集三档基线见同文 §4。 - 防复发:
docs/research/rectification_minute_resolution_closure_2026_09_14.md§8 与ACTIVE_FRONTS.md写明此后判定以 v5 为准;TASK-rectification-scoring-research-20260929.md的判定必须在 v5 上做。分层配额 UTC+8 只到 5/6,缺口与原因写在构建说明 §1,不得静默。 - 相关记录:BUG-560、BUG-1089、BUG-1091(研究单预留)
- 复发自:无
- 修复版本:
codex/rectification-holdout-expansion-20260929,未合入
BUG-1091 | 生时校正打分只奖不罚、权重未校准、高分辨率信号未计分
- 状态:closed_by_design(2026-09-30 在 v5 77 例上判定:R-A 似然比校准权重与 R-B 缺席证据在三档半径 × truth/opposite 六格全部未过硬红线 1,打分层不立实现单;R-C 精度追问与同子集基线比无一致收益,同样不立实现单)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-30
- 影响面:
scripts/active_rectification_event_engine.py::_score_event(11 条加分规则,权重手拍)、OBSERVATION_ONLY_LAYERS(KP 宫头子主每分钟在算但不计分)、_fine_minute_indices(Pranapada / Hora / Ghati / Bhava lagna 已算未计分)、计分分盘无 D60。 - 用户现象:开场说 12 件带年份经历、答约 10 张卡后范围整窗不动;引擎原始分头名簇命中在 v4 上只有 0.35 / 0.15 / 0.10(±10 / ±30 / ±60),约为随机水平的 4–5 倍。
- 触发条件:任何一次校正。打分对候选「能解释事件」加分,对「预测了没发生的事」不扣分;一条规则对真分钟和错分钟同样常命中时照样 +2。
- 根因(已确认):(1) 只奖不罚——负证据只来自选择题答「没有」(
core/build-state.ts::applyProbeOutcome,±2);(2) 权重是人拍的,R1–R5 / V1 / V2 / V1n 都是「再拍一个权重看结果」,没有按数据校准;(3) KP / Pranapada / D60 这些分钟级信号没进计分,09-14 KP@默认权重更差是「权重没校准」的另一个症状。 - 修复:不修(研究不过门)。离线脚手架与判定:
scripts/research/scoring_research_lib.py(特征记录 provider 与线上score_candidates逐分对账、似然比权重、leave-one-case-out、加权 provider、缺席 / 精度追问辅助)、scripts/research/scoring_research.py(R-A / R-B / R-C 流水线)、tests/test_scoring_research.py。生产代码与冻结文件零改动。 - 验证:
tests/test_scoring_research.py(含 v4 公开案例对账 0 差);v4 与 v5 全集两次复跑 JSON 逐字节一致(PYTHONHASHSEED=0);v5 对账 231/231 case-radius 差 0。 - 判定数字(v5,
raw口径,留一法按案例;基线真值在区间 / 头名 = 76 / 0.675、76 / 0.494、75 / 0.325):R-A A1 76 / 0.571、77 / 0.455、72 / 0.247;A2 75 / 0.545、76 / 0.416、72 / 0.247;A3 77 / 0.597、77 / 0.468、75 / 0.221——头名三档全降,六格不过。42 个特征里 36 / 38 / 39 个 bootstrap 区间跨 0,剩余 |log LR| ≤ 0.15,v4 与 v5 都稳定同号的特征 0 个。R-B @0.5 / 1.0 / 2.0 分:真值在区间 77 / 73 / 55、75 / 71 / 33、75 / 70 / 19,头名全降,六格不过。按方案矩阵重出六题与共用题目逐格相同(出题器不读矩阵分数)。全文docs/research/rectification_scoring_research_2026_09_29.md。 - 防复发:定论页 §9 记录「线上规则 + KP / Pranapada / D60 对分钟几乎无区分力、缺席年扣分伤真值」;再有人想从加权重 / 换尺度 / 缺席证据入手,先读 §9 与本条数字。任何计分改动仍须先在 v5 上过任务书硬红线,再按 ERR-110 重冻结。
- 相关记录:BUG-560、BUG-1048、BUG-1089、BUG-1090
- 复发自:无
- 修复版本:未修(研究分支
codex/rectification-scoring-research-20260929;脚手架已合入 staging84aaeadf)
BUG-1092 | 报告技术页标题出现「字段」,行星主星表头是看不懂的 RL / NL / SL / SS / SB
- 状态:resolved(代码 + 回归测试;未部署,真机待合入 staging 后复核)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
scripts/pl9_reader_export.py(p3 / p6 / p17 三个####标题、p6 表头、_strip_pl9_user_engineering_markers的field_状态替换)。 - 用户现象:个人报告「出生资料与图盘」里出现「Birth Chart 星主与状态字段」「原版补充字段」;表头是
Planet | RL | NL | SL | SS | 状态 | SB,产品反馈「用户都不知道这是啥」。 - 触发条件:任何 reader_main 报告;p3 / p17 补充表只在引擎返回对应原版资料时出现。
- 根因:模板标题照搬了引擎侧的工程命名(field / 字段)和 PL9 原版缩写,没有做面向读者的命名。
- 修复:标题改为「行星主星与状态」「出生资料补充」「特殊上升与派生点补充」;
field_状态改译「状态」;表头改为「行星 | 星座主 | 星宿主 | 分主 | 分分主 | 尊贵状态 | 力量比」,表下加一句名称说明(力量比 =ishta_bala_pct / 100,1 以上为达标)。 - 验证:
tests/test_report_reader_main.py::test_reader_headings_drop_field_jargon_and_dignity_is_one_name;虚构案例同机改前 / 改后两次渲染 diff 只含这几处文案(goldenfrontend/tests/fixtures/report-reader-main-fictional.json按该 diff 打补丁,其余逐字节不变)。 - 防复发:报告标题不得出现「字段」;新增表头用读者能懂的中文,缩写须在表下说明。
- 相关记录:BUG-1093、TASK-report-reader-polish-20260929
- 复发自:无
- 修复版本:分支
codex/report-reader-polish-20260929
BUG-1093 | 报告尊贵状态显示成「入旺(入旺)」「中性(中性)」
- 状态:resolved(代码 + 回归测试;未部署)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
scripts/pl9_reader_export.pyp6「行星主星与状态」与「宫主(Bhavesh)落宫解释」两张表的状态列。 - 用户现象:状态列同一个词写两遍(入旺(入旺)、入庙(入庙)、中性(中性)),另有「入敌(敌对星座)」「极敌(Great Enemy)」中英混杂。
- 触发条件:任何 reader_main 报告。
- 根因:引擎
DIGNITY_LABELS本身是中英双写(入旺(Exalted));reader 的汉化是全文逐词替换,把括号里的英文也译成中文,于是两边相同;glossary 里没有的词(Great Enemy)保持英文。 - 修复:新增
_dignity_label,只取双写标签的中文部分;两张表的状态单元格都经它输出。引擎DIGNITY_LABELS不动(其它消费方依赖它)。 - 验证:同 BUG-1092 的回归测试断言全文不再出现
X(X)形式、两张表状态列无括号;_dignity_label单测覆盖双写、纯中文、空值。 - 防复发:reader 表格里的引擎枚举值先归一再进汉化,不依赖逐词替换处理括号。
- 相关记录:BUG-1092
- 复发自:无
- 修复版本:分支
codex/report-reader-polish-20260929
BUG-1094 | 报告页可用性:生成入口不显眼、没有到底部、导出按钮找不到、目录层级分不清、表格行难对齐、导出不知道多大
- 状态:resolved(代码 + 回归测试;未部署,浏览器级真机欠,见
docs/testing/report-reader-polish-20260929.md) - 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
personal-report-center.tsx、generate-personal-report-button.tsx、personal-report-markdown-view.tsx、新增report-edge-jump.tsx、report-actions.tsx、report-export-drawer.tsx、lib/report-export-blocks.ts、globals.css。 - 用户现象(产品截图):生成按钮是标题栏右上角的小描边按钮,空状态文案写「生成完整报告」而按钮叫「生成报告」;长报告没法一下到底;导出只是一个灰色图标;目录里「强度、关系与 Ashtakavarga 技术页」像上一组的子项;表格无底色;分块导出只显示字数。
- 根因:入口与导出做成了次要样式;按章节懒渲染却没有配套的到底部;目录一级 / 二级只差 12px 缩进、同色同字重;报告表格沿用聊天的极简表格样式;导出计数只统计字符(图盘块含 SVG,与文件大小差距大)。
- 修复:生成入口挪进页面顶部卡片(删标题栏按钮),下方「过往的报告」;右下角「到底部 / 回到顶部」单按钮,点击先
flushSync挂载全部懒渲染章节再滚到scrollHeight;导出改为带文字的描边按钮;目录一级加粗用 ink、组间留白,二级缩进--space-5;仅报告表格加表头淡底与隔行底色(打印更浅);导出弹窗显示TextEncoder实测字节(全篇与已选)。 - 验证:
frontend/tests/report-reader-polish.test.tsx(6 条)、report-export-drawer.test.tsx新增文件大小用例;既有断言改动三栏见docs/tasks/PROGRESS-report-reader-polish-20260929.md。 - 防复发:报告页只有一个生成入口;到底部不得接入会话滚动锚;表格底色选择器限定在报告作用域。
- 相关记录:BUG-1092、BUG-1093
- 复发自:无
- 修复版本:分支
codex/report-reader-polish-20260929
BUG-1095 | 太阳返照本地时间是 UT 原样:读者版「Varshapravesha」差一个时区,Sahams 昼夜按错误时刻判
- 状态:resolved(2026-09-29 Claude 独立验收通过:6 测试文件+隐私守卫 156 passed;基线树 vs 分支北京虚构盘 A/B:solar-return 6 处差异全为修正项、tajika 0 差异;快速门 Python 步 1001 passed / 1 skipped 与基线一致,npm 步为 worktree 无 node_modules 的环境缺口)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
scripts/solar_return.py(find_solar_return_ut返回dt_local = dt_ut,注释「调用方另行加 tz」但无调用方加)、solar_return_full_report→calc_all_sahams(tz=birth_tz)昼夜判定、pl9_reader_export.py:1830/2201打印dt_local、年运包annual_tajika_pack.py经cmd_solar_return同一路径。 - 用户现象:报告读者版「Varshapravesha:2026-06-15 08:04:52」实为 UT,北京盘应为 16:04:52;跨日时连日期都错(虚构北京盘 2028 年行 06-14 → 06-15)。
- 触发条件:任何目标年的年运;偏移越大越明显。
- 根因:返照求解只算 UT,本地化从未实现;任务书写的「固定偏移在夏令时地区差 1 小时」低估了——本仓根本没加偏移。
- 修复:抄上游
41d1c740的_localize_return_datetime(ZoneInfo 按返照瞬间取偏移,无 IANA 区则固定偏移),calc_solar_return_chart写回dt_local与annual_location;Sahams 昼夜用annual_location;返照地点可选current_location(引擎 / CLI / 年运包参数;外部 PyJHora 回放异地时 blocked);新增出生地--timezone-id/ APItimezone传入。 - 验证:
tests/test_solar_return_timezone.py(10 条);北京盘改前改后解析比对差异仅dt_local、annual_location(新增)、daynight_evidence.julian_day_ut;tajika0 差异;Asia/Shanghai与固定 +8 0 差异;纽约盘 IANA 区 21:08:03(−4)vs 固定 20:08:03(−5);伦敦异地返照盘上升 Taurus vs Capricorn。数字见docs/tasks/PROGRESS-upstream-sync3-20260929.md。 - 防复发:
test_beijing_return_local_time_is_ut_plus_eight锁dt_local − dt_ut = +8h与昼夜判定时刻 =jd_ut;不得再返回 UT 当本地时间并寄望调用方补偏移。遗留:网页报告链只传数字偏移(personal-report-longform-birth.ts:79),IANA 区未到引擎,夏令时地区用户仍是固定偏移。 - 相关记录:BUG-608、BUG-1028、BUG-1096、BUG-1097
- 复发自:无
- 修复版本:分支
codex/upstream-sync3-20260929,未合入
BUG-1096 | 全量报告装配时年运面板键被清空(上游 089858c0 所修)——本仓核实不适用
- 状态:closed_not_applicable(2026-09-29 执行方核实)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
scripts/jyotish_engine.py::build_professional_report_reference_packet/_apply_pl9_pack_selection;scripts/annual_tajika_pack.py。 - 用户现象:无(本仓从未生成这些面板)。
- 已确认事实:虚构北京盘
pl9-export --pack full的annual_tajika_pack15 个键里,上游 12 个键中profile、patyayini_dasha有,其余 10 个(monthly_chart_pack、normalized_tables、lifetime_annual_chart_pages、annual_chart_series_pack、pl9_annual_visual_panels、pl9_annual_page_map、pl9_current_year_results、pl9_annual_dasha_results、annual_special_points、tripataki_chakra)在整个导出包任何路径都不存在;本仓annual_tajika_pack.py没有生产者,只有pl9_reader_export.py防御性读取。_apply_pl9_pack_selection在full时原样返回 packet,不存在装配丢失。 - 根因:不适用——上游修的是它自己 09 月新增的面板生产者被装配层清空;本仓没有该生产者。
- 修复:不抄。上游同批
full_report_quality_gate.py+86 行(80756803)是逐年 Muntha / Year Lord 一致性与 Ashtakavarga 12 宫映射校验,与面板无关,亦未接。 - 验证:核实表见
docs/tasks/PROGRESS-upstream-sync3-20260929.md§T2。 - 防复发:若日后引入年运面板生产者,再评估
_preserve_annual_report_surface。 - 相关记录:BUG-1095、BUG-1028
- 复发自:无
- 修复版本:未修(不适用)
BUG-1097 | PL9 导出 JSON 在内存里再造一份报告大小的缩进字符串,文件体积翻倍
- 状态:resolved(2026-09-29 Claude 独立验收通过:6 测试文件+隐私守卫 156 passed;基线树 vs 分支北京虚构盘 A/B:solar-return 6 处差异全为修正项、tajika 0 差异;快速门 Python 步 1001 passed / 1 skipped 与基线一致,npm 步为 worktree 无 node_modules 的环境缺口)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
scripts/jyotish_engine.py::output_json与main()的pl9-export --output落盘。仅 CLI 与落盘路径;生产 API(jyotish_api_server.py)自行序列化,不经过此处。 - 用户现象:
pl9-exportJSON 9.57 MB(缩进),序列化峰值 46.9 MB。 - 根因:
print(json.dumps(..., indent=2));上游59c9139f改为流式紧凑。 - 修复:
output_json(data, *, stream=None)照抄签名;实现为 PL9 schema 紧凑dumps一次写出,其他命令仍indent=2。实测上游的json.dump流式在 CPython 走纯 Python 编码器(0.59 s),比紧凑dumps(0.30 s)慢,故未抄其实现。 - 验证:单步基准 A 0.410 s / 46.9 MB / 9.57 MB → C 0.303 s / 22.1 MB / 4.61 MB;整条 CLI 交错 A/B 3 次基线 6.92/6.62/6.42 s、RSS 197.9/169.2/169.6 MB → 6.58/6.42/6.38 s、191.3/159.4/159.0 MB;解析后相等;
test_output_json_is_compact_only_for_pl9_exports。 - 防复发:非 PL9 命令输出格式不变由同一测试锁定。
- 相关记录:BUG-1095、ERR-111
- 复发自:无
- 修复版本:分支
codex/upstream-sync3-20260929,未合入
BUG-1098 | 星盘行星表宫位标题左对齐,内容误用居中样式
- 状态:investigating(2026-09-29 本地修复及独立浏览器验证通过;最终全量无新增失败,但保留 29 项既有加载失败,未部署、线上待验)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
chart-vedic-tab.tsx分盘、本命/月亮、Bhava、行运表格与对应 CSS。 - 现象 / 触发:打开上述盘型,宫位标题与每行宫位内容不在同一左边线。
- 根因:标题继承默认左对齐,宫位 td 误用
chart-page-planet-mid。旧测试验证数据、列数与 D1 字节冻结,没有检查宫位 computed alignment;分盘之外同类写法也存在。 - 修复:分盘、Bhava、行运仅移除宫位格 mid;本命/月亮保留原八列 markup,通过八列表头限定 CSS 第四列左对齐。宿主、pada、速度、换宫等刻意居中列不变,不全局改 mid。
- 验证:执行方前端定向与 D1 字节冻结通过;新增源码/样式合同。独立真实 Chrome 六盘型×六视口确认宫位 computed text-align left、标题和值 padding 12px、宿主/pada仍居中,窄屏无整页横滚;独立全量结果见
PROGRESS-chart-surface-polish-20260929.md。未部署,不提前宣布线上解决。 - 防复发:各盘宫位标题/值同左对齐和 padding;保留 BUG-1083 外层可收缩与表内横滚合同。
- 相关记录:BUG-1076、BUG-1083;本地诊断任务书记录延续,非重新编号。
- 复发自:无旧对齐修复证据;属于既有表格未覆盖的展示问题。
- 修复版本:
codex/chart-surface-polish-20260929工作区,未提交、未推送。
BUG-1099 | 星盘分盘表把全部 Dn 同座比较统一展示为原生同位能力
- 状态:investigating(2026-09-29 D9 能力边界本地验证通过;最终全量无新增失败,但保留 29 项既有加载失败,未部署、线上待验)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
chart-view-mapper.ts::buildVargas、chartViewVargaSchema、chart-vedic-tab.tsx::VargaTable。 - 现象 / 触发:非 D9 分盘也显示同位,没有能力声明,false 横杠无法区分不支持与未命中。
- 根因:09-28 产品约定将任意 Dn 与 D1 同座泛化为 vargottama;原生
_calc_vargottama只处理 D9。算术比较本身没错,旧测试按旧产品定义验证,并非计算回归漏检。 - 修复:服务端可选能力声明,两列独立严格 true 才展示;D9 有有效来源时支持同位,其他 Dn 隐藏;三档庙旺保留,上升标不适用。支持性由有效来源而非结果真值判断。旧模块快照缺声明保守隐藏,不改原始引擎缓存或身份隔离。
- 验证:能力组合、全 false/null、坏类型、缺失/无效源、缓存直读与分层合并均增加 golden 回归;Python 合同直接将真实 TS mapper 与
_calc_vargottama行星和build_d9_summary上升分别对照。执行方及独立定向通过,最终 87 项受影响前端测试、5 项 Python 合同全部通过;全量新增 17 项、删除 0,29 项基线加载失败逐名逐原因一致。完整证据见 PROGRESS。 - 防复发:禁止用
some(vargottama)判断支持,禁止将未知补 false;缺能力整列不渲染,分盘行不能退回 D1。 - 相关记录:BUG-1076、
TASK-chart-types-and-report-buttons-20260928.mdT3;本任务书明确有限修订旧约定。 - 复发自:无;产品术语能力范围的收口,不是 D1 冒充分盘复发。
- 修复版本:
codex/chart-surface-polish-20260929工作区,未提交、未推送。
BUG-1100 | 星盘页印度盘外框比星图宽一截,左右大片留白
- 状态:resolved(代码 + 回归测试;未部署时写于分支,真机欠)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
globals.css桌面断点.chart-page-vedic-plot/.chart-page-vedic-stage。 - 用户现象(产品截图):星图四周多了一张更宽的卡片,「框太丑」。
- 触发条件:≥1024px 桌面。
- 根因:
64aa0ec2(TASK-chart-surface-polish T5)把 440px 上限加在外框内的绘图区上,外框仍随栅格列拉宽。 - 修复:上限移到外框(440px + 两侧内边距 + 边框),绘图区在框内撑满;手机端不设上限。等待骨架在同一外框里,揭幕不跳尺寸。
- 验证:
frontend/tests/chart-page-mobile-width-20260929.test.ts(断言改动三栏见 PROGRESS)、surface-feedback-20260929.test.tsx。 - 防复发:尺寸上限加在带边框的容器上,不加在它的内层。
- 相关记录:BUG-1098、BUG-1099(同一提交的其他项)
- 复发自:无
- 修复版本:分支
codex/surface-feedback-20260929
BUG-1101 | 星盘图标说明显示在盘下一行字,不是图标旁的气泡;还常驻一句「停在或轻点……」
- 状态:resolved(代码 + 回归测试;手机真机欠)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
components/chart-page/chart-symbol-help.tsx、western-wheel-svg.tsx、globals.css。 - 用户现象:鼠标停在行星符号上,说明出现在盘下方;产品要的是图标旁弹出气泡,且不要那句常驻提示。
- 根因:
64aa0ec2按「说明在盘外预留区域,不遮挡图标」实现为盘下状态行,与产品要的交互不同。 - 修复:同一交互入口改为锚在标记旁的气泡(上方优先、近顶翻下、左右夹在外框内、不接收指针);电脑悬停 / 点击 / 键盘聚焦出现;手机轻点出现,触摸离开事件不关,点盘外或 3 秒自动关;读屏保留隐藏
role="status"。西洋盘的「最近行星」解析结果同时给出锚点。删除提示句。 - 验证:
chart-symbol-help.test.tsx原 4 条不改断言全部通过;新增定位纯函数与触摸时序测试。几何与真实触摸需真机(清单)。 - 防复发:说明形态以产品描述为准;「不遮挡」由气泡不接收指针、避让边缘满足,而不是挪到盘外。
- 相关记录:BUG-1100
- 复发自:无
- 修复版本:分支
codex/surface-feedback-20260929
BUG-1102 | 「那一刻的天空」按钮慢一秒才出现,且不显眼
- 状态:resolved(代码 + 回归测试;真机欠)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
hooks/use-birth-sky-cover.ts、components/birth-sky/birth-sky-entry.tsx、chart-page-view.tsx、globals.css。 - 用户现象:进星盘后右上角按钮约一秒后才出现;细线框小按钮不明显。
- 根因:按钮渲染条件是「封面 PNG 已画好」(TASK-birth-sky-cover T3 的设计),画图需要网络 + 离屏绘制。
- 修复:钩子返回
{ cover, pending };星盘就绪即显示按钮,封面在画时点击被记下,画好立即开播;失败时按钮消失。样式改星芒图标 + accent 浅底。 - 验证:
surface-feedback-20260929.test.tsx;birth-sky-replay-20260929.test.ts一条断言按三栏更新,chart-page-view.test.tsx既有「没有封面也不在加载时不显示」断言不变。 - 防复发:入口按钮不等待次要资源;需要资源的动作在点击后等待并由动作本身承担等待。
- 相关记录:TASK-birth-sky-cover-20260928、TASK-mobile-chart-and-confirmed-edit-20260929 M4
- 复发自:无
- 修复版本:分支
codex/surface-feedback-20260929
BUG-1103 | 「我的报告」生成卡片贴着顶栏;标题「生成个人报告」与按钮「生成报告」重复
- 状态:resolved(代码 + 回归测试)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
personal-report-center.tsx、globals.css .report-center-create。 - 根因:BUG-1094 改版时卡片上外边距为 0;标题与按钮都写了动作。
- 修复:卡片上方
--space-6;标题改「完整本命报告」,说明一句写 6 章与中英两版,按钮仍「生成报告」(产品选定)。 - 验证:
surface-feedback-20260929.test.tsx。 - 相关记录:BUG-1094
- 复发自:BUG-1094 的遗漏
- 修复版本:分支
codex/surface-feedback-20260929
BUG-1104 | 先打开星盘 / 报告等页面的标签,第一次点「新建对话」仍要等两三秒
- 状态:resolved(代码 + 生命周期回归测试;真机浏览器耗时未测)
- 首次发现 / 最近更新:2026-09-29 / 2026-09-29
- 影响面:
frontend/src/lib/session-list-context.tsx(新增后台预取)、frontend/src/lib/home-warm-prefetch.ts(新)、frontend/src/lib/home-cloud-sync.ts::fetchRectificationEntrySummary(从首页 effect 抽出,冷启动与预取共用)、frontend/src/lib/new-chat-timing.ts(新)、frontend/src/components/app-sidebar.tsx。 - 用户现象:在星盘、星历、我的报告、星盘档案等页面点侧栏「新建对话」,要等两三秒才进首页(整段加载屏)。
- 触发条件:这个标签页里首页还没有完整冷启动过——刷新了次级页、从链接直接打开次级页,再点「新建对话」。首页已经冷启动过的标签不复现(BUG-1040 的暖快照生效)。
- 根因:暖快照只由首页自己在冷启动成功后写入(
src/app/(app)/page.tsx写快照的 effect);home-warm-start.ts::resolveHomeWarmStart读到isCompleteHomeWarmSnapshot为假(没有模型目录 / 入口摘要未 settle)就回冷路径。所以「先落在次级页」的标签第一次回首页必然整段冷启动。/路由的代码块本来就由 NextLink预取,不是原因。 - 修复:
SessionListProvider在列表 settled、已登录、资料完整、首页未注册、地址不是/时,于空闲时刻用冷启动同一组客户端函数(fetchModelCatalog、fetchRectificationEntrySummary)只读预取,并经同一个writeHomeWarmSnapshot写入(listBoot: null,游标以 provider 自己的 boot 为准)。首页挂载 / 退出 / 换账户 / 列表重载即 abort,写入前再确认仍是当前;目录失败不写,摘要失败按冷启动同样 settle 为默认卡。另加本地 Performance 打点jyotisha:new-chat (warm|cold)。 - 验证:
frontend/tests/home-warm-return-lifecycle.test.tsx新增 3 条(真实 Home + 侧栏 + provider):标签先开/chart、/reports,预取完成后「新建对话」首帧无加载屏且在任何请求发出前就绪,预取只发 2 个 GET;预取失败时不留快照、照旧冷启动。去掉预取调用后两条暖路径用例超时失败(复现原问题)。frontend/tests/home-warm-prefetch.test.ts6 条覆盖取消、已完整不重取、失败、空目录、单飞与账户隔离。 - 防复发:新的首页冷启动必需件(
isCompleteHomeWarmSnapshot条件)必须同时加进home-warm-prefetch.ts,否则预取会写出一个永远不完整、被忽略的快照——生命周期用例会因此超时报警。 - 未验证:真机浏览器上的实际秒数(无登录态与 Chrome);部署后可在 DevTools Performance 里看
jyotisha:new-chat的测量值。 - 相关记录:BUG-1040、BUG-1052
- 复发自:无(BUG-1040 的覆盖缺口)
- 修复版本:分支
codex/new-chat-prewarm-20260929
BUG-1105 | 生时校正目标函数是分钟而非用户要用的分盘;窗内盘型不变的用户被迫答题;无「分盘不可判」出口
- 状态:resolved(研究阶段;2026-09-30 Claude 验收:M0 两张表与我 09-30 起点数字逐格一致、231 例区间端点 0 差;M1–M3 + 稳健性两次完整运行 JSON 逐字节一致,M0 基线与已提交文件逐字节一致;测试 48 passed;快速门 Python 1001 passed / 1 skipped;生产代码与冻结文件零改动。结论成立:±10 分盘可分档给可信度,±30 多数用户只能给 D1,±60 分盘 blocked;提前停不过门。产品层实现单另立)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:校正选题(按分开候选分钟排序)、交付卡(分钟区间 + 最可能分钟)、并列判定(按分钟数);
frontend/src/lib/rectification-agentic/v9/*、core/build-state.ts、user-copy.ts。 - 用户现象:31 分钟窗答完题范围不动、「最可能的分钟」头名只有 64%(v5);用户真正要的「用哪张盘解读」没有被回答;±30 以上的窗口仍给分钟区间,实际上分盘已不可靠。
- 触发条件:任何一次校正。
- 根因(已确认):目标函数是「分钟」。分盘上升段远少于候选分钟(±10 时 D9 / D10 平均 2.4 / 2.5 种,候选 11 个),把六题证据按段汇总后,±10 头段 = 真值 D9 88%、D10 86%,头段占比 ≥ 0.6 时留一法 92% / 90%;±30 只有约 1/4–1/3 用户能给可信分盘;±60 真值段保留 74/77 低于基线 75,分盘只能 blocked。
- 修复:本条只做研究,不改代码。研究脚本
scripts/research/varga_resolution_lib.py/varga_resolution_probe.py,结论docs/research/rectification_varga_resolution_2026_09_30.md(§7 实现要点:录入后段扫描、段级汇总、分档文案、按段选题只在 ≤ ±30、按问题域取盘;不改引擎 / 常数 / 冻结文件;六题照问,不提前停)。 - 验证:
tests/test_varga_resolution_research.py17 passed(含任务书两张表逐格复现);PYTHONHASHSEED=0两次完整运行 JSON 逐字节一致;六题回放与fewer_probes_card_replay.evaluate_case逐例区间 0 差。 - 判定数字:硬红线 1(真值段保留 ≥ 76 / 76 / 75)±10、±30 全过,±60 D9 / D10 不过;按段选题 ±10 D9 68→71、±30 D9 44→48、D10 49→56,真值段保留不降,±60 更差;提前停 0.7 / 0.8 在 ±10 丢 1 例,不过;「不用校正」出口三种策略 0/77,只对只需 D1 的问题成立(±10 64/77);答错 1 题 ±10 保留 98.7%、命中 83–86%。
- 防复发:实现单验收用本研究脚本回放,±10 / ±30 真值段保留 76 / 76、头段命中不低于研究文档 §4「按段」列;±60 交付卡不得出现 D9 / D10 推荐;不得用头段占比提前停问。
- 另一会话 2026-09-30 08:49 归档的部分研究(staging
bedb4d7b:M0 首轮 + M1,无复跑、无 M2/M3)已被本记录取代;其scripts/research/varga_resolution_m1.py与artifacts/varga-resolution/随本次合并移除。 - 相关记录:BUG-560、BUG-1084、BUG-1090、BUG-1091
- 复发自:无
- 修复版本:研究分支
codex/rectification-varga-resolution-research-20260930(未合入)
BUG-1115 | 生时校正缺少全窗逐分钟目标分盘段扫描
- 状态:resolved(Claude 2026-10-01 独立验收:77 例生产路径回放与研究 M1/M2 整树差 0;tsc 0、lint 0 error、npm 失败清单同 staging、gzip +0.79%、快速门 Python 1035/0;真 PostgreSQL 17(本地替身)全量 test:db 与 staging 失败清单一致、新增 11 条全过;未部署,真机与真 Docker 复跑待产品负责人)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
scripts/rectification_varga_segments_api.py、API 薄注册、重计算限流、开案后全窗判定。 - 用户现象:窗内盘型本来不变仍进入采集;只看候选代表分钟不能证明完整出生窗只有一种上升。
- 触发条件:录入出生窗后应判断目标盘集合全窗一致,但旧链没有逐分钟扫描证据。
- 根因:旧目标是分钟候选,没有可缓存的完整窗分盘上升段与民用日期输出。
- 修复(在途):独立扫描模块复用 native chart + varga、Raman/mean;逐分钟 D1/D9/D10,拒绝其它目标盘、校验窗口与有限数字、按窗口键缓存;仅薄注册主 handler、纳入 heavy-compute gate。不改冻结引擎。
- 验证:新扫描/API growth/heavy-compute 定向合计 19 项通过、exit 0;±10/±30/±60 每分钟与研究相等,跨午夜保留日期、非法输入拒绝。开案调用、checks、「不用校正」单一成功出口与最终 quick 尚未验收;不声称已上线。
- T3追加现场(仍investigating):新nullable持久domain/checks、server-only 12参数open_v2 overload原子开案与初始化、旧11参数/session保持合同;开案完整scan、shared turn-exit一致卡挂实际server turn与公开结束态。最新本机119/119、fail/cancel/skip0、exit0;tsc0、lint0/126原warnings;无模型route控制证明unique在模型解析/收费/focus之前退出,正常message锁窗回复不被抢。新增真DB测试本机1fail/0cancel/0skip、exit1,runner报duplicate migration filename(Windows目录重叠),未改runner/历史迁移;须Linux immutable source真PG。当前生产首页仍general,婚恋/事业实际owned source入口尚未接,不能称D2或T3完成。
- ownedsource追加(仍investigating):生产POST从owned chat_sessions的consultation类型及明确theme allowlist派生领域;browser只传sourceSessionId,不能注入domain/出生数据,exact-session仍旧11参数。新案领域一次固定,resume不覆盖;路线正负控制只用实际POST+状态RPC替身,不算真DB。a2e93独立DB首新12参数open报42501,73/pass72/fail1/cancel0/skip0、exit1,后续SQL断言未执行;旧权限reconciliation与内部definer调用只读诊断,不修改授权/另路绕过。此前「首页仍general」是旧快照事实,不代表当前在途接线,实际browser/DB仍未闭。
- c1a2独立追加(仍investigating):真DB73/pass72/fail1/cancel0/skip0、exit1,opening仍42501且后续SQL断言未执行;实际POST来源/ACL核证过不等于真实开案过。该快照全npm/quick新增普通新chat源码合同失败:首个unsaved-empty匹配已跨入handoff成功后的清草稿。已检索BUG-989并保其普通首send才persist、不复用draft、失败不清草稿全部原门;只将旧源码slice精确限定普通send,新增handoff先保存/失败不清草稿/不走普通回滚控制,本机原名3/3、fail/cancel/skip0、exit0。三栏及新快照范围见PROGRESS;不将此新失败豁免为旧32。
- 防复发:只有目标每盘全窗一种才可不用校正;candidate signature fallback 不得伪称完整扫描;不加 handler 方法/伪造实例;无新增端口/凭据。
- 相关记录:BUG-1105;
TASK-rectification-varga-resolution-20260930.mdT1/T3;本轮 PROGRESS。 - 复发自:无(研究结论的实现)
- 修复版本:
codex/rectification-varga-resolution-20260930,未提交/未推送/未部署。
BUG-1116 | 校正后验与选题没有按目标上升连续段汇总
- 状态:resolved(Claude 2026-10-01 独立验收:77 例生产路径回放与研究 M1/M2 整树差 0;tsc 0、lint 0 error、npm 失败清单同 staging、gzip +0.79%、快速门 Python 1035/0;真 PostgreSQL 17(本地替身)全量 test:db 与 staging 失败清单一致、新增 11 条全过;按段选题默认关闭,按原任务书让步顺序不计入;未部署)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
core/segment-summary.ts、segment-probe-order.ts、raw/签名只读旁路、InferenceState、生产桥回放。 - 用户现象:用户要用的目标盘未被校正结果回答;占比只有分钟口径,提问按分开分钟而非盘段排序。
- 触发条件:六题后需要按领域目标输出盘段置信度。
- 根因(产品):分钟是旧目标函数;实际 public 候选不含任务书所称 signature/raw,需从原始候选分数与 feature snapshot 读取,不修改 frozen 输出。
- 修复(在途):raw 有效簇非负质量均摊实际成员分钟、连续段后验、D3 固定分档、先出现段并列标记;信息增益中性候选 ½/½、仅窄窗重排,不改 ±2 / 三冲突淘汰、停止时机或原题。
- 验证:当前真实引擎 12 例 goldens 的纯 TS 定向 147 pass / 0 fail / 0 cancelled、exit 0;生产桥 77 例单盘 ±10 D9/D10 保留均 76、头段 71/66;±30 均 76、头段 48/56。但直接对已验收 JSON,现有 72 格只匹配 66 格;当前 Python/TS 同输入一致,accepted 六位占比差 1~3e-6,不能用聚合或本机 golden 自洽替代红线。
- 历史诊断现场:本机 Python 3.11.7 与研究验收 Python 3.13 不同;仅诊断进程补偿浮点 sum,raw score4 量化变化后 770/770 窄窗 raw per_case 重现 accepted。当时待 Linux 验证,不改冻结 scorer、不放宽容差、不更新 accepted JSON。
- 追加验证:独立 Linux Python 3.13 同 contexts 已闭环 72 单盘 golden、77 例×三档 M1/M2 整树 difference_count=0,exit 0;旧六位差不再是当前阻塞。该证据只证明研究单盘/核心桥,不证明默认联合 method 六题及答案持久化收益。
- 追加回归修复:off/no-state 原 eligible 排序不得 eager 构造 gain probe;合法 event 缺 optional split hash 无 contrast 时安全 fallback。备选原用 round6 shares 重排 slice(1),微小领先 top index 与显示并列顺序不一致会丢备选;改用未舍入 mass、按真实 top index 排除,mass>EPS 显示 share=0 仍保留。最新本机七套 336/336、fail/cancel/skip=0、exit0、tsc0;仅在途本机,不替最新 Linux 全量。
- 旧案隔离追加(仍investigating):fresh rescore仅持久domain非null才scan并构造产品段inference;nullable history清previous optional segment carryover与输入fallback元数据,minute prior/posterior/holdout/probes/停止不变,外层真实native raw/signature仍保留。真实AA native capture定向覆盖nullable/marriage/career/general与carryover;owned持久化替身不是PG,不等于cached/action/history/Skill bump或六题联合验收。
- 领域身份补漏(在途,后于45dd):只读核证新SQL合法domain包含report/other,而product-delivery allowlist漏这两项;缺scan时身份会为空并退旧分钟出口。现补纯映射,仍按D2默认D1+D9+D10,不改取盘policy或SQL/权限。真实native capture矩阵及直接scan抛错→score receipt持久化替身→reload/tools/report新增report/other控制,旧test names保;初三文件56/56与最终24文件555/555、fail/cancel/skip0exit0,最终tsc0/lint0errors126warnings。新增11域SQL文本allowlist合同锁,不是SQL执行;替身不算真PG。
- T5持久链追加(仍investigating):新增独立公开v5 driver与PG runner,显式虚构Case种子非newopening;ownedsource经原domain resolver/持久dossier目标,真实append turn/evidence/score/catalog/owned focus/options分类/choice持久链。off为默认、on仅实验排序,truth只oracle,每题保actual class/source/choice_kind、gate与ledger,真实stop/no-renderable/missing oracle不补题;10distinctcases×三窗×双开关只60traces非60病例或six完成。Windows10例入口actualexit1/records0、原migrationduplicate拒绝,后链均未exec;本机syntax/tsc/eslint0不等于PG,初新trace字段TS2551已仅fixture改实际semanticKey。输出已存在拒绝且exclusive写,旧failed evidence保。c637 Linux10distinct/60traces真实exit1、tsc/privacy0,全部seed/source_session_unavailable,domain/targets/beforeledger/append未到,questions/metrics/six0;此次非turn拒绝。独立只读root为新harness误把service传ownedsource resolver,真实POST用authenticated,原chat_sessions service SELECT撤而authenticated合法。仅newharness改原app_runtime/authenticated owner读取及closepool,保真实resolver/domain/persistedCase/service后RPC,不恢复授权/harddefault/直插turn替代;tsc/eslint0。后续d029542f独立Linux10distinct×三窗×双开关60traces,全部认证owned source/general与持久dossier targets D1D9D10成功;下一stage appendV9Turn全部原append_agentic_rectification_turn权限拒绝,前后五ledger均0且相等,asked/metrics/six0;replay1/tsc0/privacy0,wrapper0非通过。全77未跑,不以seed turn/evidence做假校准,joint红线not-evaluable,off默认且收益不计。两类permission(opening与append)是新增失败,不豁免baseline,不改权限/题池/门/accepted,BUG不关闭/不可合入。
- T6当前产物阻塞:repo实施回放JSON仍旧Windows产物sha41144a7c…afd3,无implementation_identity、strict1242diff,不能当正式current;已有a8c0独立Python3.13.15 public offline树sha9902260f…1f5dc严格0差仅局部单chart证据。拟原bytes覆盖repo被权限明确拒绝,未执行,原文件不变,不换工具/改名/删目标/造同目的JSON或借会话代执行。真人待执行清单与新真实持久回放入口见本轮PROGRESS/任务索引,未称T6正式产物完成。
- 防复发:≥10 例真实引擎 goldens 必须直接对 accepted 逐格,fixture 与研究/生产运行平台身份明确;不挑选避开差异案例;不把 m2 未保存的 semantic_key 顺序说成 JSON 已有;>61 分钟回退旧顺序;占比只决定档位不能提前停。宽窗 D9/D10 不推荐。
- 相关记录:BUG-1105、BUG-712(异平台浮点经验)、BUG-985;任务书 T2/T3/T5/T6;本轮 PROGRESS。
- 复发自:无(研究结论的实现;平台诊断尚未定案)
- 修复版本:
codex/rectification-varga-resolution-20260930,未提交/未推送/未部署。
BUG-1117 | 交付卡仍以分钟为主且采用接口只能保存候选代表分钟
- 状态:resolved(Claude 2026-10-01 独立验收:77 例生产路径回放与研究 M1/M2 整树差 0;tsc 0、lint 0 error、npm 失败清单同 staging、gzip +0.79%、快速门 Python 1035/0;真 PostgreSQL 17(本地替身)全量 test:db 与 staging 失败清单一致、新增 11 条全过;采用分钟 D7 真库测试通过;未部署,真机待)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:交付/采用卡、timeline、user-copy、候选采用 RPC、报告盘型与来源。
- 用户现象:卡片主结论是时间而非用哪张盘;旧采用动作保存代表候选,无法采用多个目标盘头段交集内的非候选中点。
- 触发条件:按盘型交付、尤其跨午夜或头段交集的中点不是当前候选代表分钟。
- 根因:分钟优先的历史 UI 与 UUID 对应候选采用 RPC;RPC 保存 candidate_time,inference representative_time 另受活跃头名一致性保护,不能安全替换为任意段中点。
- 修复(授权/在途):产品 D1/D7 推翻旧分钟主卡,改目标盘逐行上升/档位、分钟次行;产品另授权「补齐接口」,新增向后兼容采用接口/迁移并真跑 test:db。现已新增 service-role-only 段采用 wrapper 与虚构权限/跨日边界测试,复用旧 accept_v2 gate;不伪造候选 UUID、不改历史迁移或 identity 校验。历史现场:独立 Linux 首轮新迁移报 PG
42601、syntax error at or near "offset",新 CTE 保留字别名改为minute_offset;之后 golden-db-v2 独立真 DB 71/71、latest-choice 快照72/72闭环。不是仅凭语法修改宣称通过,也不覆盖之后 loader/UI/server 编辑。 - 验证:按快照的独立证据见 PROGRESS:golden-db-v2 71/71、confirmation 定向3/3、latest-choice完整72/72(fail/cancel/skip=0、exit0),含真实 API raw/scan 持久重载、非候选采用后确认拒绝且无半写、原 same-minute confirm positive、legacy retry只读与manual freshness。不能覆盖后续源码或最终全量。
- 追加修复/验证(在途):shared civil identity贯穿dossier决策、卡、工具eligibility、confirm execute;完全legacy缺candidate_date只接受服务端持久baseline日期证明、不造metadata。真实sealed holdout not_ready仍使真实card/tool两种civil原门关闭;synthetic-open helper/overlay与SQL positive各自标scope。新segment server report优先完整scan/原窗、目标推荐保护,代表签名/宫位只作diagnostic不得假称实际采用盘;旧minute-only Skill口径保留。本机七套336/336、tsc0、lint0/126warnings,九格为明确虚构render边界不是enginegolden;新增legacy loader真DB、最终build/gzip/全量/浏览器仍待新快照复核。Home incremental/exclusive前次+2.172%/+4.021%未过±2%,不拿all initial+0.725%冒称通过。
- 最新稳定组追加(仍investigating):本机15套197/197、fail/cancel/skip0、exit0,tsc0/lint0errors126原warnings,覆盖实际dossier有效tap卡/旧身份只读/fictional delivered consistency及fresh nullable rescore隔离,非真PG或六题收益。a2e93独立build0/Home Static,all+0.666%、incremental+1.988%、exclusive+3.681%未过,至少需减2104 gzip bytes;不覆盖后来源码。T4 opt-in缺summary不得混用旧minute-only主句、actual saved tuple报告与D7/Skill真历史仍待闭环。
- T4产品身份子组追加(仍investigating):持久domain独立构造segment-product-v1;新opt-in缺summary仍明确目标盘未知/扫描不可用,不退历史分钟主卡。identity接score receipt、dossier、tools、copy、卡与timeline;显式历史null保旧绑定,top identity覆盖nested缺失,summary目标不匹配清可选摘要。真实golden矩阵捕获公共报告parser误读整range对象,改读verification_markdown字段;无冻结inference仍原fail-closed,不放宽门。直接scan抛错→真实score/parser/inference→持久化RPC替身→dossier reload/tools控制捕获partial气泡与报告漏说明,现仅partial追加候选/不能全窗证明,complete原文不变。缺扫描与精度blocked文案分开,但报告仍禁推荐。本机新/旧合同回归与失败现场详PROGRESS,非SQL、calibration、真实浏览器或完整T4。c1a2实际exclusive缺口2149 bytes、incremental18,build/quick不覆盖此live;agent/新Skill/saved-profile→longform→native仍未闭。
- 领域身份追加(在途,后于45dd):合法持久report/other也必须维持segment-product-v1三盘身份;缺summary显示未知、partial不冒全窗证据,不得因allowlist漏项退legacy。已补纯映射与同真实golden控制;本机最终24文件555/555、fail/cancel/skip0exit0、tsc0/lint0errors126warnings,非最终full/PG/六题联合或Skill历史验收。未改SQL、权限或冻结算法,新结果不覆盖45dd独立成绩。
- T4 Agent/Skill追加(仍investigating,后于fa4f):持久product identity独立新prompt、opening brief/bootstrap/validator/run兜底,缺score仍新盘型口径,显式null保原legacy字符串;新immutable10.0.33六文件/hash
447605a1a2d853e339b78649f73109bd4ffe0471db4a45df9d02a46aefcf5379、旧包身份/bytes不改。原active断言逐处三栏同步,旧10.0.32 bump改精确lookup保历史含义。本机合同52/52与active168/168、fail/cancel/skip0exit0,初新fixture类型错误后tsc0;Agent/history组25/22pass/3failexit1,原Windows路径regex及两symlink EPERM,真实Agent/model控制未运行,不消红、不套fa4f验收。真Skill bump历史list/open/action、saved-profile→longform→native仍待闭环。 - T4独立及保存链追加(仍investigating):721313 Linux scoped325/325、fail/cancel/skip0exit0、tsc/privacy0,实际Agent/runner/scriptedmodel已执行;旧包204files及legacy prompt4066bytes同baseline,build0/Static及三gzip全过,仅覆盖固定源。新增saved-native独立控制用实际native扫描、明确虚构policy gate、RPC保存后的profile真实读回→生产global profile/longform payload→native,代表签名不作报告输入;named与offset-only两项本机均在原runner重复migration filename保护失败(2/0pass/2fail/cancel0/skip0exit1),后续保存链未执行。独立native plumbing1/1exit0、tsc/新文件eslint0不是PG链通过;不改旧测试、runner、migration或ACL消红,待Linux新固定源核证。
- D7追加(仍investigating,后于0d5e):保存链新增现有case adopted date、decision saved date、profile active tuple与selection RPC绑定控制(无selected_date/accepted_date列,不添schema)。明确虚构SQL控制覆盖empty交集D1>D9>D10及无D1内部D9>D10、clamp、gap等距earlier;gap只能内部算法读回,公共dated-v1 gate仍拒非法disjoint并四表无半写,不放宽资格。新D7本机tests2/pass1/fail1/cancel0/skip0exit1,DB原runner Windows重复migration入口拒绝、SQL后续未执行;纯两name-filter控制2/2exit0,初readonly新fixture三错后spread修复tsc0/新文件eslint0。实际SQL待新固定源Linux,不关闭BUG。
- 新固定源追加(仍investigating):0d5e Linux保存plumbing三项3/3exit0,named/offset-only实际profile→生产longform→native与scan/head一致;a8c0三saved-native含现有civil日期/RPC绑定通过,但两files整体5/4pass/1failexit1。D7 confirmed fixture漏completed_at违反原CHECK,最后该拒绝/快照未执行,前序SQL已执行不代表总test通过;no-D1 fallback为public synthetic/null-identity算法控制,非合法产品目标。后于history固定的最小新fixture修复补原CHECK必需completed_at=now(),保名字/七项mutation/error/四表snapshot断言;本机tsc/eslint0、纯1/1exit0,后续0b0c独立Linux两files5/5/failcancelSkip0exit0、tsc/privacy0,最终confirmed拒绝/四表snapshot真实执行,三civil保存控制pass;只限该fixture/SQL源,不改constraint/gate/ACL或冒合法确认/calibration/newopening成功。a8c0全77 accepted offline m1+m2整树difference0仅单chart实验,非joint PG六题。新history exact10.0.32在active10.0.33下真实列表/open/cache/choice/run控制本机1/0pass/1failexit1,原Windows migration入口失败,后续未执行,tsc0/eslint0不是历史链通过;后续1a0c Linux1/0pass/1failexit1实际越过list/旧11参数open/cache/focus/choice,在after summary undefined失败,末transition/action/run未执行。独立golden根因证实新fixture initial绕生产null adapter、已有signature fallback summary,不是已证明生产choice回归;仅新fixture纠正adapter并增initial/reloaded raw/signature缺失控制,after原断言保,后续7a3301独立Linux三files6/5pass/1failexit1、tsc/privacy0,corrected initial/reload隔离及真实list/两旧11参数session open/cache/focus/choice/revision/transition/action全部pass;下一appendV9Turn真实RPC permission denied,runreceipt/finalidentity未执行。整receipt链仍blocked,不grant/owner/definer/新turn旁路。不改production/parser/权限或旁路。不关闭BUG,固定源scope见PROGRESS。
- 防复发:采用/确认门、BUG-690 来源不变;BUG-691 默认医院记录/偏移分钟/两路选择不能消失,文案必须以实际落库 date/time 算偏移;date/time 跨午夜一起存;>61 分钟所有交付/采用/timeline/气泡均不得推荐 D9/D10;旧会话缺段证据安全回退,新持久opt-in缺证据明确未知不得混为legacy。既有断言改动必须列原值/新值/原因。
- 相关记录:BUG-1105、BUG-690、BUG-691、BUG-621、BUG-595/597;任务书 T4/D7;本轮 PROGRESS。
- 复发自:无(产品修订与既有接口能力不足)
- 修复版本:
codex/rectification-varga-resolution-20260930,未提交/未推送/未部署。
BUG-1118 | 注销冻结触发器引用不存在的 new.amount,会让所有扣点写入报错(门禁拦下,未上线)
- 状态:resolved(代码 + 本机真实 PostgreSQL 17 数据库测试;未部署)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/supabase/migrations/20260930020000_account_deletion_requests.sql::refuse_charge_while_deletion_pending,挂在usage_reservations、birth_time_rectification_billing、credit_transactions三张表的 insert 触发器上。 - 现象:合规轮推 staging(
2f6581ff)后门禁 validate 失败;本机复现:record "new" has no field "amount",计费、免费完成、人物删除释放预留等 4 个数据库测试失败。若上线,所有咨询 / 校正扣点写入都会报错。 - 根因:同一个触发器函数服务三张表,只有
credit_transactions有amount列;PL/pgSQL 在另两张表上解析new.amount时即报错,表名判断在前也挡不住。执行方本机无 Docker,数据库测试被跳过,问题只在门禁里暴露。 - 修复:改为
(to_jsonb(new) ->> 'amount')::integer读取。该迁移从未在任何库执行过(门禁在 publish / migrate 之前失败),因此原地修改。另把六张新表补进database-local-business.test.ts的表清单(三栏注释)。 - 验证:本机 PostgreSQL 17.11(zonky 预编译二进制)+ 临时
docker compose替身跑全部database-*.test.ts:修复前本分支比基线多 4 条失败,修复后失败名单与基线逐条相同(6 条,均为替身不支持的 pg_dump / 部署类),并多通过 1 条新测试。 - 防复发:新增或修改迁移的轮次,交付前必须在真实 PostgreSQL 上跑
database-*测试;无 Docker 时用同样的本机替身(方法见 PROGRESS-compliance-launch-20260930)。触发器函数跨表复用时不得直接引用只存在于某一张表的列。 - 相关记录:TASK / PROGRESS-account-deletion-20260930、PROGRESS-compliance-launch-20260930
- 修复版本:分支
codex/compliance-launch-20260930
BUG-1119 | 本人性别有两个入口:设置 →「个人资料」与星盘档案编辑表单
- 状态:resolved(代码 + 回归测试;待部署)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/components/profile-panel.tsx(OwnerGenderSection)、frontend/src/hooks/use-profile-onboarding.ts::persistGender。 - 现象:产品反馈「性别应该在档案里设置而不是在个人资料的账户里设置」——账户设置里出现性别单选,与星盘档案里的编辑表单重复。
- 根因:性别选填(2026-09-27)上线时档案里本人不能编辑,个人资料是本人唯一入口;BUG-1081(09-28)让本人在档案可编辑并带性别后,个人资料那一处没有撤掉。
- 修复:删除个人资料里的性别区与首页 hook 的
persistGender、withSavedGender;本人性别只经档案编辑表单保存(saveSelfGender,单字段 PATCH 不变)。 - 验证:
profile-gender-ui-20260927.test.tsx同名用例改为断言个人资料无性别单选、档案走saveSelfGender(三栏注释);前端全量 4,470 条,失败 24 条与基线逐条相同;tsc 0、lint 0 error。 - 防复发:同一字段新增编辑入口时,同一轮撤掉旧入口并在 DESIGN 标明唯一入口。
- 相关记录:BUG-1081、TASK-consult-gender-optional-20260927
- 修复版本:分支
codex/gender-archive-only-20260930
BUG-1120 | 星盘符号说明气泡在鼠标悬停时频闪
- 状态:resolved(代码 + 回归测试 + 本机 Chromium 复现 / 复验;待部署)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/app/globals.css的全局:has(> [role="tooltip"])规则;frontend/src/components/chart-page/chart-symbol-help.tsx(印度盘与西洋盘共用)。 - 现象:产品反馈「鼠标浮动到图标上方会频闪」。本机 Chromium 用真实 golden 盘复现:在任一符号上移动 1px,气泡被移除再插入,22 个标记全部如此。
- 触发条件:桌面鼠标悬停星盘内任一可说明的标记(行星、上升、星座编号)。
- 根因:侧栏悬停提示的全局规则
:has(> [role="tooltip"]) { pointer-events: none }本为弹出层定位器写的;气泡(role="tooltip")是.chart-page-symbol-help的直接子元素,于是整个星盘舞台在气泡出现时失去指针事件 → 标记收到 pointerout(relatedTarget 为外框)→ 气泡关闭 → 舞台恢复命中 → pointerover → 气泡再开,循环即频闪。BUG-1100 引入气泡时单测只在无样式的 DOM 里跑,看不到这条全局 CSS。 - 修复:全局规则改为
:has(> [role="tooltip"]):not(.chart-page-symbol-help);气泡保留role="tooltip"。 - 验证:
chart-symbol-help.test.tsx新增 CSS 合同(带 pointer-events:none 的 tooltip 规则必须排除星盘舞台);本机 Chromium 复验:同样的移动序列下气泡不再移除 / 插入,只在盘心相邻星座编号间切换文字;chart 相关 158 条测试通过。 - 防复发:新增内联
role="tooltip"时检查全局:has(> [role="tooltip"])规则的作用范围;悬停类交互需在真实浏览器(带全站 CSS)里至少走一次。 - 相关记录:BUG-1100(气泡引入)
- 修复版本:分支
codex/notice-loading-polish-20260930
BUG-1121 | 星盘档案保存失败以绿色「成功」样式显示
- 状态:resolved(代码 + 回归测试;待部署)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/components/chart-profile-form.tsx(notice一律渲染成.form-success)、frontend/src/components/people/people-page.tsx(保存本人 / 他人失败把错误文字写进notice传给表单)。 - 现象:星盘档案「编辑」里保存失败(「保存失败,请重试」或接口返回的错误)显示在绿色成功条里,用户会以为已保存。
- 触发条件:档案编辑表单保存时网络失败或接口返回非 2xx。
- 根因:表单的
notice只有一种样式(成功),调用方却用同一个通道传失败文案;删除相关的两句另有分支挑出来画红色,保存失败没有。 - 修复:随全站提示改 toast(2026-09-30):人物页不再有
notice状态,保存 / 删除 / 查询失败一律notifyError(红色 error toast,6 秒,可关);ChartProfileForm删除notice属性。 - 验证:
self-edit-people-20260928.test.tsx断言本人保存失败走notifyError;people-archive-view.test.tsxD3 断言视图不再渲染.form-error/.form-success、删除失败走notifyError(三栏注释);前端全量 4,475 条,24 条失败与基线逐条相同。 - 防复发:结果提示只经
src/lib/notify.ts,成功 / 失败由调用函数决定类型,不再有「一个文本槽、一种样式」的通道。 - 相关记录:BUG-1081(本人档案可编辑)
- 修复版本:分支
codex/toast-notices-20260930
BUG-1122 | 登录页底部协议链接把登录卡片顶到偏上
- 状态:resolved(代码 + CSS 合同测试;待部署,真机欠)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/app/globals.css的.auth-page/.auth-legal-footer;frontend/src/components/legal/login-consent.tsx::LoginLegalFooter。 - 现象:产品截图——合规轮加上底部「用户协议 · 隐私政策」后,整个登录卡片明显偏上。
- 触发条件:用户登录页(
successPath === "/")渲染底部法律链接时。 - 根因:
.auth-page是display: grid; place-items: center的单列网格,页脚作为第二个网格项进入文档流,卡片与页脚被当成一组居中,卡片因此上移页脚高度的一半多。 - 修复:
.auth-page改为三行minmax(0,1fr) auto minmax(0,1fr),卡片固定第 2 行、页脚第 3 行贴底;767px 以下为1fr auto,页脚在全高卡片之下,不与卡片重叠。 - 验证:
legal-consent-20260930.test.tsx新增 CSS 合同(三行网格、卡片第 2 行、页脚第 3 行贴底、手机两行、不再place-items: center);前端全量无新增失败。真机(iPhone / 桌面)走查欠,见docs/testing/compliance-launch-20260930.md。 - 防复发:往居中网格里加新元素时给它指定行,不依赖自动排布。
- 相关记录:合规轮 A(用户协议 / 隐私政策 / 登录同意)
- 修复版本:分支
codex/toast-notices-20260930
BUG-1123 | 点「我的报告」要等很久才进页
- 状态:resolved(代码 + 回归测试;待部署,真机体感待产品复核)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/app/(app)/reports/page.tsx、frontend/src/components/personal-report/personal-report-center.tsx、frontend/src/lib/secondary-page-data.ts - 现象:产品反馈「点击我的报告 跳转页面的时间很长」。
- 触发条件:从任一页面点侧栏「我的报告」。
- 根因:三段串行等待叠加,且用来掩盖等待的预取被丢弃。① 页面
force-dynamic且无loading.tsx,客户端跳转必须先等一轮服务端 RSC 渲染才切页(/reports并不读 cookies,是纯客户端外壳;BUG-716 已把/chart改成静态外壳,/reports当时没跟上,还被stale-client-recovery.test.ts钉住)。② 进页挂载时invalidateReportsPage()重置缓存,侧栏悬停 / 按下时已发出的GET /api/reports结果被作废,再发第二次同样的请求。③ 列表接口本身(登录校验 → 列表 → 失败段落)排在后面。 - 修复:去掉
force-dynamic,/reports成为静态外壳;进页不再作废缓存:有缓存直接显示并后台重读,无缓存则加入正在进行的预取请求(同一 inflight)。删除行时仍作废缓存,旧 GET 不能让已删行复活。 - 验证:
report-list-paging-20260930.test.tsx「entering 我的报告 joins the sidebar prefetch: one GET, not two」——预取后挂载只发 1 次 GET(修前 2 次);report-row-delete.test.tsx删除后旧 GET 不复活仍通过;stale-client-recovery.test.ts断言改为不再 force-dynamic(三栏注释)。未在真实 staging 计时,体感待产品复核。 - 防复发:次级页(chart / ephemeris / people / reports)一律静态外壳 + 客户端取数;进页 effect 不得 reset 共享缓存,只能 join 或后台重读。
- 相关记录:BUG-716、BUG-1104
- 修复版本:分支
codex/reports-list-paging-20260930
BUG-1124 | 「过往的报告」只显示最新 20 份,更早的无法访问
- 状态:resolved(代码 + 回归测试;待部署)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/app/api/reports/route.ts(GET)、frontend/src/lib/report-cursor.ts(新)、personal-report-center.tsx - 现象:产品反馈「过往报告列表应该加一个分页」;核查发现接口写死
.limit(20)且无游标,第 21 份起在列表里永远看不到。 - 根因:列表接口初版只按
created_at desc取前 20 行,没有分页参数;前端一次性渲染全部返回行。 - 修复:
GET /api/reports?subject=&limit=10&before=<created_at,id>键集分页,按(created_at desc, id desc)排序、取limit + 1判断是否还有更多,返回{ reports, nextCursor };非法游标 400。不带limit视为首页 10 份(原为 20)。前端 10 份一批 +「加载更多」(产品选定);3 秒轮询与刷新只重读首页并合并到已展开的列表,不丢已加载的更早页;删除本地移除;换人物重置。.or(created_at.lt…,and(…))与双.order的写法和/api/sessions游标同一套,自托管兼容层已在该接口上线使用。 - 验证:
report-list-paging-20260930.test.tsx7 条(游标编解码与 Date 行、接口源码合同、合并规则、客户端 URL 与 before、加载更多追加 / 加载中 / 到底隐藏、静态外壳与样式);前端全量 4,478 条,失败 24 条与基线逐条相同,无测试名消失。未跑test:db(本轮不动表;游标查询写法与已上线的会话分页相同)。 - 防复发:列表接口不得用写死的上限代替分页;新列表按
session-cursor/report-cursor的键集模式。 - 相关记录:BUG-1123
- 修复版本:分支
codex/reports-list-paging-20260930
BUG-1125 | 首页揭幕后今日一句才出现,问候「突然跳一下」
- 状态:resolved(代码 + 回归测试;待部署,真机待产品)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/components/daily-starlanguage-binder.tsx、frontend/src/components/starter-home.tsx(StarterGreeting)、globals.css.starter-greeting-sub。 - 现象:产品反馈「首页加载完之后这个标语会突然跳一下才出来」——问候语底对齐在输入框上方,下方一句今日星语在揭幕后才出现 / 换字,把问候整体顶上去。
- 触发条件:冷启动首页,当前人物为本人,人物目录(
/api/chart-library)晚于准备阶段其余数据返回。 - 根因:Binder 在人物目录未就绪(
!subjectReady)时把今日卡置为unavailable;unavailable计为已结算,准备阶段不再等它,首页先揭幕;目录回来后 Binder 才去取卡(pending → ready),副行在揭幕后出现或从静态句换成真句。本人资料本不依赖人物目录,等待是多余的。另外副行无任何进入动效,出现即硬切。 - 修复:只有当前人物是他人时才等人物目录,且等待期间置
pending(揭幕会等它,受 4s 准备超时约束);本人直接取卡。副行按文本key重挂载,进入时 240ms 淡入 + 4px 上移,减少动效时无动画。 - 验证:
daily-starlanguage.test.ts守卫断言改写(三栏注释);新增loading-motion-20260930.test.tsx(副行淡入规则与 key);前端全量失败名单与基线逐条一致。浏览器级「冷启动无跳动」留真机(docs/testing 清单)。 - 防复发:准备阶段里「还不知道」必须是 pending,不得写成 unavailable;首页揭幕后出现的文字一律带进入动效。
- 相关记录:BUG-1040(暖快照)、BUG-1104 / 1109 / 1110(今日句库与预取)
- 修复版本:分支
codex/loading-motion-20260930
BUG-1126 | 报告阅读页切换中文 / English 后正文不换
- 状态:resolved(代码 + 回归测试;待部署,真机待产品)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/components/personal-report/personal-report-page.tsx(PersonalReportPagemarkdown-ready 分支);所有带英文版的新报告。 - 现象:点 English 后地址栏变为
?lang=en、开关显示已选中,但正文和目录仍是中文;带?lang=en打开后点中文,正文仍是英文。导出的英文版正常。桌面与 iPhone 均复现。 - 触发条件:报告同时带核对表(
factTables/factTablesEn,真实报告都有),在阅读页切换语言。 - 根因:
PersonalReportMarkdownView与ReportFactTables是同级元素,key 都是shown,发生重复;React 清不掉旧节点,旧语言正文留在最上面,新语言正文追加在后面(Claude 在 staging 线上 JS 上复现:正文区块 1→2→3 份,开发版报Encountered two children with the same key)。响应里没有核对表时不复现,所以只测组件的用例(report-english-edition.test.tsx)没有拦住。引入提交b2ad772e。 - 修复:两处 key 改为
markdown-${shown}/facts-${shown},仍随语言重新挂载,不再重复。 - 验证:新增
frontend/tests/report-language-switch-20260930.test.tsx(整页渲染 + 真实引擎虚构盘 golden 的中英正文与核对表;中→英→中、英→中两条):修复前 2 条均失败(正文区块 2 份),修复后通过;前端全量失败清单与基线逐条一致;tsc / lint / build 见docs/tasks/PROGRESS-report-language-switch-stale-20260930.md。浏览器级走查见docs/testing/report-english-edition-20260929.md第 10、11 步。 - 防复发:同一父级下按语言重新挂载的多个子元素,key 必须带各自前缀;语言切换要有整页测试,数据带齐核对表。
- 相关记录:BUG-1106~1108(跨语言保留章节)
- 修复版本:分支
codex/report-language-switch-stale-20260930
BUG-1127 | 首页冷启动接口串行四轮,「正在载入账户」等得久
- 状态:resolved(代码 + 回归测试;待部署,真机待产品)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/lib/session-list-context.tsx(loadSessionList)、(app)/page.tsx取校正入口摘要的 effect。 - 现象:真机进站长时间停在「正在载入账户」。
- 触发条件:新开页面的冷启动(暖快照 BUG-1040 只在同一文档内生效)。
- 根因:账户 → 人物目录 → 会话列表 → 校正入口摘要依次等待,其中只有「会话列表要知道当前人物」是真依赖。Claude 在 staging 线上页面用 CDP 给接口统一加 600 ms 延迟实测:从首个接口到揭幕共 4 轮串行,约 2.5 s。另外,这些请求要等全部首屏 JS 执行完才开始发,DOMContentLoaded 后约 1 秒没有任何可并行的网络请求(2026-09-30 追加,任务书 T8)。
- 修复:①
loadSessionList与loadSubjectCatalog把账户、人物目录、按本地记住的人物取的会话列表放在同一轮发出;人物目录回来后当前人物变了,或本地记住的账户与这次返回的账户不同,就丢弃这份会话列表,按正确的人物重新取;首次登录(本地没记住账户)不再作废刚读到的人物目录。② 校正入口摘要改在账户阶段就发(rectification-entry-summary-read.ts),首页只取用一次。③ T8:根 layout 加一段内联 ES5 脚本(只在/生效),HTML 一到就发出账户、人物目录、会话列表、模型目录四个请求,挂在window.__jyotishaBootReads上;启动代码对每个请求只取用一次,超过 30 s 就不信任,失败时回退到正常请求;请求路径和参数与正常请求共用boot-early-reads.ts里的常量。 - 验证:
frontend/tests/home-first-load-20260930.test.tsx(T1 六条、T8 十条:同一轮发出、人物被剔除后丢弃预取、换账户丢弃、401 照样登出、早发失败回退、只取用一次、30 s 失效、脚本是 ES5 且只在/生效、早发请求与正常请求逐字节相同)。本地生产构建 + 测量脚本frontend/scripts/measure-home-first-load.mjs(接口统一延迟 600 ms):串行轮数 4 → 2;不限速时揭幕约 2.7 s → 0.9 s;模拟 1.6 Mbps / 150 ms 慢网时 8.0 s → 5.6 s,首个接口从「首屏 JS 下完后 +30 ms」提前到「首屏 JS 下完前约 3.2 s」。数字见docs/tasks/PROGRESS-home-first-load-20260930.md。 - 防复发:冷启动新增的请求先问两件事:它依赖谁;能不能放进 HTML 早发。预取出来的列表必须带上「为谁取的」(人物、账户),对不上就丢弃。早发请求与正常请求共用
boot-early-reads.ts里的常量。 - 相关记录:BUG-936、BUG-1021、BUG-1040、BUG-1104
- 修复版本:分支
codex/home-first-load-20260930(ab656e0dT1、2fd1e2b0T8) - 部署后复测(2026-10-01,TASK-home-first-load-fix-20261001):staging
e600cff5上早发请求在 2357 / 3104 ms 才发出。原因是内联经典脚本排在 3 个样式表之后,按规范要等样式表下载完才执行(script-blocking stylesheets);本地样式表瞬间就到,所以测不出来。修复:改为<script async src="data:text/javascript;base64,…">(bootEarlyReadsScriptSrc(),内容与原脚本逐字节相同),带 async 的外部脚本不受样式表阻塞,data URL 不需要网络往返;构建产物里 Next 保留了async和 data URL。验证:测量脚本新增--css-delay 1500,本地next start下基线e600cff5早发在 1545 / 1558 / 1559 ms(均晚于第一个样式表放行 1528~1543 ms),修复版在 32 / 34 / 45 ms(均早于样式表);iPhone UA + 390×844 视口下 21 / 42 ms,揭幕后window.__jyotishaBootReads只剩startedAt,四个早发结果都已被取用。真实 iOS Safari 未测(环境缺口,见真机清单第 8 步)。防复发:需要尽早执行、又不需要阻塞渲染的脚本,不要写成内联脚本放在样式表之后;本地测量加上 CSS 延迟开关再下结论。修复提交:分支codex/home-first-load-fix-20261001。
BUG-1128 | 每次部署后浏览器重新下载全部 JS 与宋体字体
- 状态:字体部分 resolved(待部署,staging 两次部署地址不变待核);JS 部分 blocked(
supportsImmutableAssets在自托管构建下不生效,见 BLOCKED.md) - 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/next.config.ts(deploymentId)、src/app/fonts/serif-sc/。 - 现象:进站慢、标题宋体很久才出来;staging 一天部署多次,用户几乎每次都是冷缓存。
- 触发条件:任何一次新部署之后首次打开。
- 根因:
deploymentId让 Next 给所有/_next/static资源(含 CSS 引用的字体)加?dpl=<SHA>。内容没变,地址也会变,缓存随之失效。首屏 JS 约 690 KB(压缩后),宋体切片按页面用字 130~170 KB 起。 - 修复:T3-b:30 个宋体切片从
src/app/fonts/serif-sc/挪到public/fonts/serif-sc/,文件名加 sha256 前 10 位(字节不变,git 重命名 100%),serif-sc.css改为引用/fonts/serif-sc/...绝对地址;next.config.ts给/fonts/serif-sc/:path*加Cache-Control: public, max-age=31536000, immutable;build_serif_slices.py与SOURCE.txt跟着改命名规则;OFL.txt 复制到切片旁。按任务书决策 2 推翻serif-headings-contract.test.ts里「bundled by Next, not served from public/」(三栏已写)。T4:在next.config.ts试supportsImmutableAssets: true后,生产构建产物没有/_next/static/immutable/,预渲染 HTML 里 133 处资源地址仍带?dpl=(Next 文档说明该功能要托管 adapter 配合),第一条证据不成立,配置没有改。 - 验证:本地生产构建:CSS 里的切片地址为
url(/fonts/serif-sc/jyotisha-serif-sc-00.1812be0085.woff2),不带?dpl;next start返回Cache-Control: public, max-age=31536000, immutable。tests/test_serif_font_slices.py新增「文件名哈希与内容一致、public 旁有 OFL」;serif-headings-contract.test.ts新增 next.config 长期缓存断言;font-stack-loadable-contract.test.ts按 public 解析绝对地址。 - 防复发:要长期缓存的静态资源不走 Next 打包(打包资源带每次部署都变的
?dpl=),放 public/、文件名带内容哈希、配 immutable 响应头,并用测试钉住「名字对得上内容」。JS 的每次部署失效要等部署方式支持 immutable assets(adapter)后再议,不得为此去掉deploymentId(BUG-936)。 - 相关记录:BUG-936(引入
deploymentId的原因)、BUG-737 - 修复版本:分支
codex/home-first-load-20260930(8112f62b)
BUG-1129 | 加载屏标题用宋体,JS 就绪前先下载 3~4 个字体切片
- 状态:resolved(代码 + 回归测试;待部署,真机待产品)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/app/globals.css.app-loading-content strong。 - 现象:加载屏标题换体晚、会跳一下;字体下载和关键 JS 抢带宽。
- 触发条件:任何冷启动。
- 根因:加载屏标题用
--font-display。「正在载入账户」一行命中 3 个切片(约 130 KB),实测连同副标题共触发 4 个切片请求。 - 修复:
.app-loading-content strong改用--font-body;DESIGN.md 把它列入「刻意用黑体的标题」,并写明加载屏在 JS 就绪前不触发网络字体下载。 - 验证:
serif-headings-contract.test.ts新增一条钉住加载屏标题用黑体。测量脚本:load之前的宋体切片请求从 4 个(00/01/03/04)降到 1 个;剩下的是侧栏品牌字「Jyotisha」(.brand-row,00 号切片,38 KB,只含拉丁字母),它不在决策 1 的范围内,没有改,交产品决定。切片现在长期缓存,每台设备只下载一次。 - 防复发:JS 就绪前就可见的元素(加载屏、静态 HTML 里的侧栏)用网络字体前先想清楚代价;加载屏标题的黑体由合同测试钉住。
- 相关记录:BUG-737、BUG-1128
- 修复版本:分支
codex/home-first-load-20260930(80ea7b0b)
BUG-1130 | 首页首屏 JS 带着首屏用不到的模块(全国城市表、校正代码、研究数据等)
- 状态:resolved(代码 + 回归测试;待部署,真机待产品)——T7 第 1、2 项及日期选择器完成;校正界面与 markdown 渲染未做(见修复)
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
- 影响面:
frontend/src/data/china-locations.*及以值方式导入它的src/lib/home-types.ts、global-birth-payloads.ts、birth-rectification-payload.ts等;首页首屏 chunk。 - 现象:进站等待长的原因之一,首屏 JS 约 520 KB(brotli,不含
noModule补丁)。 - 触发条件:任何冷启动。
- 根因:Claude 用 source map 构建按来源统计首屏 JS:全国城市表
china-locations.json(brotli 47 KB,实测)经home-types.ts等以值导入进入首屏;约 20 KB 的校正研究封存评测 JSON 进入首屏;首屏还带着校正 / 生时录入代码(未压缩约 226 KB)、react-day-picker+date-fns(约 94 KB)、markdown 渲染(约 80 KB),而空白首页用不到这些。估计合计可以挪走约 150 KB(brotli),约占首屏三成。 - 修复:① 城市表:客户端改为按需加载(
china-locations-client.ts),home-types.ts不再以值的方式导入;selectedBirthPlace仍是同步的,读已加载的表。人物目录(包括本人资料)里只要有「只有省市编码、没有坐标」的老资料,就在upsertSelfChart和揭幕之前先加载城市表;出生地选择器与人物档案页继续静态引入城市表(数据跟着 /people 与引导页的代码包走)。② 研究数据:confirmation-gate.ts不再整个导入rectification_sealed_holdout.v1.json,只保留用到的 6 个字段,合同测试逐字段钉住它与 JSON 一致。③ 引导页(出生地选择器、城市表、生时录入、日期选择器都挂在它下面)改为按需加载:资料未填完时,冷启动在揭幕前预加载,React.lazy拿到同步兑现的 thenable,揭幕同一帧就画出来。未做:校正界面、markdown 渲染按需加载——需要在「打开校正、打开会话、落在?c=会话」几个入口都加揭幕前等待,改动面大,按让步顺序留给下一单。 - 验证:首页首屏 JS(排除 noModule,本地构建实测):29 个文件 / 1994 KB / brotli 516 KB / gzip 611 KB → 28 个 / 1608 KB / brotli 431 KB / gzip 504 KB(brotli −16.5%,gzip −17.5%;超出 ±2% 属预期);城市表、封存评测 JSON、日期选择器已不在首屏 chunk,markdown 仍在。测试:老资料(河北石家庄长安区、北京东城区两组虚构编码)在加载表后逐字段等于改动前的算法;冷启动遇老资料先加载表;首屏模块不再以值导入城市表和封存 JSON;资料未填完时揭幕前等引导页预加载、填完的不加载;预加载后引导页首帧即渲染。
- 防复发:首页首屏只放空白首页用得到的东西;只有少数人或少数时刻用得到的模块(引导、老资料兼容、研究数据)按需加载,并在「一定会用到」的那一刻之前(揭幕前)预加载。别把整个研究 JSON 导入客户端模块。
- 相关记录:BUG-1127、BUG-1128
- 修复版本:分支
codex/home-first-load-20260930(b743b16b、e2c3791f) - 修复版本:—
BUG-1131 | 盘型口径的 12 参数开案包装以属主身份调用 11 参数开案,首页 / 新建开案 42501
- 状态:resolved(Claude 2026-10-01:真 PostgreSQL 17 上
database-segment-opening.test.ts修复前 42501、修复后 1/1;skill-registry-database.test.ts锁定 12 参数包装 prosecdef=false;全量 test:db 与 staging 失败清单一致;产品负责人 10-01 真 Docker(Linuxpostgres:17-alpine)test:db 80/79/1,唯一失败为解包目录无 .git 的环境用例,开案用例 ok;未部署) - 首次发现 / 最近更新:2026-09-30 / 2026-10-01
- 影响面:
frontend/supabase/migrations/20261001020000_rectification_segment_checks.sql的open_agentic_rectification_case_v2(…, p_product_domain text);frontend/src/lib/rectification-agentic/v9/case-service.ts::openRectificationCase对intent !== "session"(首页、新建)一律走该 12 参数版本。 - 用户现象(若部署):从首页或「新建」发起生时校正全部失败,接口报
permission denied for function open_agentic_rectification_case_v2(42501);按会话打开历史 Case 不受影响。 - 触发条件:任何首页 / 新建开案。
- 根因:12 参数包装是
security definer,以属主schema_owner身份调用既有 11 参数open_agentic_rectification_case_v2;而20260814010000_immutable_skill_registry.sql的 ACL 对账块把 11 参数函数的 EXECUTE 从除service_role外所有角色(含属主)收回。改成security invoker后又暴露第二层:运行角色对agentic_rectification_cases/turns没有直接读表权限,包装里「是否待开场」的exists(...)被拒(permission denied for table agentic_rectification_cases)。 - 修复:包装改为
security invoker(调用方 service_role 本就可执行 11 参数开案与initialize_agentic_rectification_domain_v1);「是否待开场」判断移入新的属主函数agentic_rectification_opening_pending_v1(uuid,uuid),只授权 service_role。未给属主或任何非 service_role 角色补 EXECUTE,未改 08-14 迁移。两条新迁移尚未部署到任何环境,原文件修改。 - 验证:本机无 Docker,Claude 用独立 PostgreSQL 17.9 +
docker/psql本地替身跑frontend/tests/database-segment-opening.test.ts:修复前 1/0/1(42501,与执行方 DinD 现场一致);修复后 1/1/0。测试新增断言:新属主函数 authenticated 不可执行;12 参数包装prosecdef = false。 - 同轮修正的测试缺陷:该测试第二段「真实扫描」开案的出生快照缺
birth_time_source,被 11 参数开案的agentic_rectification_profile_incomplete校验拒绝——此段此前一直被 42501 挡住从未执行。原值:快照无birth_time_source;新值:birth_time_source: "family_exact"(与同文件第一段一致);原因:满足原开案资料完整性约束,不放宽约束。 - 防复发:新增 SECURITY DEFINER 函数若调用其他 RPC,必须确认被调函数 ACL 仅 service_role 时属主不可调;优先
security invoker包装 + 属主只读小函数。真库database-segment-opening.test.ts锁定prosecdef与 ACL。 - 相关记录:BUG-1115、BUG-1116、BUG-1117、BUG-621。
- 复发自:无(新迁移引入,未部署)
- 修复版本:
codex/rectification-varga-resolution-fix-20261001(未部署)。
BUG-1132 | 普通对话回答像谜语:四步开场形状逼出格局名、大运拟人、扮演象,问父母时爸妈揉成一段
- 状态:resolved(代码 + 合同测试;未部署,真机待产品)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:普通对话(本命 / 无出生分钟 / 申报时段)的回答形状提示词:
frontend/src/mastra/product-voice.ts(productConversationVoice、natalSpokenReportContract)、frontend/src/mastra/index.ts、frontend/src/mastra/skill-binding.ts、frontend/src/lib/consultation-thinking-plan.ts、frontend/src/app/api/consult/route.ts(旧路径用户回合)。生时校正不受影响。 - 现象:产品真机(staging,deepseek-v4-flash)问「我和父母关系如何」,开场是自造四字格局名、「推这一段的是某某大运,修形式的是某某子运」「别去应某星的冷,去扮演某星的照看」,爸妈揉在一段里,没有分开讲和妈妈、和爸爸、他们怎么对我;读者评价「还是废话」。
- 触发条件:任何本命首轮回答,问到多个对象时最明显。
- 根因:2026-09-17(
84b293fb)定的四步开场形状(反差并命名格局 → 谁在推谁在修 → 别去应 X 的象去扮演 Y 的象 → 行动,≤ 400 字无标题)来自一段单对象、单时段的流年范文,被强制用在所有问题上;模型逐格填空。≤ 400 字、只容一个反差,问多个人时没有位置分开讲。同一形状文字抄在 7 处。不是 BUG-1070 复发:1070 只把反差的取材收窄到盘上,形状本身没改。 - 修复:产品 10-01 授权推翻四步形状(TASK-consult-plain-answer-20261001 D1–D3、D5、D7、D8)。新 ANSWER SHAPE:开场一到三句人话、第一句是结论;问题里点到几个人或子问题就分几段(H2 用问题里的词,父母必分妈妈 / 爸爸);每段先人话、括号里给依据;固定标题
## 盘上依据(原「盘里支持这个判断的地方」,可省)→## 时间怎么看(可省)→## 这周可以做的一件事。人话自检:删掉括号后不懂占星的人也能看懂;禁格局名、大运拟人与角色修辞、星名当形容词的主人。希望纪律去「扮演象」。取消开场 400 / 首轮 900 / 追问 200 字上限,讲清楚为准,保留「只说一遍」与追问不重开骨架。形状只在consultation-thinking-plan.ts定义一处(NATAL_ANSWER_SHAPE_SUMMARY、natalAnswerShapeBody()),其余位置引用。思考栏开场节改为在任意 H2 出现时完成、被省略的节在后面标题出现时完成。 - 验证:
consultation-voice-contract、consultation-thinking-plan、consult-answer-the-question-20260927、consultation-entrypoint合同测试改断言(三栏见 PROGRESS)并新增「形状只定义一处」「开场节在对象段标题处完成」两条;git grep "扮演\|谁在推\|命名成一个格局\|外松内紧" -- frontend/src0 行;tsc 0、lint 0 error、npm test 失败名单与基线一致、build/Static。真实模型输出:本机无模型凭据,留待部署后按docs/testing/consult-plain-answer-20261001.md。 - 防复发:回答形状只在一处定义,合同测试锁定摘要、合同、用户轮、思考栏一致且不含旧修辞与字数上限;新口吻样例必须是「多对象分段」与「单对象不拆段」各一,且通过人话自检。
- 相关记录:BUG-1070、BUG-1071、BUG-1072、BUG-1073、BUG-1133、BUG-1134
- 复发自:无(推翻 2026-09-17 产品定稿的形状)
- 修复版本:分支
codex/consult-plain-answer-20261001(未部署)
BUG-1133 | 父母数据卡不分母亲、父亲,模型混用依据
- 状态:resolved(代码 + golden 测试;已部署 staging)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
frontend/src/lib/consultation-evidence-card.tsEVIDENCE_CARD_SPECS.parents。 - 现象:父母题回答把太阳、月亮、4 宫、9 宫放在一句里讲,分不清哪条说妈妈、哪条说爸爸。
- 触发条件:任何 parents 领域回答。
- 根因:parents 卡只给
houses: [4, 9]与planets: ["Sun", "Moon"],没有标注角色,也没有 Jaimini 母亲代表星 MK。 - 修复:parents 规格加
karakas: ["MK"]与roles;卡上roles.mother(4 宫、宫主、月亮、MK、D12 第 4 宫星座与落星)、roles.father(9 宫、宫主、太阳、D12 第 9 宫);PiK 只在引擎表里有时才加(当前引擎 7 karaka 方案没有 PiK,不造、不记缺口);「母亲 / 父亲」标签进EVIDENCE_CARD_LABELS;Agent 指令说明 roles 的用法。 - 验证:
consult-evidence-card-20260927.test.ts新增两条:golden 三张盘逐字段核对 roles(宫主取自本节、MK 存在、D12 宫位按 D12 上升推算、无缺口);删掉 MK 时记parents.karaka.MK缺口。原有「每个卡值都是引擎值」与体量上限测试照常通过。 - 防复发:说多个人的领域,卡上必须写明每条依据属于谁。
- 相关记录:BUG-1132
- 修复版本:分支
codex/consult-plain-answer-20261001(未部署)
BUG-1134 | 回答拿空宫直接下结论(「4 宫、9 宫空宫,感情不靠自动流动」)
- 状态:resolved(提示词 + 合同测试;未部署,真机待产品)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:普通对话提示词。
- 现象:回答把「空宫」当成结论依据。Jyotish 判宫看宫主,空宫本身不说明弱。
- 触发条件:问题宫没有落星时。
- 根因:没有任何规则约束空宫推断。
- 修复:共享口吻「推断边界:空宫不单独下结论——要说这个宫,就说它的宫主落在哪、什么状态」;本命合同 Never 一条;中文用户轮与英文形状摘要各一句。
- 验证:
consultation-voice-contract.test.ts、consultation-thinking-plan.test.ts锁定这几句。真实模型输出待部署后真机第 1 步。 - 防复发:同上合同测试。
- 相关记录:BUG-1132
- 修复版本:分支
codex/consult-plain-answer-20261001(未部署)
BUG-1135 | 生时校正题干出现两遍:被替换的旧题仍打印题干,同一题又在独立问题块再出一次
- 状态:resolved(Claude 2026-10-01 验收:变基 staging
209352eb后 npm 4842/24 失败清单同基线、新增 11、0 丢失;按镜像 COPY 清单构建通过;gzip +0.05%;快速门 Python 1036/0;DOM 级题干恰好 1 次测试先红后绿;真机待产品) - 首次发现 / 最近更新:2026-09-29 / 2026-10-01
- 影响面:
frontend/src/components/rectification-message-entry.tsx、src/lib/rectification-snapshot-messages.ts::placePersistedQuestion、src/lib/rectification-chat-view.ts(BUG-917 的unansweredDeadChoiceOnMessages)、src/lib/rectification-agentic/v9/turn-question.ts::attachQuestionsToTurns。 - 用户现象:一条校正回复里出现「题干、题干、A–D」;另一种形状是只剩一行没有选项的题干,下面写「没有拿到下一个问题」,而 Case 其实有一道可答的题。
- 触发条件:同一探针被重新 set-focus(旧焦点已链接到最新助手轮、被
superseded且没人答;新焦点尚无asked_turn_id);或正文末尾带着题干句、问题由客户端挂到这条消息上。 - 根因:(1) GET 组装只在最后一条助手轮没有链接焦点时才把悬空的活焦点挂上去,旧死焦点占着位置,活焦点落到独立问题块;(2) 消息组件对死题仍打印题干(BUG-917 画法:题干无选项);(3) 客户端
placePersistedQuestion遇到目标消息挂着不同焦点就走独立块,且挂题时不剥正文题干;(4) BUG-917 的「死卡 → unavailable」判断没看 Case 是否另有活题。 - 修复:GET 与客户端都让活焦点替换「superseded 且无答案」的死焦点;挂题时用既有
stripQuestionSentences剥正文题干;死题或被替换的未答题在消息上什么都不画(当前焦点在 busy / 只读时仍保留题干);Case 另有活题时死题不再把缺口判成 unavailable;复制文本跳过死题。 - 验证:
frontend/tests/rectification-dup-question-20260926.test.tsx新增三条 DOM 测试(真实RectificationAgenticChat+ 真实attachQuestionsToTurns):同题重新出题、客户端守卫、正文残留题干——修复前全红(题干 2 次 / 0 个可点选项),修复后题干恰 1 次、4 个可点选项。 - 防复发:任何题干可见位置(消息内、独立块、正文残留)都在同一组 DOM 测试里按「出现次数 = 1」锁定;新增挂题路径必须复用
stripQuestionSentences与questionIsDeadUnanswered。 - 相关记录:BUG-585、BUG-917、BUG-1045、BUG-1046、BUG-678。
- 复发自:BUG-585 / BUG-1045(题干两遍)。旧防线只覆盖「同一焦点」的重复(BUG-1046 的
placePersistedQuestion只摘同focus_id的副本),没覆盖「两个焦点、同一句题干」;BUG-917 有意保留死题的题干,正好与新题形成两遍。原测试都只用单焦点夹具。 - 修复版本:
codex/rectification-message-cleanup-20261001(未部署)。
BUG-1136 | 生时校正证据轮逐条复述用户说的经历,一次说十几件时正文占满屏
- 状态:resolved(Claude 2026-10-01 验收:变基 staging
209352eb后 npm 4842/24 失败清单同基线、新增 11、0 丢失;按镜像 COPY 清单构建通过;gzip +0.05%;快速门 Python 1036/0;DOM 级题干恰好 1 次测试先红后绿;真机待产品) - 首次发现 / 最近更新:2026-09-29 / 2026-10-01
- 影响面:
frontend/src/lib/rectification-agentic/v9/agent-run-finish.ts、agent-run-attempt.ts、host-fallback.ts、user-copy.ts::evidenceTurnRecap、case-dossier-response.ts(每轮recorded_evidence)、turn-question.ts::recordedEvidenceByTurn、rectification-message-entry.tsx、src/mastra/agentic-rectification.ts两套提示。 - 用户现象:开场一次说了 12 件经历,回复第一段是一整句「记下了:2000年 随家人搬家、2006年 小学毕业、……」,问题被挤到屏幕下方。
- 触发条件:一轮里入账多件经历。
- 根因:提示要求证据轮正文逐条复述「记下了:年 月 事件、…」,件数没有上限。
- 修复:批次有入账条目时,证据轮正文由服务器按条目写——两件以内直接列出,三件以上「记下了 N 件事。」;案例快照给每条助手回复带上本轮入账清单(证据账本,排除拒绝与被替换),三件以上在消息里收起显示。提示改为「复述由服务器写,你不逐条复述」。中文日期标签直接接事件(「2016年入学」)。
- 验证:
rectification-v9-agent.test.ts新增 12 件入账 → 正文与落库均为「记下了 12 件事。」;rectification-message-cleanup-20261001.test.tsx覆盖复述规则、账本分组(拒绝 / 被替换排除)、12 件收起渲染与 2 件不收起。 - 防复发:复述只来自服务器批次结果与证据账本,不来自模型正文;新增入账路径须经
acceptedRecapLines/recordedEvidenceByTurn。 - 相关记录:BUG-606、BUG-633、BUG-635、BUG-1055。
- 复发自:无(文案与形态问题)。
- 修复版本:
codex/rectification-message-cleanup-20261001(未部署)。
BUG-1137 | 生时校正回复结算后仍显示「已完成 N 步」系统内部步骤
- 状态:resolved(Claude 2026-10-01 验收:变基 staging
209352eb后 npm 4842/24 失败清单同基线、新增 11、0 丢失;按镜像 COPY 清单构建通过;gzip +0.05%;快速门 Python 1036/0;DOM 级题干恰好 1 次测试先红后绿;真机待产品) - 首次发现 / 最近更新:2026-09-29 / 2026-10-01
- 影响面:
frontend/src/components/rectification-message-entry.tsx(timeline/vargaSentence在结算后为空)。 - 用户现象:每条校正回复顶部一行「已完成 3 步」,展开是「看了你的资料 / 记录你说的经历 / 准备好下一个问题」——对用户没有信息量;真机复制出的文字里还多一个「1.」。
- 触发条件:任何已结算的校正回复(含刷新、历史打开)。
- 根因:校正与普通对话共用结算后的步骤回执(BUG-1050 只做了去重)。「1.」:步骤列表是
<ol>,收起后仍在 DOM 里;按纯文本复制时浏览器给有序列表项加序号(屏幕上list-style: none不显示)——为推断,无浏览器复现;回执整块不再渲染后不再可能出现。 - 修复:校正回复结算后不渲染步骤时间线(含分盘说明句);写作中步骤行仍是等待态;普通对话结算回执不变。
- 验证:
rectification-message-cleanup-20261001.test.tsx三条渲染测试——校正已结算无「已完成」与步骤名(改前红)、写作中仍有步骤与「正在分析」、普通对话结算仍「已完成 1 步」;rectification-opening-plain-20260926.test.tsx、rectification-timeline-adapter.test.ts断言按三栏改。 - 防复发:普通对话回执由范围锁测试守住;校正结算回执的重新出现会让上述测试红。
- 相关记录:BUG-1049、BUG-1050、BUG-606(分盘说明句只在活动展开区)。
- 复发自:无(产品修订)。
- 修复版本:
codex/rectification-message-cleanup-20261001(未部署)。
BUG-1138 | 校正采用后聊天不知道哪些分盘判断不了,照样按存下的分钟读 D9 / D10
- 状态:resolved(Claude 2026-10-01 验收:变基 staging
c304cf3a后 npm 4827/24 失败清单同基线、新增 16、0 丢失;按镜像 COPY 清单构建通过;gzip ±0.00%;替身真库采用测试 2/2;快速门 Python 1036/0;含 Claude 存盘分钟头段判定修正;未部署时以部署后 health 为准,真机待产品) - 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
frontend/src/lib/report-candidate-range.ts::loadReportCandidateRange(聊天与报告共用)、frontend/src/lib/consultation-route-service.ts::prepareConsultationRoute、frontend/src/mastra/consultation-workflow.ts::runConsultationWorkflow/projectEvidenceContract、frontend/src/lib/consultation-birth-time-mode.ts。 - 用户现象:盘型口径的交付卡对宽窗口用户写「D9 / D10 分不开」,但采用后在聊天里问婚恋 / 事业,回答照样按存下的那一分钟读 D9 / D10 下结论,没有任何提示。
- 触发条件:资料
active_birth_provenance.contract = "segment-v1",且该次校正结果segment_summary中目标分盘档位为blocked或indistinct。 - 根因:盘型口径只改了校正交付卡;下游聊天只拿一个分钟与采用区间,不读分盘档位(旧版校正同样如此,旧版连卡片都不提示)。
- 修复:新模块
rectification-adopted-chart-tiers.ts按来源记录读该 Case 的档案(get_agentic_rectification_case_dossier,服务账号),解析segment_summary(采用的那次结果;若之后同一 Case 有更新的结果则用更新的,不跨 Case),5 分钟缓存;共用的范围读取函数在有不可判分盘时附带unreliableVargas。聊天把它写进排盘工具输入,工作流调用时从发给引擎的载荷里剔除,写入answer_policy.unreliable_vargas/deterministic_claims_forbidden_for与一句服务端说明(VOICE「采用后判断不了的分盘」)。为不改另一单正在改的consult/route.ts,挂接点放在路由已经注入的范围读取函数里。 - 验证:
tests/rectification-adopted-chart-tiers.test.ts(segment-v1 / 旧采用 / 未校正 / 结果缺失 / 摘要损坏五种输入、跨 Case 拒绝、缓存);tests/rectification-chart-tier-caveats-consult.test.ts(范围读取 A/B、工具输入 A/B 与旧资料字节快照、引擎载荷不含该字段、模型数据包带策略与说明);旧资料聊天工具输入、引擎请求、工作流上下文、模型数据包与基线437dcecb逐字节相同(脚本见 PROGRESS);tests/database-segment-adoption.test.ts追加真库读取断言(PostgreSQL 17 本地替身,正式证据待真 Docker)。 - 防复发:读取失败只记日志不阻断聊天;非 segment-v1 资料不发 RPC;模型侧沿用
mastra/index.ts既有「deterministic_claims_forbidden_for是硬禁令」规则,不新增提示词。 - Claude 验收修正(2026-10-01):执行方实现总读 Case 最新结果、只看档位;但存下的分钟不随新结果变,且 D7 在头段无交集时退回 D1 头段中点,存盘分钟的 D9/D10 可能不在该盘头段——只看档位会把存盘分钟读不到的星座说成「较可信」(放宽置信边界)。改为
unreliableChartsForSavedMinute:档位不可判,或(非全窗唯一且)存盘分钟window_offset_minutes不在该盘头段,或偏移缺失,均标不可判。新增两条测试(存盘分钟落在非头段 → D9 标注;偏移缺失 → 所有窗内会变的盘标注),原测试 fixture 补真实采用必带的window_offset_minutes(原值:无该字段 / 新值:60 或该 summary 的 adoption_minute.offset / 原因:真实 segment-v1 来源恒含此字段,新规则依赖它)。 - 相关记录:BUG-1115~1117、BUG-1131、BUG-1139、BUG-690
- 复发自:无
- 修复版本:
codex/rectification-chart-tier-caveats-20261001(未合入、未部署)
BUG-1139 | 校正采用后新报告不标注判断不了的分盘
- 状态:resolved(Claude 2026-10-01 验收:变基 staging
c304cf3a后 npm 4827/24 失败清单同基线、新增 16、0 丢失;按镜像 COPY 清单构建通过;gzip ±0.00%;替身真库采用测试 2/2;快速门 Python 1036/0;含 Claude 存盘分钟头段判定修正;未部署时以部署后 health 为准,真机待产品) - 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
frontend/src/lib/personal-report-longform-generate.ts::generatePersonalReportLongform、新模块frontend/src/lib/personal-report-chart-caveats.ts;报告读取范围同 BUG-1138 的共用函数。 - 用户现象:宽窗口用户采用后生成的个人报告,D9 / D10 图盘与数值位置表照常呈现,读者不知道这几张盘在出生时间范围里会变。
- 触发条件:同 BUG-1138,且生成新报告(长报告路径;写作模型路径在生产关闭)。
- 根因:同 BUG-1138,报告链路不读分盘档位。
- 修复:长报告取回引擎正文后、计算快照哈希前,在不可判分盘的图盘标题(
D9 …、Navamsha、D10 …)与数值位置表标题下各插一行固定提示,中英文正文各用本语言;按标题里的盘代号匹配,不匹配正文;幂等。图盘卡片解析允许标题与图之间恰好一段说明,提示行成为图注,网格不拆。发给引擎的载荷不变,不改 Python;写作模型路径(demoteThemesMissingRequiredCharts)生产关闭,本轮不接。 - 验证:
tests/rectification-chart-tier-caveats-report.test.ts:旧资料 / 空档位正文逐字节不变;读者版真实输出(虚构出生)D9+D10 命中 5 个标题、只标 D9 时命中 3 个、表格行数不变、幂等;英文版命中英文标题且提示无汉字;密度版真实输出图盘卡片数不变且 3 张卡带提示图注;生成流程 A/B:引擎载荷相同、旧资料落库正文与引擎原文逐字节相同、新资料只多 3 行提示。 - 防复发:提示只加在新生成报告的快照里,旧报告不回改;匹配基于引擎盘代号,若引擎改标题需同步
CHART_PATTERNS与测试。 - 相关记录:BUG-1138、BUG-1115~1117、BUG-1016(图盘网格)
- 复发自:无
- 修复版本:
codex/rectification-chart-tier-caveats-20261001(未合入、未部署)
BUG-1140 | staging 镜像打包失败:frontend/scripts 下的研究脚本引用了 frontend/tests 的夹具
- 状态:resolved(修复已推 staging;以门禁 publish 通过与
/api/health部署版本为最终证据) - 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:Gitea
backend-quality-gate的 publish 作业(deploy/railway-web.Dockerfile构建阶段npm run build)。validate 通过、publish 失败,未部署,staging 停在上一版。 - 用户现象:盘型口径(BUG-1115~1117、1131)推送后 staging 不更新。
- 触发条件:
frontend/scripts/rectification-segment-persisted-replay.ts(盘型口径研究的 PG 持久回放 harness)import … from "../tests/helpers/postgres-fixture.ts"。web 镜像只复制frontend/src、frontend/scripts等,不复制frontend/tests;frontend/tsconfig.json的 include 为**/*.ts,next build类型检查覆盖 scripts,于是TS2307 Cannot find module '../tests/helpers/postgres-fixture.ts'。 - 根因:本地与验收机都有
frontend/tests,tsc --noEmit与next build均通过,门禁 validate 也在完整检出上跑,没有任何一步按镜像的复制清单构建。 - 修复:harness 移到
frontend/tests/research/rectification-segment-persisted-replay.ts(相对路径随之调整),Python driverscripts/research/varga_resolution_persisted_replay.py指向新路径;新增frontend/tests/runtime-sources-no-test-imports.test.ts:src/与scripts/不得 import /new URL引用tests/。 - 验证:按
railway-web.Dockerfile的 COPY 清单从提交git archive出构建目录,修复前npm run build报上述 TS2307、exit 1,修复后 exit 0、/Static;守卫测试 2/2,且对旧 harness 文本判定为违规;移动后 harness 1 例 ±10 冒烟runner_exit=0。 - 防复发:守卫测试进
npm test(门禁 validate 必跑);错误台账 ERR-113。 - 相关记录:BUG-1115~1117、BUG-1131。
- 复发自:无
- 修复版本:
codex/staging-publish-fix-20261001。
BUG-1141 | 生时校正缺少度数级时间信号(角度类技法研究)
- 状态:closed_by_design(2026-10-01 离线研究:9 项预先登记检验 0 项显著,见下)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:生时校正计分只有「大运 + 宫位 / 分盘宫位」类规则(
scripts/active_rectification_event_engine.py),灵敏度约 1 分钟 ≈ 大运边界 3.8 天;产品追问能否用度数级技法进一步缩小范围。 - 用户现象:答完选择题后分盘与分钟仍分不开(盘型口径已诚实呈现,BUG-1105 / 1115~1117)。
- 触发条件:无(研究项)。
- 根因(研究结论):在 77 例公开 AA 传记事件上,行运压角(TR)、次限推运角(SP)、太阳弧角(SA)三类度数级技法,真实日期下真值分钟的排位与乱序日期无差别:主窗口 ±30 真实日期胜过乱序 31% / 58% / 45%(TR 1° / 2° / 3°)、38% / 37% / 56%(SP)、8.5% / 1.0% / 29%(SA),Bonferroni 门槛 99.44%;年龄结构保持对照同样无信号;按精度、年代拆分 45 格无一达到 95%。SA 1° / 2° 反向偏差校正后不显著、机制未解释。
- 修复:不改代码;产品口径(盘型 + 可信度、不承诺分钟)不变。
- 验证:
scripts/research/angle_timing_research.py,结果docs/research/angle_timing_{a1,jitter,subsets}_2026_10_01.json两次复跑逐字节一致;tests/test_angle_timing_research.py11 passed;生产代码零改动。 - 防复发:再提「换某种时间技法就能定到分钟」前,先按本研究的乱序 / 年龄保持双对照在 v5 上预先登记检验;不得在本轮结果上事后挑选容许度、角点或领域映射。
- Claude 验收正对照(2026-10-01):
scripts/research/angle_timing_positive_control.py每例埋 3 个「行运土星距真实上升 <0.15°」的日精度事件,用同一ranks_for:真值分钟平均排位 0.077(77/77 例 <0.15);同日期整年挪动 0.502;真实事件 0.514。流程能检出真信号,真实事件无信号的结论成立。 - 相关记录:BUG-1091、BUG-1105、BUG-1115~1117。
- 复发自:无
- 修复版本:研究分支
codex/rectification-angle-timing-research-20261001。
BUG-1142 | 普通对话答题时钟 70 s 容不下推理模型;无出生分钟路线只有 110 s 一道闸
- 状态:resolved(分支已验收;部署后以
/api/health版本为准) - 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:普通对话(
/api/consult,agentic 运行时)写回答的阶段。生时校正有自己的预算(RECTIFICATION_RUN_BUDGET_MS),不受影响。 - 用户现象:选推理较重的模型时,回答写到最后一节被截断,显示「回答未完成,已保留现有内容;本次不会扣点」。
- 触发条件:10-01 真实模型对比(
docs/testing/consult-plain-answer-20261001-model-runs.md)中,deepseek-v4-pro父母题从发出到写完 77 s,超过 70 s 答题时钟;deepseek-flash30–41 s,其中约 85% 是推理 token。同日取消了回答字数上限(TASK-consult-plain-answer-20261001 D8),回答平均变长约 50%。另一条:无出生分钟 / 公开日历路线(usesPublicDailyGeneralAgent)没有工具,answerReady永远为 false,答题时钟只在续写和重试时才启动,整段模型循环被 110 s 工具时钟管着。 - 根因:70 s 按非推理写作模型的吞吐(约 90–100 tok/s)定,而回答步骤开着 provider thinking,推理 token 也从同一只钟里扣;无工具的路线没有「拿到计算结果」这个交接点。
- 修复:
CONSULTATION_ANSWER_TIMEOUT_MS70_000 → 300_000(产品 10-01 定 5 分钟,只防卡死、不限长度);createConsultationRunClock加answerFromStart,route 对usesPublicDailyGeneralAgent的路线从第一步起走答题时钟;maxDuration240 → 480(110 + 300 + 准备与结算)。工具阶段 110 s、领域预算(65 s / 2 个领域,由工具时钟与 45 s 预留推出,不读答题时钟)不变。截断语义不变:答题时钟到点仍是answer_truncated、不扣点(BUG-1051)。 - 验证:
frontend/tests/consult-answer-clock-20261001.test.ts(答题时钟 300 s / 工具 110 s / maxDuration 留余量;领域预算仍 65 s、2 个且公式不读答题时钟;answerFromStart时工具钟到点不掐循环、答题钟到点仍掐;route 对无分钟路线接线);两条既有断言按三栏改;全量测试失败名单与基线逐条一致。 - 防复发:答题时钟的注释写明它只防卡死;下游各层(Caddy 无响应超时、Node 无自定义超时、undici bodyTimeout 300 s 为「两块数据之间」的空闲时间、推理 token 也是流式下发、预留扣点租约 15 分钟)均在 410 s 以上或不按总时长计。
- 相关记录:BUG-1051、BUG-1053、BUG-944。
- 复发自:无
- 修复版本:
codex/consult-answer-clock-20261001
BUG-1143 | 健康类区分选择题写不进焦点表,流程静默提前交付
- 状态:resolved(2026-10-01 Claude 直接执行;77 例真库持久回放修前修后对比;未部署时以部署后 health 为准)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
frontend/src/lib/rectification-agentic/v9/server-focus.ts(区分类焦点写targetDomain)、answer-choice.ts::persistFocusAfterChoice(写入失败被吞)、所有生时校正用户。 - 用户现象:答完两三道选择题就直接出交付卡,后面还有家人、感情、搬迁等可答的题却不再问;与「按盘型挑题」开关无关。
- 触发条件:任一轮的区分题落在健康领域(引擎领域名
health_pressure)。 - 根因:区分类焦点把计划层领域名原样写入
agentic_rectification_conversation_focuses.target_domain;该列检查约束只收education/career/relationship/relocation/finance/health/family/other。写入抛target_domain_check违约,被persistFocusAfterChoice的 catch 吞掉(日志仅retry next focus failed reason=tool_failed),流程落到 exhaustion 出口terminalNote交付。采集类焦点早经persistableFocusDomain映射,09-13530f260f(BUG-661~663)的区分类写入分支漏掉。假数据库测试不校验约束,所以一直没测出来。 - 修复:新增
focusTargetDomain,所有意图的焦点写入都经persistableFocusDomain;method-followup.ts三处焦点与探针的领域比较改走sameCollectDomain(BUG-672 规则);不改数据库约束。 - 验证:
frontend/tests/rectification-focus-target-domain-20261001.test.ts(引擎全部领域名 × 四种意图映射后都在迁移的约束清单内;健康区分焦点存health且与health_pressure探针同域;源码合同禁止原样写领域名与probe.domain === focus.targetDomain)。真库(PostgreSQL 17 替身)持久回放 77 例 × ±10/±30(docs/research/varga_resolution_persisted_onoff_{before,after}_fix_2026_10_01.json):平均问题数 ±10 2.82→4.56、±30 3.58→5.43;问满 6 题 21→50、30→69;默认顺序下 D9 / D10 头段命中 ±10 65→70 / 64→65、±30 38→46 / 39→52;真值段保留全部 76/76;修后日志retry next focus failed0 条。 - 防复发:焦点写入只能经
focusTargetDomain;引擎领域与表约束的对照由测试逐值锁定。 - 相关记录:BUG-672(同族:健康 / 职业别名)、BUG-586、BUG-661~663、BUG-1144。
- 复发自:BUG-672(别名未经归并,写入侧新分支漏掉)。
- 修复版本:
codex/rectification-segment-order-20261001。
BUG-1144 | 「按盘型挑题」默认关闭
- 状态:resolved(2026-10-01;默认开启窗口 ≤ 61 分钟)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
core/segment-probe-order.ts、v9/score-persist.ts。 - 用户现象:盘型口径实现(BUG-1116)把按段选题做成环境变量
RECTIFICATION_SEGMENT_ORDER=on才启用,生产默认按引擎信息增益出题,分盘头段命中低于研究「按段」列。 - 根因:实现单允许 T5 延后;执行方因真库持久回放未跑通而默认关闭。
- 修复:
segmentOrderEnabledFor(windowMinutes, flag):默认窗口 ≤ 61 分钟开启,off关闭,on为研究包络。 - 验证:修复 BUG-1143 后的 77 例真库持久回放,开 vs 关:±10 D9 70→71、D10 65→67;±30 D9 46→51、D10 52→55;真值段保留四格全部 76/76。修 BUG-1143 前 ±30 开启曾丢 3 例真值段(
…before_fix…json),根因即 BUG-1143(开启后健康题被排到第一位,更早撞上写入失败)。frontend/tests/rectification-segment-order-default-20261001.test.ts。 - 防复发:放宽到 > 61 分钟前须用同一回放脚本在对应窗口重跑并逐格核对真值段保留。
- 相关记录:BUG-1116、BUG-1143、BUG-1105。
- 复发自:无
- 修复版本:
codex/rectification-segment-order-20261001。
BUG-1145 | 普通对话进度栏提前打勾:回答提纲在算盘前就显示为「已完成」
- 状态:resolved(2026-10-01 Claude 直接执行;单测复现修前失败、修后通过;未部署时以部署后 health 为准)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
frontend/src/lib/consultation-run-timeline.ts(普通对话进度栏的实时与历史重建共用同一个 reducer);生时校正进度栏不用这个 reducer,不受影响。 - 用户现象:发问后,「先回答你问的这件事」「把判断钉在盘上」「时间窗口怎么看」「这周可以做什么」四行立刻全部打勾,而这时「正在计算本命盘…」还在转,正文一个字都没写。
- 触发条件:任何本命普通对话首轮(通用 / 每日 / 申报时段的计划节同理)。
- 根因:服务端在第一个流块就下发回答提纲(
think.plan+ 每节一条thinking.section);reducer 把每节做成一行 live 的 think 行,而completeLiveThink在下一个事件(下一节、tool.started)到来时把所有 live think 行标成完成。提纲是「回答将怎么写」,不是已走过的步骤,却按步骤渲染,于是算盘一开始四行就假完成。自 09-18 进度栏设计(94c1e81f)起即存在;BUG-1132 之后回答按人分段、「盘上依据」「时间怎么看」可省,提纲与真实正文进一步对不上。 - 修复:产品 10-01 选 B——提纲不再成行。
think.plan不改状态;thinking.section只把领域与对照项暂存为计算行的明细(calculateHints),在计算行出现时补上(实时与历史一致);正文开始(answer.delta)时计算行收口为完成;历史重建的思考文本改走thinking.delta行,不再挂在提纲行上。服务端仍下发think.plan/thinking.section(公共事件合同与存档字段不变,供applyThinkingSectionProgress等使用),客户端只是不再把它们画成行。 - 验证:
frontend/tests/consultation-run-timeline.test.ts新增三条(算盘前提纲不成行、只有「读取分析方法」完成;整轮结束无提纲行、无 live 行;历史重建与实时同形且计算行仍有明细),三条在修前代码上失败、修后通过;改写一条既有断言(三栏见测试注释)。 - 防复发:进度栏只画真实发生的步骤(方法、计算、写作、模型推理);回答提纲若要再上屏,必须由正文标题驱动、不能用「下一事件到来即完成」的规则。
- 相关记录:BUG-942~944(进度 / 思考 / 正文分通道)、BUG-1074(发出即显示排队行)、BUG-1132(按人分段)。
- 复发自:无
- 修复版本:
codex/consult-timeline-no-plan-20261001。
BUG-1146 | 采用后的核对题只有题干、没有选项按钮,刷新也不出;题挂在上一题的旧回复下
- 状态:resolved(2026-10-01 Claude 直接执行;单元与客户端回归测试。第一版
282455cc真机仍挂错位置:采用轮次的 requestId 不是 uuid,真库拒写、被吞(BUG-1149),补修后真库重放通过;真机复核以docs/testing/rectification-post-adopt-card-20261001.md为准) - 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
v9/method-followup.ts(已采用时承接活焦点的 keep 分支)、candidates/accept/route.ts、v9/answer-choice.ts::writeAdoptFollowupTurn(新)、rectification-chat-accept-run.ts。所有点「采用」后出现核对题的校正,盘型口径之前也会。 - 用户现象:真机点「采用」后出现「2016 年 9 月那次升学…」题干,下面没有四个按钮;刷新后仍然没有。题干挂在答上一题那条消息(「已记录,范围没变;…」)下面。
- 触发条件:采用成功,
persistNextInterviewIfIdle落了一道 reverse_verify 核对题;且采用发生在交付后 60 秒内(BUG-596 的already_delivered窗口)。 - 根因:(1) 已采用时 keep 分支按活焦点重建 followup,丢了探针身份
semantic_key/candidate_split_hash;重建出的卡片 id 是reverse_verify:<主题>:score,而落库题号是探针身份probe:<key>,projectRectificationChoiceCard按「不串题」规则返回 null,GET 每次都这样重建,所以刷新也没有按钮。(2) 采用接口只落题、不写对话轮次,丢掉了hostNarration;客户端随后的只读运行在 60 秒窗口内被判already_delivered,也不写轮次,题没有自己的消息,GET 把它挂到了最近一条助手消息(上一题的回复)下。 - 为什么旧测试没拦住:BUG-498 的回归用例里活焦点
questionId与重建 id 本来就一致或为空,走不到 id 比较这一步;采用路径测试只测接口返回,不测采用后的对话轮次。 - 修复:keep 分支从已落库焦点带回
semantic_key与candidate_split_hash(BUG-498 的「无 semantic_key 时 id 必须相等」校验不放宽);采用接口在非幂等采用后用hostNarration写一条确定性轮次(requestId<req>:adopt;有核对题时开头是「已采用 HH:MM。再核对一件过去的事。」,因为题干在挂卡后会从正文去掉),把未挂轮次的 reverse_verify / out_of_sample_check 焦点挂到这条轮次,并标记交付;接口返回adopt_turn_id,客户端拿到后用快照把这条消息换掉进行中那一行,不再发只读运行;幂等采用或无话可说时保持原来的只读续跑。 - 验证:
frontend/tests/rectification-post-adopt-card-20261001.test.ts(带年份核对题与已知事件质量题两种:持久化 → GET 重建均出 4 个选项,修前两例都红;采用轮次的 requestId、p_user_message = null、开头句、焦点挂到新轮次、交付标记;无话可说不写轮次;客户端有adopt_turn_id时不发只读运行且进行中行被替换,幂等采用仍续跑)。 - 防复发:已采用的 keep 分支必须保留探针身份;采用后出题必须有自己的轮次,不得依赖只读运行补写。
- 相关记录:BUG-498(同一症状:采用后核对卡为空)、BUG-536~538、BUG-596、BUG-678、BUG-1135。
- 复发自:BUG-498(当时修的是 id 前缀不一致;之后核对题落库改用探针身份,keep 分支没跟上)。
- 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1147 | 盘型口径的交付与答题回复仍念分钟排名,并与卡片自相矛盾
- 状态:resolved(2026-10-01 Claude 直接执行;单元测试;真机复核同 BUG-1146 清单)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
v9/choice-action.ts::composeChoiceNarration、v9/answer-choice.ts::adoptHostNarration、user-copy.ts::deliveryTurnNarration;只影响盘型口径(Case 有rectificationDomain且推断态有segment_summary)。 - 用户现象:同一屏里,答题回复说「范围没变;04:52–04:53 领先、05:00–05:05 落后」;交付卡写「D9 天蝎(较可信)」,旁边的话却说「这几个候选按现有信息分不开」;「这只是代表性候选,不是已确认的唯一出生分钟」卡片里一次、正文里又一次。
- 根因:三处分钟口径文案没有按盘型口径分支:答题回复的分数移动句、交付旁白末尾追加的分钟候选提示(
rangeNarrowHint交付分支)、交付正文固定追加的边界句(卡片已有)。 - 修复:盘型口径下答题回复不带分数移动句(只说「已记录,范围没变。」);交付旁白不再追加分钟候选提示;交付正文不重复边界句。分钟口径的文案不变。
- 验证:
frontend/tests/rectification-post-adopt-card-20261001.test.ts(盘型口径回复无「领先 / 落后」、分钟口径仍有;盘型交付正文不含边界句且含「D9 上升:天蝎(较可信)」,分钟口径仍以边界句结尾)。 - 防复发:盘型口径的用户可见文案不得出现分钟排名;边界句由卡片负责。
- 相关记录:BUG-1115~1117(盘型口径实现)、BUG-1085、BUG-1086。
- 复发自:无
- 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1148 | 普通对话回答夹英文 / 梵文音译,同一个东西几种叫法
- 状态:resolved(2026-10-01 Claude 直接执行;单元测试 + 真实模型复测;真机复核以部署后
docs/testing/consult-plain-answer-20261001.md为准) - 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:普通对话所有领域的首轮与追问(本命 / 无出生分钟 / 申报时段共用
product-voice.ts与consultation-thinking-plan.ts的形状说明)。 - 用户现象:产品要求「其他领域不说人话就改」。10-01 用产品提供的临时 DeepSeek key(deepseek-flash)对公开名人盘跑婚恋、财富、健康、子女、学业、迁移、年运、时机、综合九个领域:整体已是人话、分段、空宫看宫主;但正文出现 sade sati、vargottama、「6.61 rupas」「Sade Sati 未启动」等英文 / 梵文音译,计都写成「克图」,大运下面一层时叫子运、时叫中运 / 副运 / 小段 / 小运。
- 触发条件:证据卡与引擎字段是英文 / 梵文名,形状说明只要求「术语首次用白话套住」,没规定用哪个中文名、也没禁音译。
- 根因:BUG-1132 的人话自检只管「括号外看不看得懂」,没有名词表。
- 修复:形状说明(
product-voice.tsANSWER SHAPE、NATAL_ANSWER_SHAPE_SUMMARY、natalAnswerShapeBody()、追问轮指令)加一条:名词只用中文、全篇一个叫法(罗睺、计都;大运 → 子运 → 小运),不写英文或梵文音译(sade sati、vargottama、rupas 等,给出中文替代说法),力量只说强 / 中 / 弱。 - 验证:
consultation-thinking-plan.test.ts、consultation-voice-contract.test.ts各加一条;真实模型复测同 5 个问题:英文 / 音译 6 处 → 0,「克图」4 处 → 0,子运混称 5 处 → 1(那一处是在解释「子运是大运里的一小段」)。 - 防复发:名词规则与人话自检同处定义;新增术语先进这条名词表。
- 相关记录:BUG-1132(人话自检)。
- 复发自:无
- 修复版本:
codex/consult-term-glossary-20261001。
BUG-1149 | 采用轮次与交付收尾轮次的请求号不是 uuid,真库拒写且被吞
- 状态:resolved(2026-10-01 Claude 直接执行;本机真 PostgreSQL 17 重放:修前报 uuid 语法错误,修后采用轮次写入成功)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
v9/answer-choice.ts::writeAdoptFollowupTurn(BUG-1146 新增)、exhaustionGateRequestId→persistExhaustionGateTurn(turn-exit.ts、agent-run-finish.ts、ensureNonTerminalTurnExit三处调用)。 - 用户现象:BUG-1146 部署后真机点「采用」,核对题仍挂在上一题「已记录,范围收到…」下,没有单独的采用消息;交付收尾那句(无轮次时补写的旁白)在库里从来没有,刷新或重挂后就不见了。
- 触发条件:每次采用;每次走交付收尾补写。
- 根因:
append_agentic_rectification_turn现行(V10)重载的p_request_id是 uuid;<请求号>:adopt与<轮次号或案例号>:gate都不是 uuid,PostgreSQL 报invalid input syntax for type uuid。两处调用方都在 try/catch 里只打一行 warn,交付标记却已经打上,所以只读续跑也不写轮次。假数据库测试不校验类型(BUG-1146 的单元测试断言的正是错误的req-1:adopt)。 - 修复:
deterministicTurnRequestId(seed)用 sha256 派生 v5 形 uuid(同一种子同一 uuid,重试仍幂等);adoptTurnRequestId、exhaustionGateRequestId改用它。 - 验证:
frontend/tests/rectification-post-adopt-card-20261001.test.ts(两种请求号符合 uuid 格式且同种子稳定;采用轮次断言改为adoptTurnRequestId("req-1")——原值req-1:adopt/ 新值派生 uuid / 原因:原值在真库必然失败)。本机 PG17 + 真引擎重放虚构案例:修前invalid input syntax for type uuid: "<uuid>:adopt",修后adopt_turn写入。 - 防复发:写轮次的请求号一律 uuid;新增确定性轮次必须在真库(PG 替身)至少跑一次。
- 相关记录:BUG-1146、BUG-596、BUG-1150。
- 复发自:无(
:gate自 BUG-596 一侧引入后一直未在真库写成功过) - 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1150 | 采用后核对题点任何选项都报「这次没提交上」
- 状态:resolved(2026-10-01 Claude 直接执行;本机真 PostgreSQL 17 重放:修前
stale_probe,修后作答成功并收尾) - 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
v9/server-focus.ts::expectedAnswerSchemaFor(reverse_verify / out_of_sample_check 的探针身份)。所有计分的采用后核对题。 - 用户现象:采用后核对题四个按钮出现了,点任一个都弹「这次没提交上,请再点一次。」,再点也一样。
- 触发条件:采用后核对题计分(
scoring为真),且推断态里该探针的 id 带拆分哈希。 - 根因:核对题盖章时不看推断态(BUG-912 防串题),
probe_id写成probe:<语义键>;推断态里同一探针的 id 是probe:<语义键>:<拆分哈希>。作答时前端层按语义键找到探针、用带哈希的 id 写转移,数据库persist_agentic_rectification_inference_transition发现它与活焦点 schema 的probe_id不同,拒为agentic_rectification_stale_probe,接口回非 200。 - 修复:核对题盖章时,推断态里若有同语义键、同拆分哈希的那一道,就写它的 id;找不到时保持原样(仍不盖无关的高增益探针,BUG-912 不放宽)。
- 验证:
frontend/tests/rectification-post-adopt-card-20261001.test.ts「a scoring post-adopt check carries the state's probe id」(修前红:实际probe:career.2018.dasha_activation)。本机 PG17 重放虚构案例:修前stale_probe,修后applied: true、收尾句「前事核对到这里…」。 - 防复发:计分题的 schema
probe_id必须与推断态 id 同形;采用后流程的回归需在真库走到「作答」一步。 - 已存在的卡:修复前已落库的核对题仍是旧 id,作答仍会被拒;可点「这题跳过」,或新开一次校正。
- 相关记录:BUG-912、BUG-498、BUG-1146、BUG-1149。
- 复发自:无
- 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1151 | 盘型口径的验证报告仍按分钟排名,与卡片「D9 较可信」打架
- 状态:resolved(2026-10-01 Claude 直接执行;产品 10-01 选 A)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
v9/skill-verification-report.ts::buildSkillVerificationPacket(新增segmentProduct)、mastra/rectification-v9-tools.ts(盘型口径报告打开开关)。报告每次读取时现算,已有校正打开即生效。 - 用户现象:交付卡「查看验证报告」里写「当前候选基本并列:04:53、05:06…」「本命上升(该分钟)」,并附「排名 / 时间 / 相对支持」表;卡片顶部却是「D9 天蝎(较可信)」。
- 根因:盘型口径实现(BUG-1115~1117)只在报告前加了盘型摘要与「只供引擎诊断」一句,并删掉 D9/D10 换升行;八法报告的筛选段与候选窗表仍是分钟口径。
- 决策记录:产品 10-01 选 A——盘型口径下报告去掉分钟排名表、并列/代表分钟行、「本命上升(该分钟)」;保留不可分宽度、事件吻合、Dasha 表与技法审计。分钟口径(旧案例)报告逐字不变。
- 修复:
segmentProduct为真时不输出上述三处;盘型口径报告传segmentProduct: segmentReport。 - 验证:
frontend/tests/rectification-product-delivery.test.ts(真引擎 golden)新增断言:盘型口径报告不含「当前候选基本并列 / 当前代表分钟 / 本命上升(该分钟)/ 相对支持 / 候选窗」,仍含「不可分宽度」「Technique Audit Table」;分钟口径仍含排名表与「本命上升(该分钟)」。去掉开关时 15 条红。 - 防复发:盘型口径的用户可见面(气泡、卡片、报告)都不得出现分钟排名。
- 相关记录:BUG-1115~1117、BUG-1147。
- 复发自:无
- 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1153 | 交付时同一段盘型结论连出两条消息
- 状态:resolved(2026-10-01 Claude 直接执行;回归测试)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
v9/answer-choice.ts::persistExhaustionGateTurn(turn-exit.ts、agent-run-finish.ts、ensureNonTerminalTurnExit三处调用)。 - 用户现象:答完最后一道选择题出交付卡时,「已记录,范围收到 …」下面是一段「D1 上升… 用了 5 道选择题」,紧接着又是一条一模一样的「D1 上升… 用了 5 道选择题」。
- 触发条件:答题回复本身已带交付正文,随后的收尾补写(无轮次时的 gate 轮次)又写同一段。
- 根因:本人 BUG-1149 引入的回归。gate 轮次请求号此前不是 uuid,真库一直写不进去,重复被这个错误掩盖了;BUG-1149 修好请求号后它开始真的落库,与答题回复里的同一段重复。我在 BUG-1149 时判断「无轮次才补写,所以不会重复」,没有核对答题回复已经带了交付正文。
- 修复:
persistExhaustionGateTurn写之前读最近一条助手消息,若已包含同一段(去空白后包含)就不写,只保留交付标记;新增narrationAlreadyInLastAssistant。 - 验证:
frontend/tests/rectification-post-adopt-card-20261001.test.ts「delivery gate turn is not written when the last reply already says it」(已说过→不写;没说过→写一次;两种都打交付标记;去掉判断时实际写 1 次,红)。 - 防复发:补写类轮次一律先查最近一条助手消息;让「原本写不进去」的路径恢复时,要检查它与相邻消息是否重复。
- 相关记录:BUG-596(交付轮连写两条)、BUG-1149。
- 复发自:BUG-596(同一症状:交付连出两条;当时的 60 秒守卫只管无用户消息的二次运行,不管补写轮次)。
- 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1152 | staging 新邮箱用固定验证码登录后被要求设置密码,测试者以为必须用密码登录
- 状态:resolved(2026-10-01 Claude 直接执行;产品 10-01 选「测试通道跳过设置密码」)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
frontend/src/components/email-otp-login.tsx::verifyOtp(新增offersFirstPasswordStep)。只在服务端配置了IDENTITY_TEST_OTP的环境(staging)生效;生产不配置,行为不变。 - 用户现象:打开 staging 登录页只看到「验证码登录 / 密码登录」,没有任何固定验证码的提示;用新邮箱输入固定码后又被拦在「设置登录密码」,没有跳过入口。产品以为 staging 改成要密码才能登录。
- 触发条件:staging 测试通道 + 「验证码登录」+ 该邮箱账户没有密码(新邮箱必然如此)。
- 根因:
verifyOtp在hasPassword()为 false 时一律进入set-password步骤(07-27ab2944f7起),不区分测试通道;验证码通过时会话已经建立,这一步只是界面步骤。BUG-1121/1122(3e0c615f)把登录卡上常驻的固定码提示改成点「发送验证码」后 10 秒的浮层,进页面时看不到固定码,问题更显眼。固定码通道本身一直开着(线上/login带testOtp,发码接口 200)。 - 决策记录:产品 10-01 只选「测试通道验证码登录跳过设置密码」;不恢复常驻提示(BUG-1121/1122 的浮层设计不变)。「注册账号」模式仍设置密码;二步验证仍优先。
- 修复:
offersFirstPasswordStep(testMode, mode)在测试通道 +otp模式下为 false,verifyOtp此时不查hasPassword、直接enterApp()(记录协议同意后进入首页)。 - 验证:
frontend/tests/identity-login-provider.test.ts「staging fixed-code sign-in skips the first-password step」:四种组合(测试/真实邮件 × 验证码登录/注册),以及二步验证分支仍在密码判断之前。修复前该函数不存在,测试红。 - 防复发:测试通道的便利只能挂在
testMode上,不得改服务端鉴权或让生产受影响。 - 相关记录:BUG-1121、BUG-1122。
- 复发自:无
- 修复版本:
codex/staging-otp-skip-password-20261001。
BUG-1154 | 普通对话的引擎输出没有「谁照谁」和燃烧
- 状态:resolved(代码 + 真实引擎测试;已部署 staging)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
scripts/consultation_native_layers.py(新增build_graha_drishti)、scripts/jyotish_engine.py(燃烧规则提成COMBUSTION_ORBS/is_combust)、frontend/src/mastra/consultation-workflow.ts投影白名单。 - 现象:父母、婚姻、健康类回答读了旺弱,没读受冲:土星照太阳、火星照 7 宫、9 宫主距太阳 10° 燃烧这类信号模型一次没提(排查记录
docs/research/consult_affliction_audit_2026_10_01.md)。 - 触发条件:任何普通对话。
- 根因:完整报告在
jyotish_engineStep 5 用aspects.calc_house_aspects算了相位、在 Shadbala 块按度数写了planet['combust'];普通对话工作流走的是另一条排盘路径,响应里既没有相位层也没有combust字段,投影白名单也没有这两个键。 - 修复:
build_graha_drishti用aspects.calc_house_aspects算七颗行星的整宫相位(所有行星照第 7 宫,火星另照 4、8,木星另照 5、9,土星另照 3、10;罗睺、计都只作被照对象),输出每颗星aspects_houses/aspected_by、每个宫aspected_by;燃烧调用引擎同一条规则jyotish_engine.is_combust(原地提取,容许度与比较不变)。挂在chart.consultation_native_layers.graha_drishti,_guard包裹,失败只记blocked。投影新增graha_drishti白名单。jyotish_api_server.py未改动。 - 验证:
tests/test_consultation_native_layers.py新增 3 条:三张公开盘逐星逐宫与calc_house_aspects直接调用一致、燃烧与is_combust一致、罗睺计都不施照;奥巴马土星(1 宫)照 3/7/10、太阳aspected_by含 Saturn、水星combust = true;燃烧规则容许度与边界不变。golden 重生后除新键外与旧 golden 逐值一致(0 处差异)。 - 防复发:同上测试;数据卡测试(BUG-1155)逐值对照引擎层。
- 相关记录:BUG-1054(同一投影白名单曾挡掉嵌套键)、BUG-1133
- 复发自:无
- 修复版本:
19802a48(分支codex/consult-card-affliction-data-20261001,已部署 staging691ee440,2026-10-02 health 核对、/api/account 401)
BUG-1155 | 数据卡没有相位、同宫、燃烧
- 状态:resolved(代码 + golden 测试;已部署 staging)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
frontend/src/lib/consultation-evidence-card.tsbaseNatal、domainSection。 - 现象:卡上行星只有星座、宫位、度数、星宿、逆行、旺弱;宫位只有星座、落星、宫主。四份乔布斯答案都没对出「木星与计都同宫」,模型看不到谁照谁。
- 触发条件:任何普通对话。
- 根因:
PLANET_ROW_KEYS不含相位与燃烧;领域段只列被选中宫位的星,宫主与谁同宫要模型自己去 base 里对。 - 修复:
base.natal.planets[行星]增加aspected_by(引擎值)、conjunct_with(同house的其他行星,选择所得)、combust(只在 true 时出现);两个列表空时不出现。宫位相位放在领域段的宫位行houses[N].aspected_by(选中的宫才有),没有放进 base(字符预算,见 PROGRESS)。引擎没给相位层时meta.gaps记base.aspects,不补算。字符预算按任务书 D6 先压缩格式:antardashas_in_mahadasha改为[主星, 起, 止]短数组,D9 行的vargottama只在 true 时出现;12,000 字符断言一条未改。 - 验证:新增
frontend/tests/consult-card-affliction-data-20261001.test.ts:三张盘逐星对照引擎层(aspected_by / combust / conjunct_with);奥巴马太阳aspected_by含 Saturn、水星combust: true;乔布斯 9 宫主金星conjunct_with含 Rahu;领域宫位行aspected_by逐宫对照;相位层缺失时记缺口不补算;压缩后的子运与 D9 行逐值对照引擎。consult-evidence-card-v2-20260927.test.ts的 D9 行断言按三栏改写(PROGRESS)。三个 12,000 字符断言全过,最大 11,918(泰勒年运,含清单)。 - 防复发:同上。
- 相关记录:BUG-1154、BUG-1133
- 复发自:无
- 修复版本:
19802a48(分支codex/consult-card-affliction-data-20261001,已部署 staging691ee440,2026-10-02 health 核对、/api/account 401)
BUG-1156 | 父母卡缺宫主与代表星的完整行;健康卡缺 1、12 宫,学业卡缺 4 宫;年运卡看不到土星压月亮
- 状态:resolved(代码 + golden 测试;已部署 staging)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
EVIDENCE_CARD_SPECS.health/education、parentsroles、annualslow_transits;consultation_native_layers.build_slow_transits。 - 现象:乔布斯父母答案没读出 9 宫主金星合罗睺、太阳是功能凶星;泰勒健康答案说「两颗星没有受损」(命主在 4 宫与 8 宫主同宫、月亮在 12 宫受土星照,卡上没有 1、12 宫);乔布斯学业卡看不到 4 宫罗睺;年运卡看不到「土星行运压本命月亮」。
- 触发条件:parents / health / education / annual 领域。
- 根因:roles 只给宫主与代表星的名字;health
houses: [6, 8]、educationhouses: [5, 9];引擎行运层没有「土星距本命月亮第几宫」。 - 修复:roles 每个条目加
lord_rows、significator_rows:{house, sign, status, functional, conjunct_with, aspected_by, combust?},functional按该星在引擎功能吉/凶/中性哪张列表里选择(标签 benefic / malefic / neutral 进EVIDENCE_CARD_LABELS);D12 同号宫加lord与lord_sign(该宫宫主在 D12 的星座,读自 D12 行)。health[1, 6, 8, 12],education[4, 5, 9]。引擎slow_transits.saturn_from_moon_house(行运土星星座距本命月亮星座的整宫宫距),年运卡原样抄。 - 验证:新增测试:三张盘 roles 逐字段对照 base 行与功能列表;乔布斯父亲
lord_rows.Venus.conjunct_with含 Rahu、significator_rows.Sun.functional = malefic;泰勒健康卡含 1、12 宫;乔布斯学业卡 4 宫落星含 Rahu;乔布斯年运卡saturn_from_moon_house = 1(行运土星双鱼、本命月亮双鱼);Python 侧三张盘宫距逐个核算。 - 防复发:同上。
- 相关记录:BUG-1133、BUG-1155
- 复发自:无
- 修复版本:
19802a48(分支codex/consult-card-affliction-data-20261001,已部署 staging691ee440,2026-10-02 health 核对、/api/account 401)
BUG-1157 | 瑜伽不分领域:母亲、子女类瑜伽进了财富卡,父母卡、子女卡反而没有
- 状态:resolved(分流;大格局缺席另见 BUG-1159)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:
scripts/consultation_native_layers.py(新增build_yoga_domains、YOGA_PACKET_DOMAINS)、EVIDENCE_CARD_SPECS(yogaTags)、卡片 yogas 段、onCard("yogas")。 - 现象:奥巴马 8 条瑜伽(Matrunasa、Bahu Puthra、Eka Puthra……)原样进财富卡、综合卡;父母卡、子女卡
yogas: false。 - 触发条件:career / wealth / general 卡(及 golden 的 family 路由)。
- 根因:
modules.yogas是 Raman 支持包按路由筛出的规则清单,一份给所有卡;卡上没有领域标签。 - 修复:标签取自管辖该规则的 Raman 包(固定表
YOGA_PACKET_DOMAINS:Nabhasa/Chandra 批 = general、wealth_core = wealth、spouse = marriage、children = children、mother = parents、sibling_core = siblings),按rule_id对照,不按名字猜;新层yoga_domains同时带筛选自己的hit。卡片:parents、children 打开 yogas,只收本领域;career 收 career + general,wealth 收 wealth + general,general 只收 general。yogas 段改为{names, hits, claim_boundaries}(hits= 引擎筛选命中的名字;原count是全量计数,与分流后的名单对不上,去掉)。完整清单仍可一次查阅,所以带标签分流的卡不再把yogas从可补查列表里拿掉(断言三栏见 PROGRESS)。 - 验证:新增测试:annual 路由(20 条全包)下三张盘五个领域的 names / hits 逐条对照引擎层;奥巴马财富、综合卡没有 Matru / Puthra 类,父母卡全是 Matru 类且 hits = Matrunasa,子女卡全是 Puthra 类;标签层缺失时记
<domain>.yoga_domains缺口并退回原清单。Python 侧逐条对照包成员。 - 防复发:同上。
- 相关记录:BUG-1159
- 复发自:无
- 修复版本:
19802a48(分支codex/consult-card-affliction-data-20261001,已部署 staging691ee440,2026-10-02 health 核对、/api/account 401)
BUG-1158 | 功能吉凶层把命主一律标成 yogakaraka,10 宫主太阳在天蝎上升标中性
- 状态:resolved(2026-10-02 产品采纳上游功能吉凶 profile v2,见本条末「决策日志(第三次)」;生时校正算法身份随之升到 scoring-10,见 BUG-1181)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
scripts/functional_benefics.pyderive_functional_benefic_malefic(SOURCE = strict_functional_benefic_malefic_v1,2026-06-2809817b96引入);所有读功能吉凶的地方(数据卡、Technique Audit、报告)。 - 现象:泰勒盘(天蝎上升)火星(1、6 宫主)标 yogakaraka、太阳(10 宫主)标中性;同一规则下奥巴马(摩羯上升)土星(1、2 宫主)、乔布斯(处女上升)水星(1、10 宫主)也是 yogakaraka。
- 触发条件:任何非日月作命主的上升。
- 根因(已核实代码,未定是否缺陷):规则把 1 宫同时算作三方宫和角宫,
owns_trine and owns_kendra对命主恒成立,所以除日月外的命主都进 yogakarakas(也进 benefics);角宫主一律归中性,没有「凶星主角宫转吉」的处理,天蝎上升的太阳因此是中性;6 宫主与命主同星时按「含 1 宫不算凶」处理。模块没有引用典籍或流派出处。常见 BPHS 列表的 yogakaraka 指同时主一个角宫和一个三方宫且不含命宫的行星(天蝎上升无),天蝎上升的功能吉星常列太阳、月亮、木星。 - 修复:未改(任务书红线 3)。
- 验证:无。
- 防复发:待决定。
- 相关记录:BUG-1156(父母卡的
functional取自这一层) - 复发自:无
- 决策日志(2026-10-02):产品 D1(
TASK-consult-career-yoga-functional-20261002)推翻strict_functional_benefic_malefic_v1:yogakaraka 只认同时主角宫(4、7、10)与三方宫(5、9)的单颗星;12 个上升按 BPHS 第 34 章逐上升写死。实现1d039429(分支codex/consult-career-yoga-functional-t1-20261002):BPHS_CH34_TABLE+SOURCE = bphs_ch34_table_v1,每格带出处(公开英文译本 + 梵文逐词对照,第 13、19–44 颂),14 个格保留公式值标formula_fallback,12 上升全格由tests/test_functional_benefics_bphs_table.py锁住。生时校正 v5 77 例评测(硬红线 3):±10 引擎头名 0.1818 → 0.1688、六题回放头名 0.6364 → 0.5974、线上区间回放头名 0.6753 → 0.6364,±30 六题回放 0.4935 → 0.4675,覆盖全部不变,±60 上升;超过 1 个百分点,按红线停在 T1,未合入。逐格旧值 / 新值 / 出处与歧义清单见docs/tasks/PROGRESS-consult-career-yoga-functional-20261002.md。 - 决策日志(2026-10-02 第二次):产品看了 T1 回归数字后决定只合入 yogakaraka 一条:yogakaraka 须同时主 4/7/10 之一与 5/9 之一,1 宫不计入此判定,吉 / 凶 / 中性分组不变(命主仍为吉)。实现
ffafa28b,SOURCE = strict_functional_benefic_malefic_v2;变化:白羊火星、金牛 / 天秤金星、双子 / 处女水星、天蝎火星、射手 / 双鱼木星、摩羯 / 水瓶土星不再是 yogakaraka,分组逐格与 v1 相同。生时校正 v5 77 例改前 / 改后(81f6e513/ffafa28b)三档头名、覆盖、宽度逐项相同(±10 先验 0.1818、六题回放 0.6364、线上区间回放 0.6753,覆盖 0.987),未触发红线。验证:tests/test_functional_yogakaraka_kendra_trikona.py。BPHS 全表与 15 个待复核格见 T1 分支docs/research/bphs_ch34_functional_table_review_2026_10_02.md(c66c122a)。另:协调方随后要求移植上游yinduzhanxinge9beae6b的功能吉凶文件,本会话的自动权限判定拒绝了从另一仓库原样拷入代码,未执行,需用户授权后再做。 - 决策日志(2026-10-02 第三次,产品授权):改为采用上游 Skill 仓同日的功能吉凶 profile v2(
bphs_ch34_general_with_sign_exceptions_v2):BPHS 第 34 章总则(三方宫主吉;3/6/11 宫主与有条件的 8 宫主凶;只管 12 宫的星是条件性的,不自动判凶;日月管 8 宫按例外;只管角宫或只管 2 宫的星判中性)+ 逐上升明文例外(ROLE_OVERRIDES:白羊火星中性、金牛金星凶 / 太阳吉、双子木星凶、处女木星凶、天秤金星中性 / 火星凶、天蝎火星中性 / 太阳吉、射手木星与水星中性、摩羯月亮凶、水瓶火星凶 / 水星中性、双鱼水星凶);yogakaraka 维持严格定义(同时管 4/7/10 之一与 5/9 之一,1 宫不计两次)。每颗星带role_basis,rule_reference指向references/functional-role-profile.md。瑜伽引擎yoga_engine.py的functional_malefics()/functional_benefics()改读同一函数(第一次决策日志第 9 条「两套口径」随之消除)。本条推翻第二次决策日志里的「吉凶分组不变」与 T1 分支的逐格对照表方案;T1 分支codex/consult-career-yoga-functional-t1-20261002不再合入。生时校正 v5 77 例(PM 在57782aea前后同法实测):引擎先验头名 ±10/30/60 0.1818/0.0779/0.0649 → 0.1818/0.0909/0.0779;六题回放 0.6364/0.4935/0.2597 → 0.6364/0.4935/0.3117;线上区间回放 0.6753/0.4935/0.3247 → 0.6623/0.5584/0.4026;覆盖 0.987/0.987/0.974 → 0.987/0.987/0.987。五格上升、一格(区间回放 ±10)−1.3 pp(一例),产品接受。 - 验证(第三次):
tests/test_functional_yogakaraka_kendra_trikona.py(57782aea改写);两份普通对话 golden 用 capture 脚本重生成(PYTHONHASHSEED=0 JYOTISH_API_CHART_CACHE_TTL_SECONDS=0,两次逐字节相同,只有功能吉凶字段变化);frontend/tests/consult-upstream-functional-v2-20261002.test.ts锁 golden 的 profile 与天蝎、处女两个上升的分组;consult-card-affliction-data-20261001.test.ts乔布斯太阳malefic→neutral(三栏在测试注释)。Python 全量失败名单与基线逐条相同。 - 修复版本:
57782aea(移植)+codex/consult-upstream-functional-v2-20261002(golden、算法身份、测试;未推送)。早先部分修复ffafa28b已被取代。
BUG-1159 | 数据卡瑜伽清单是规则候选全集,大格局(Sasa、落陷取消、Gajakesari)进不来
- 状态:resolved(2026-10-02:检测跳过修复见 BUG-1174;大格局以「传统格局」栏上卡见 BUG-1175)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
scripts/jyotish_api_server.py普通对话层 yogas 块(if not compact:分支)、scripts/raman_support_observations.pysieve_yogas_for_routes。 - 现象:卡上的瑜伽名字大多不是这张盘成立的瑜伽;奥巴马盘引擎能检测出 Sasa、Neechabhanga Raja,乔布斯与泰勒能检测出 Gajakesari,都不在卡上。
- 触发条件:任何带 yogas 的卡。
- 根因(已核实):一,
modules.yogas.yogas= Raman 包成员规则的「命中 + 未命中」全集(hits + misses),卡只抄名字不抄hit,未命中的规则看上去像成立的瑜伽(乔布斯 family 路由 8 条全是未命中)。二,Sasa / Neecha Bhanga / Gajakesari 不在任何 Raman 包里,检测到也只进ungoverned_detected,不进modules.yogas。三,规则引擎(yoga_engine.detect_yogas)只在chart.yogas为空时才跑;乔布斯、泰勒的排盘结果自带 1~2 条扩展瑜伽,于是完整检测根本没跑,乔布斯盘本可命中的 Matrunasa 也成了未命中。 - 修复:本轮只做卡侧缓解:yogas 段加
hits(引擎hit原值)。第二、三条属治理范围与检测路径,任务书要求只诊断、不得放宽(AGENTS §8.4),未改。 - 验证:诊断脚本对三张公开盘直接调用
yoga_expansion.detect_all_yogas+yoga_engine.detect_yogas(PROGRESS T4 段)。 - 防复发:待决定。
- 相关记录:BUG-1157
- 复发自:无
- 决策日志(2026-10-02):产品 D2 / D3 决定规则引擎每次都跑(BUG-1174),封闭名单内的大格局单列
traditional_yogas、带固定标签「传统格局,本站未验证,只作参考」(BUG-1175,产品授权仅在这一栏放宽 AGENTS §8.4);Raman 包与modules.yogas不变。 - 修复版本:缓解随
codex/consult-card-affliction-data-20261001;其余随codex/consult-career-yoga-functional-20261002
BUG-1160 | 普通对话没有「先看受冲、再看旺弱」的读法,模型默认「旺 = 好」
- 状态:resolved(文本层;2026-10-02 产品定年运 / 时运字数口径后全部领域生效)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
frontend/src/lib/consultation-condensed-checklist.ts、frontend/src/lib/consultation-methodology.tsconsultationMethodologyForDomains、frontend/src/mastra/index.ts方法说明句。 - 现象:父母题把 9 宫里的火星读成「有分量」,健康题写「两颗星都处在正常状态」,婚姻题「罗睺更多是噪音」;13 份公开名人样稿 11 份漏读关键受冲信号(排查记录
docs/research/consult_affliction_audit_2026_10_01.md第 2 节)。 - 触发条件:任何普通对话;卡上有相位、同宫、燃烧、功能吉凶等事实,但方法段从没说哪些算受冲、受冲与旺弱冲突时听谁的。
- 根因(已核实):数据卡只给事实;方法段只有路由的英文 baseline 与事业 / 婚姻 / 财富三份只讲时间分层的清单,模型只能用常识「入旺 = 好、Shadbala 高 = 没事」。
- 修复:新增
CONDENSED_SHARED_READING_LINES(任务书 T1 定稿六条 + 一条「瑜伽只认 yogas.hits」),由consultationMethodologyForDomains每轮在sections最前推入一次(标题「通用读法 · 先看受冲」),多领域同轮也只一次;括号里的字段名按卡上真实位置改写(见进度记录)。提示词方法说明句同步讲这一段。 - 未修部分(2026-10-02 已解除):产品决定年运、时运与事业 / 婚姻 / 财富同口径(契约 + 卡 ≤ 12,000,含清单 ≤ 18,000);
CONSULT_READING_BUDGET_BLOCKED已删,两条 size 断言改口径,修复随codex/consult-no-presupposition-backtest-20261001。原记录:单领域年运轮「契约 + 卡 + 清单」已到 11,918(泰勒),时运 11,523;加通用读法(733 字符)后年运最高 12,979、时运 12,257,超过consult-condensed-checklist-20260927「size」与consult-evidence-card-20260927「model-visible size」两条 12,000 断言。断言未改;CONSULT_READING_BUDGET_BLOCKED = {annual, timing}让只含这两个领域的轮次保持本单之前的方法段,等产品重定口径后删掉对应项即可。 - 验证:
frontend/tests/consult-affliction-reading-20261001.test.ts(每个非阻塞领域sections[0]是通用读法、多领域同轮只一次、阻塞项被钉住);三个 12,000 断言原样通过。 - 防复发:通用读法的存在与次数、清单字段名都由合同测试钉住;第三单名人回测验收读法效果。
- 相关记录:BUG-1070、BUG-1132、BUG-1134、BUG-1154~1159
- 复发自:无(BUG-1134 只禁空宫下结论,未讲受冲)
- 决策日志(2026-10-02):产品 2026-10-02 决定(名人回测第一轮对照组误报 1→15;Claude 按卡片确定性计数核对,在场与缺席的父亲受冲信号数相同,盘分不出):通用读法第 3 条改为引用
AFFLICTION_RANGE_RULE(consultation-thinking-plan.ts唯一定义)——受冲两个及以上写「压力或距离的迹象」+ 依据 + 现实范围,不断定用户自己更清楚的传记事实,不写反向安慰,段末请用户说一句、全篇最多一处。推翻本单 T1 第 3 条「第一句先写这一面,用『可能』」。提交066558a6;第二轮回测见docs/testing/consult-affliction-backtest-20261001.md。 - 修复版本:
codex/consult-affliction-reading-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401)
BUG-1161 | 父母、子女、学业、迁居、家庭、年运、综合 7 个领域不发任何清单,健康只发英文路由
- 状态:resolved(文本层;年运清单 2026-10-02 随产品字数口径决定下发,见 BUG-1160)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
consultation-methodology.tsdomainMethodology、consultation-condensed-checklist.tsCONSULTATION_CONDENSED_CHECKLISTS。 - 现象:有清单的领域(婚姻、财富)明显好于没有清单的领域;
domains_without_strict_checklist对上述 7 个领域非空。 - 触发条件:问父母、子女、学业、迁居、家庭、年运、综合;问健康时拿到的是英文 strict route。
- 根因(已核实):
domainMethodology7 个领域strictRoute: null,注释规定「路由没声明的清单报缺,不替代」。 - 修复:按任务书决策 D6 对普通对话推翻该注释;写入 8 份中文清单(任务书 §5 T2 定稿文本,只改字段名位置),
strictRoute填本地名consult-<domain>,来源标CONSULT_BRIEF_CHECKLIST_SOURCE。健康清单取代普通对话里的英文 health-timing-strict(报告面不变);时运保留英文路由。 - 验证:同上测试文件逐领域列出
sections标题;domains_without_strict_checklist除年运外全部为空;硬红线 3 测试:清单里的每个小写字段名都是三张 golden 盘某张卡上的真实键。 - 防复发:合同测试列出每领域标题与来源。
- 相关记录:BUG-1160、BUG-1070、BUG-1132
- 复发自:无
- 决策日志(2026-10-02):产品 2026-10-02 决定(名人回测第一轮对照组误报 1→15;Claude 按卡片确定性计数核对,在场与缺席的父亲受冲信号数相同,盘分不出):父母清单第 2 行改为「按通用读法写范围……盘分不出父母在不在身边,不替用户断定」(推翻「第一句写『可能不常在你身边』」);健康清单第 1 行改为「身体这条线有压力的迹象」+ 范围、不写「底子不差」。提交
066558a6。 - 修复版本:
codex/consult-affliction-reading-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401)
BUG-1162 | 事业、婚姻、财富清单只讲时间分层,不讲受冲
- 状态:resolved(文本层;效果待第三单回测)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
CONSULTATION_CONDENSED_CHECKLISTS.career / marriage / wealth。 - 现象:泰勒事业「不靠曝光」(AL 落 10 宫没读);泰勒婚姻「罗睺更多是噪音」;奥巴马财富 8 宫罗睺只读成纠纷。
- 根因(已核实):三份清单没有 AL 落宫、婚姻受冲门槛、8 宫的正面含义。
- 修复:各加任务书 T3 定稿一行(事业 6 行、婚姻 8 行、财富 6 行,仍在 5~8 行)。
- 验证:
consult-affliction-reading-20261001.test.tsT3;consult-condensed-checklist-20260927写作提示词逐行包含测试仍通过。 - 防复发:同上。
- 相关记录:BUG-1160
- 复发自:无
- 决策日志(2026-10-02):产品 2026-10-02 决定(名人回测第一轮对照组误报 1→15;Claude 按卡片确定性计数核对,在场与缺席的父亲受冲信号数相同,盘分不出):婚姻受冲行改为「感情这条线上有波折的迹象」+ 范围、不断定有几段(推翻 T3 定稿「感情可能不止一段 / 可能有断续」);事业清单三层改为「机会出现 / 成形 / 公开落地」,删「面试 / 正式职位」,并加首行「先讲这张盘一生的事业形态……受冲只说阻力和代价,不改写事业形态」。提交
066558a6。 - 修复版本:
codex/consult-affliction-reading-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401)
BUG-1163 | 健康被框成「身心压力」,终身体质写成「作息、睡眠是弱项」
- 状态:resolved(文本层;效果待第三单回测)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
frontend/src/lib/consultation-domain-registry.tshealth、frontend/src/lib/guided-jyotish-topics.ts、frontend/docs/VOICE.md。 - 现象:泰勒健康篇把终身重病、数十次手术的体质读成近期压力问题。
- 根因(已核实):领域名「身心压力」与默认问题「近期的身心压力模式」把问题限定在近期压力。
- 修复:
label: "健康",aliases加身心压力,prompt: "我的身体底子怎样,哪些方面要多留意?";无出生分钟问法同步;claimBoundary、confidenceCap: "low"不变。git grep "身心压力" frontend/src只剩 alias 一处。 - 验证:
consult-affliction-reading-20261001.test.tsT4;consultation-domain-registry.test.ts问法断言(三栏见进度记录)。 - 防复发:同上。
- 相关记录:BUG-1160、BUG-1161
- 复发自:无
- 修复版本:
codex/consult-affliction-reading-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401)
BUG-1164 | 写作形状预设对象在场:缺席的父亲被写成「话少、分量重、靠做事表达」
- 状态:investigating(产品 10-02 定「迹象 + 范围 + 请用户告知」口径,第二轮回测对照组误报 15→6 且均轻度;通过线未全达,已部署 staging 供真机)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 编号说明:任务书预留 BUG-1167~1172;开工核对最大号为 BUG-1163(第二单用到 1163),按连续编号改用 1164~1168:T1 = 1164、T2 = 1165、T3 = 1166、T4 = 1167、T2b = 1168。
- 影响面:
frontend/src/lib/consultation-thinking-plan.tsnatalAnswerShapeBody(新常量NATAL_SECTION_RULE)、frontend/src/mastra/product-voice.tsANSWER SHAPE 第 3 条、frontend/docs/VOICE.md§7 ③。 - 现象:奥巴马父母「他对你有要求,也有分量」「去问爸爸的意见」;基线回测 10 份父母严重冲突,缺席的父亲一律写成在场(排查记录 C 类,
docs/testing/consult-affliction-backtest-20261001.md基线)。 - 触发条件:问父母、伴侣、孩子、事业等任何「对象」题。
- 根因(已核实):形状规定「每段先用人话讲清楚这个人是什么样、你们怎么相处、事情会怎么走」,把对象在场与日常互动当成前提;
product-voice.ts父母 Good 例「爸爸话少,关心靠做事来表达」「先听听爸爸的意见」被照抄。 - 修复:任务书 T1 定稿句(D7 补「做哪类工作」)落成
NATAL_SECTION_RULE,natalAnswerShapeBody与 shared voice 都引用这一个常量;父母 Good 例改成「受冲先说、用可能、段末请确认」,删「分量重」「问爸爸的意见」。决策 D6 推翻TASK-consult-plain-answer-20261001D2 第 2 点。 - 验证:
frontend/tests/consult-no-presupposition-20261001.test.tsT1(定稿全文、三个要点、旧句在所有提示词源文件里不存在、规则文本只在一个文件);回测改动后:缺席 / 送养的 5 位父母题 10 份全部写出「可能不常在你身边」并请确认。 - 未过:同一句被套到父母都在身边的人身上——对照组 Bush、Zidane 父母 4 / 4 份误报,泰勒父母 2 份、Kahlo 父母 1 份严重冲突;婚姻、健康对照项也有误报(回测合计误报 15 份,基线 1 份)。根因判断:第二单通用读法「受冲信号两个及以上」几乎每张盘都满足,T1 又要求此时第一句就写这一面。需产品决定门槛(受冲明显多于支撑 / 按强度计分 / 卡上给计数),本单未改定稿句。
- 防复发:合同测试锁句;名人回测(BUG-1167)是普通对话提示词改动的固定验收。
- 相关记录:BUG-1132、BUG-1071、BUG-1160、BUG-1167
- 复发自:无
- 决策日志(2026-10-02):产品 2026-10-02 决定(名人回测第一轮对照组误报 1→15;Claude 按卡片确定性计数核对,在场与缺席的父亲受冲信号数相同,盘分不出):
NATAL_SECTION_RULE去掉「包括这个人在不在你身边、近不近」和「第一句就写这一面…请用户确认」,改为引用AFFLICTION_RANGE_RULE(推翻本单 T1 定稿句中冲突的部分,「不预设」一句保留);示例改成「距离的迹象 + 范围 + 告诉我一句」。第二轮回测:对照组误报 15 → 6(全部轻)、严重冲突 19 → 15、排查记录 5 份 8 / 10 过;仍未过线:Monroe、Piaf 母亲在受冲两个以上时被写成「近、来往密」3 份,Bush、Zidane 开场先下「距离 / 不能硬扛」结论 6 份。状态维持 investigating。提交066558a6。 - 第三轮回测(2026-10-02,
codex/consult-career-yoga-functional-20261002,开场规则 BUG-1177):对照组开场下结论的轻度误报 6 → 3;Monroe、Piaf 母亲一侧的反向安慰 3 份仍在(同第二轮)。状态维持 investigating。 - 修复版本:
codex/consult-no-presupposition-backtest-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401)
BUG-1165 | 示例里的读法被照抄:「宫主逆行」→「反复、打回来」;婚姻示例预设已婚
- 状态:resolved(文本层;回测禁句 0)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
frontend/src/mastra/product-voice.ts示例、frontend/docs/VOICE.md示例表。 - 现象:乔布斯父母几乎照抄是非题 Good 例「4 宫主水星逆行落 10 宫,她把心力投在你的前途上」;四份乔布斯样稿把逆行写成「反复、打回来」;基线回测禁句 7 处。
- 根因(已核实):示例括号里的盘面依据带了读法(逆行),模型当规则抄;Good 例「你这段婚姻更像先把已经在一起的关系过稳」预设已婚。
- 修复:是非题 Good 例改「不是。(4 宫主落 10 宫)她把心力放在你的前途上……」;删已婚预设的婚姻 Good 例;父母、事业 Good 例标明「括号里是依据的占位,只演示口气与结构,不是读法」。
git grep -n "逆行" frontend/src/mastra 'frontend/src/lib/consultation-*.ts'只剩 3 行:第二单通用读法的禁令句「逆行不等于『反复、打回来』」与技法审计表的两个技法名(逆行次限、逆行太阳弧)。 - 验证:
consult-no-presupposition-20261001.test.tsT2(逐行列出「逆行」并对白名单);consultation-voice-contract.test.ts三条断言改动(三栏在测试注释与进度记录);回测改动后禁句 0 处(基线 7)。 - 防复发:合同测试白名单;VOICE.md 新增「示例只演示口气与结构」一条。
- 相关记录:BUG-1164、BUG-1070
- 复发自:无
- 修复版本:
codex/consult-no-presupposition-backtest-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401) - 复发(2026-10-02):同一类「示例里的读法被照抄」在父母题复发——是非题与父母题 Good 例保留的「4 宫主落 10 宫 → 心力放在你的前途上 / 离你近、上心」被梦露盘(4 宫主正落 10 宫、母亲受冲两个以上)照抄;另有一处把逆行写成「拖出来」。见 BUG-1182。
BUG-1166 | 用户纠正盘上判断时,追问轮没有「先认下、不辩护」的写法
- 状态:resolved(文本层;回测 8 / 8 通过)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
frontend/src/lib/consultation-thinking-plan.tsfollowUpAnswerShapeInstruction(新常量FOLLOW_UP_CORRECTION_RULE)、frontend/docs/VOICE.md。 - 现象:任务书 D4:用户说「我爸其实没怎么管过我」时,追问轮可能为原判断辩护(「盘上显示你们关系不错」)。
- 根因(已核实):追问轮形状只讲「第一句回答这句话」,没有纠正场景的写法。
- 修复:追问轮加任务书 T3 定稿句。
- 验证:
consult-no-presupposition-20261001.test.tsT3(锁句、首轮形状不带);回测对四位父亲缺席名人各两次追加纠正轮,8 / 8 份先认下、列出对得上的盘面、点名读偏的句子,无一辩护。 - 副作用(记给产品):追问轮会反向把用户的话全部「对上」,把普通配置说成必然(「在传统读法里讲的就是这个人不在场」)。不辩护不等于全盘附和,本单未改定稿句。
- 防复发:合同测试锁句;回测脚本
--correction固定这一轮。 - 相关记录:BUG-1071、BUG-1164
- 复发自:无
- 决策日志(2026-10-02):针对副作用,追问轮定稿句改为「只说盘上真正对得上的那几条……盘本身分不出的,直说『盘本身分不出这一点』,不把普通配置说成必然」。第二轮回测 8 / 8 份都写出盘分不出的部分(第一轮 0 份),2 份仍有一句确定性偏高。提交
066558a6。 - 修复版本:
codex/consult-no-presupposition-backtest-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401)
BUG-1167 | 普通对话提示词改动只核格式、不核判断对不对
- 状态:resolved(回测基础设施与改动前 / 改动后两轮结果已交付;回测本身的通过线未达到,见 BUG-1164、BUG-1168)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
frontend/scripts/research/consult-biography-backtest.mts、scripts/research/capture_consult_biography_backtest_golden.py、frontend/tests/fixtures/consult-biography-backtest-golden.json、docs/research/consult_biography_backtest_rubric.json、docs/testing/consult-affliction-backtest-20261001.md、AGENTS.md§10。 - 现象:10-01 效果验证只核了格式(格局名、推修、扮演、爸妈分段),没有核对判断与生平是否冲突,两轮都没拦住父亲缺席被写成在场(排查记录第 4 节)。
- 根因(已核实):没有「对不对」的标尺。
- 修复:基础设施由
691ee440交付(9 位 Rodden AA 公开名人 × 父母 / 婚姻 / 健康 / 事业,评分表带来源);本单:golden 用真实引擎按同一命令重生成(卡上有第一单的相位、燃烧、瑜伽分领域,两次输出逐字节相同)、脚本加--correction纠正追问轮、跑改动后全量并逐份评分;AGENTS.md§10 加一行「普通对话提示词 / 清单改动 → 名人生平回测(需模型 key)」。 - 验证:改动前严重冲突 39 / 72 → 改动后 19 / 72;禁句 7 → 0;误报 1 → 15;事业「专业 / 顾问 / 幕后」16 / 18 → 16 / 18(详表见测试文档「改动后」)。
- 防复发:AGENTS §10 固定这一层;脚本不进 CI(需外网与 key)。
- 相关记录:BUG-1132、BUG-1071、BUG-1160~1166、BUG-1168
- 复发自:无
- 补记(2026-10-02 第三轮):评分表加事业
expected_field/public与how_to_score.career_field(任务书 D6);golden 按同一命令重生成(规则引擎每次都跑、卡上有traditional_yogas),两次逐字节相同;第三轮全量 72 + 8 份逐份评分,结果写入测试文档「改动后(第三轮)」:严重 15 / 72、行业对 3 / 18、专业 / 幕后误判 12 / 18、对照组轻度误报 3、禁句 0。 - 修复版本:
codex/consult-no-presupposition-backtest-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401)
BUG-1168 | 事业题不论盘面都写成「专业型 / 顾问 / 幕后」
- 状态:investigating(示例已去职业形态;回测 16 / 18 未变,通过线 ≤ 3 / 18 未达到)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-02
- 影响面:
frontend/src/mastra/product-voice.ts事业 Good / Bad 例与追问轮 Good 例、frontend/docs/VOICE.md;未改(不在本单范围):frontend/src/lib/consultation-condensed-checklist.ts事业清单。 - 现象:基线回测 18 份事业答案 16 份把总统、球星、影星、画家、企业家写成「专业人 / 顾问 / 幕后、不靠曝光」,对照组同样如此。
- 根因(部分核实):任务书 D7 疑为事业示例(「之后再谈升职」「专业能力」「做深现有工作」)。本单删掉这些词后改动后回测仍 16 / 18,示例不是主因。改动后的答案显示两个更可能的来源:①通用读法让模型先写受冲,事业宫被土星照、10 宫主落 6 / 8 / 12 几乎人人都有,开头一律成了「名分被压、在别人看不见的地方做」;②事业清单第 3 行「接触(消息、面试、初步接洽)/ 结构性机会(合同、正式职位、长期项目)/ 公开落地」把读者设成求职者(奥巴马事业 r1 原样复述这三层)。AL 落 10 宫的 8 份里 5 份读出公众形象,其中 3 份方向仍写成专业型。
- 修复:示例去掉「升职」「专业能力」「做深」「现有工作」「这份工作」「换公司」,事业 Good 例标明不预设读者在上班、做哪类工作;
NATAL_SECTION_RULE的「不预设」含「是否在上班、做哪类工作」。 - 验证:
consult-no-presupposition-20261001.test.tsT2b;回测事业 16 / 18(未过)。 - 待办:另开单改事业清单第 3 行与 AL / 10 宫 SAV 的读法,并配合 BUG-1164 的受冲门槛决定。
- 相关记录:BUG-1162、BUG-1164、BUG-1167
- 复发自:无
- 决策日志(2026-10-02):产品 2026-10-02 决定(名人回测第一轮对照组误报 1→15;Claude 按卡片确定性计数核对,在场与缺席的父亲受冲信号数相同,盘分不出):事业清单去求职口吻、加「一生事业形态」首行(见 BUG-1162)。第二轮回测 16 / 18 → 12 / 18,仍未过 ≤ 3。剩余 12 份多是模型按 AL / A10 / 10 宫主落宫读形态时确实读不出公众型(乔布斯 A10 落 12 宫、奥巴马 10 宫主落 6 宫、Bush / Zidane 10 宫主落 1 宫),事业形态与盘面信号的对应需要占星口径决定,不是措辞问题。状态维持 investigating。提交
066558a6。 - 第三轮(2026-10-02):事业清单按任务书 D4 加「行业看星的本性」与「公众 / 幕后要有依据」两行(BUG-1176)。回测专业 / 幕后误判 12 / 18 不变、行业对 3 / 18。逐份看,模型照清单取了 10 宫、10 宫主、AmK 的星性,但这批盘上它们是水星、木星、土星,对照表本身推不出表演 / 竞技 / 政治;公众型依据在乔布斯、布什、齐达内盘上不成立。剩余问题是占星口径(D10 权重、AL 落 10 宫、金星 / 月亮的表演类取法),需产品与占星顾问决定。状态维持 investigating。
- 修复版本:
codex/consult-no-presupposition-backtest-20261001(已部署 stagingf361365a,2026-10-02 health 核对、/api/account 401;部分)
BUG-1172 | 采用后答完核对题只说「前事核对到这里」,不说对不对得上
-
编号说明:首次推送(
42ff8291)时记为 BUG-1170,与普通对话任务书TASK-consult-no-presupposition-and-backtest-20261001.md预留的 BUG-1170/1171 撞号,2026-10-02 改为 BUG-1172。 -
状态:resolved(2026-10-02 Claude 直接执行;产品 10-01 选 A)
-
首次发现 / 最近更新:2026-10-01 / 2026-10-02
-
影响面:
v9/answer-choice.ts::persistApplied(已采用且所答焦点是 reverse_verify / out_of_sample_check)、新增v9/post-adopt-verdict.ts。 -
用户现象:采用后答了「2016 年 9 月那次升学」核对题,回复只有「前事核对到这里。之后新建对话即按已采用时间排盘;对不上随时改选。」用户不知道答案说明了什么,只问一道就结束。
-
触发条件:已采用,答一道核对题(跳过不算)。
-
根因:收尾文案只看「还有没有下一道」,不看刚答的这道对采用时间是支持还是冲突。只问一道是规则所致(采用后核对至多 2 道,只取采用前没问过、不在用户已报年份/领域、未拒答的探针;该案只剩 1 道),不是本条要改的。
-
决策记录:产品 10-01 选 A——收尾先说结果,题数规则不变。
-
修复:
postAdoptCheckVerdict用该探针按所答类别的 supports / conflicts,对照「采用分钟所在候选」(簇含该分钟,否则最近):支持→「这件事和 HH:MM 对得上。」;冲突→「这件事和 HH:MM 对不上;如果别的经历也对不上,可以回到上面的卡片改选。」;都不涉及→「这件事分不出 HH:MM 和别的时间。」;记不清→「这件事记不清,不算。」盘型口径再比对答题前后的分盘摘要:没变→「盘型结论不变。」,变了→「盘型结论有变化:<各目标盘上升与档位>」。结果句放在收尾句或下一道题之前。只用服务端事实,不写「确认 / 精确」。 -
验证:
frontend/tests/rectification-post-adopt-verdict-20261002.test.ts(候选映射、四种结果、盘型变/不变);frontend/tests/rectification-answer-choice.test.ts新增作答最后一道核对题用例(去掉接线即红);本机 PG17 真库重放虚构案例:回复为「这件事和 15:26 对得上。盘型结论不变。」+ 收尾句。 -
防复发:采用后作答的回复必须带结果句;跳过保持原收尾句(既有用例锁定)。
-
相关记录:BUG-536~538、BUG-1146、BUG-1150。
-
复发自:无
-
修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1173 | 采用后核对题让盘型结论变了,回复不说采用的时间还在不在、也不给改选入口;一件对不上就劝改选
- 状态:resolved(2026-10-02 Claude 直接执行;产品 10-02 选 A;真库测试)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
v9/post-adopt-verdict.ts(adoptedMinuteLeavesChart、readoptOffer)、v9/divergence-panel.ts(交付卡新字段readopt_minute)、v9/case-dossier-response.ts+v9/turn-question.ts::attachOfferResultToTurns(卡片挂载位置)、components/rectification-segment-delivery.tsx(按钮)。 - 用户现象(产品提问「假如盘型有变你怎么做」):BUG-1172 只说「盘型结论有变化:…」,不说采用的时间是否还在新结论里;交付卡在核对题上方,要往回翻;若服务端采用分钟只是在同一盘型内挪了一下,卡片按钮会重新亮起「改用 D1 + D9 + D10 盘解读」,与「不用改」矛盾。真库重放另见:答「对不上」而盘型没变时,回复仍说「可以回到上面的卡片改选」,改选只会落回同一分钟。
- 根因:采用后没有一个「是否该提示改用」的服务端判断,回复、卡片按钮、卡片位置各自推断。
- 决策记录:产品 10-02 选 A——分情况说清楚,改不改由用户点;不自动改;原填报资料保留。
- 修复:
readoptOffer(采用时间, 新摘要, 窗口分钟)只在「采用分钟在某张非『分不开』目标盘上的上升 ≠ 新结论」且「服务端有不同的新采用分钟」时给出新分钟;回复、卡片按钮、卡片位置都读它。回复四种:没变+对不上→「一件事还不足以改变盘型结论,先按 HH:MM 排盘。」;变了但仍在→「…你采用的 HH:MM 仍在其中,不用改。」;变了且离开、有新分钟→「…你采用的 HH:MM 的 D9 上升是X,和新的结论不一致;新的推荐时间是 HH:MM,可以点下面的「改用 HH:MM」。」;离开但无新分钟→「…先按 HH:MM 排盘。」。有新分钟时交付卡挂到这条回复下面,按钮为「改用 HH:MM」并注明「现在按 HH:MM 排盘;改用后按 HH:MM 排盘,原填报资料保留。」;其余情况已采用的卡保持「已采用」。改用走既有分段采用 RPC,不改数据库。 - 验证:
frontend/tests/rectification-post-adopt-verdict-20261002.test.ts(四种回复、readoptOffer五种输入、卡片挂载只在改用时移到最新回复;原「盘型结论有变化」用例按三栏改写);frontend/tests/rectification-readopt-card-20261002.test.ts(离开→「改用 09:15」与当前分钟说明;仍在→「已采用」);frontend/tests/database-segment-d7-boundaries.test.ts新增真库段:首次分段采用后只改后验(先验仍等于引擎原分),服务端采用分钟 2→8,同一代表候选再次采用成功、资料排盘时间与 provenance 跟到新分钟(PG17 替身通过)。真库重放 12 例中答「对不上」均未改变盘型(单题 ±2 分),盘型变化在实际中少见。 - 防复发:采用后的「是否提示改用」只由
readoptOffer决定;回复、按钮、卡片位置不得各自判断。 - 相关记录:BUG-1172、BUG-120(改选)、BUG-1115~1117。
- 复发自:无
- 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1174 | 排盘自带一两条瑜伽时,规则引擎整段不跑,Raman 包成员多被判未命中
- 状态:resolved(2026-10-02,未部署)
- 首次发现 / 最近更新:2026-10-01(BUG-1159 第三条)/ 2026-10-02
- 影响面:
scripts/jyotish_api_server.py普通对话 yogas 块;scripts/consultation_native_layers.pycollect_consultation_yogas/merge_detected_yogas。 - 现象:乔布斯、泰勒、Monroe、Garland、Piaf、布什、齐达内的卡上
hits为空,Matrunasa、Sunaphaa / Anaphaa、Vosi 等本可命中的成员规则都是「未命中」;只有排盘不带瑜伽的奥巴马、Kahlo 有 hits。 - 触发条件:
chart.yogas非空(排盘结果自带扩展瑜伽,如 Amala、Saraswati、Kemadruma、Gandanta)。 - 根因(已核实):
if not compact:让yoga_expansion.detect_all_yogas+yoga_engine.detect_yogas只在chart.yogas为空时运行,是性能优化遗留,与「检测完整」冲突。 - 修复:规则引擎每次都跑,与
chart.yogas按归一化名字合并去重后再进 Raman 筛选;逻辑放在consultation_native_layers.py,服务端只剩一行调用(+2 / −18 行)。不改任何检测规则或筛选边界。 - 验证:
tests/test_consultation_yoga_detection_merge.py(chart.yogas 非空时引擎仍运行、去重、引擎失败退回、源码不再有if not compact:);9 位公开名人改前 / 改后 hits 对照见进度记录(7 位从空变为 3~7 条)。 - 防复发:上述源码断言;hits 对照表。
- 观察:泰勒、齐达内排盘自带 Kemadruma,规则引擎同时判出 Anaphaa / Vosi / Ubhayachara,两套检测器对同一张盘结论相反,未改,建议另开单。
- 相关记录:BUG-1159、BUG-1157
- 复发自:无
- 修复版本:
codex/consult-career-yoga-functional-20261002
BUG-1175 | 大格局(Sasa、落陷取消、Gajakesari 等)检测到也上不了卡
- 状态:resolved(2026-10-02,未部署)
- 首次发现 / 最近更新:2026-10-01(BUG-1159 第二条)/ 2026-10-02
- 影响面:
scripts/consultation_native_layers.pybuild_traditional_yogas;frontend/src/mastra/consultation-workflow.ts投影;frontend/src/lib/consultation-evidence-card.ts(general / career / wealth 卡、EVIDENCE_CARD_LABELS.traditionalYogas);frontend/src/lib/consultation-condensed-checklist.ts通用读法第 8 行。 - 现象:奥巴马综合篇把落陷木星读成「早年自我评价偏低」,盘上同时成立的 Neecha Bhanga Raja、Sasa 不在卡上;乔布斯、泰勒的 Gajakesari 同样不在。
- 根因(已核实):这些格局不在任何 Raman 包里,检测到只进
ungoverned_detected,卡只读modules.yogas。 - 决策:产品 D3 授权在这一栏放宽 AGENTS §8.4:封闭名单(五大人格、Gajakesari、Neecha Bhanga Raja、Budha-Aditya)里成立的格局单列,固定标签「传统格局,本站未验证,只作参考」;只说明力量,不单独下结论,不算受冲也不抵消受冲,不改置信度上限。Raman 包、
modules.yogas、claim_boundary不变。 - 修复:引擎按名单取
hits+ungoverned_detected中成立的项(名称按引擎实有名称对齐,Surya-Budha 并入 Budha-Aditya);三张卡加traditional_yogas: {label, names},无成立项不出现;通用读法加一行。 - 验证:
frontend/tests/consult-career-yoga-functional-20261002.test.ts(奥巴马综合卡含 Sasa、Neecha Bhanga Raja;乔布斯、泰勒含 Gajakesari;只有三张卡;空时不出现;标签原文);tests/test_consultation_yoga_detection_merge.py(名单外不出、未命中不出);12,000 / 18,000 字符断言通过。第三轮回测:14 份带此栏的事业答案里 6 份提到,没有一份单独拿它下结论。 - 防复发:名单与标签各只定义一处;合同测试锁三张卡与标签。
- 相关记录:BUG-1159、BUG-1157
- 复发自:无
- 修复版本:
codex/consult-career-yoga-functional-20261002
BUG-1176 | 事业清单只讲时间与受冲,没有从盘上读行业和名声的读法
- 状态:investigating(产品 10-02 改为「不猜行业,问用户」;第四轮回测:请问一句 18/18 达到,行业 / 类型与生平冲突 9/18 未达 ≤ 1)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
frontend/src/lib/consultation-condensed-checklist.tscareer 清单;frontend/docs/VOICE.md§8。 - 现象:第二轮回测事业题 12 / 18 写成「专业型 / 幕后」,行业也读不出(齐达内「靠说话、写东西、分析信息」,乔布斯「偏知识型、偏幕后」)。
- 根因(已核实):清单没有「行业看什么、公众型看什么」的读法,模型用「稳妥上班族」模板补位。
- 修复:career 清单加任务书 D4 定稿两行(行业按 10 宫星、10 宫主、AmK、10 宫主 D9 定位星的星性;公众 / 幕后要有依据,没有就不写,不写人人适用的话)。
- 验证:
frontend/tests/consult-career-yoga-functional-20261002.test.ts锁两行原文。第三轮回测:行业对 3 / 18(通过线 ≥ 14),专业 / 幕后误判 12 / 18(≤ 3),均未达到。逐份看,模型照做了,但这批盘的 10 宫系统落在水星 / 木星 / 土星,对照表本身推不出名人的实际行业;需要占星口径决定,不是措辞问题(见 BUG-1168)。 - 防复发:名人回测事业项的
expected_field/public(BUG-1167)。 - 相关记录:BUG-1168、BUG-1162、BUG-1167
- 复发自:无
- 决策日志(2026-10-02 第二次):第三轮回测行业对 3/18 后,产品决定事业题不猜行业:删掉「行业看星的本性」「公众 / 幕后要有依据」两行与「先讲一生事业形态:公众型还是幕后型」,改为
CAREER_FIELD_ASK_RULE(只讲力量、阻力、时间;不断定行业与公众 / 幕后;不写人人适用的话;事业段末问一句实际做什么,算全篇唯一请求),定义在consultation-thinking-plan.ts、事业清单引用;追问轮加CAREER_FOLLOW_UP_RULE(用户说了工作就按工作读,不编依据)。AL 一行去掉「落 10 宫 = 公众形象就是事业」。提交6a02e5e9。第四轮回测:请问一句 18/18、事业追问 6/6 按真实职业读;但 9/18 仍在主线里写「不是站台式 / 不在台面上 / 往深处做」,多由 A10、AL、太阳、10 宫主落 4/6/8/12 宫直接推出;严重冲突合计 12/72(≤ 5 未达)。 - 第五轮(2026-10-02,产品批准本轮修第四轮残留):
CAREER_FIELD_ASK_RULE加一句「A10、AL、太阳、10 宫主落 4、6、8、12 宫只说这条线的阻力或代价(要经手、要绕路、来得晚),不翻成『不在台前』『不在台面上』『不靠曝光』『幕后』『不是站台式』『往深处做』,也不把落宫翻成行业(8 宫不等于研究、账目,12 宫不等于海外、幕后工作)」,仍只定义在consultation-thinking-plan.ts;frontend/tests/consult-upstream-functional-v2-20261002.test.ts锁句。第五轮回测:行业 / 类型与生平冲突 9/18 → 5/18(乔布斯 r2「靠一门本事坐实,不是靠一阵热闹」〔边界〕、泰勒 r1「不是靠人缘和曝光速成」、梦露 r1 / r2「不跟着曝光走」「经手别人的事…不是靠曝光」、布什 r2「靠能扛事,不是靠场面」),通过线 ≤ 1 未达;段末问一句 17/18(奥巴马 r1 只问了受冲那一句)。剩下 5 份里 4 份的推法是「10 宫主 / D10 落 6 宫或 8 宫 → 经手、担责 → 不靠曝光」,规则点了名的说法换成同义词(「不靠场面」「不是一阵热闹」)再出现。事业追问(足球运动员 / 演员 / 政治家 × 2)6/6 按真实职业读,泰勒、齐达内两份主动认下上一轮读偏。状态维持 investigating。 - 修复版本:
codex/consult-career-yoga-functional-20261002(文本层);第五轮规则codex/consult-upstream-functional-v2-20261002(未推送)
BUG-1177 | 开场「第一句就是结论」不受受冲规则约束,对照组被开场下了「距离感明显」一类结论
- 状态:resolved(文本层;第三轮回测对照组开场误报下降,残留见下)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
frontend/src/lib/consultation-thinking-plan.tsnatalAnswerShapeBody;frontend/src/mastra/product-voice.tsANSWER SHAPE 第 1 条;frontend/docs/VOICE.md§7 ①。 - 现象:第二轮回测 6 份对照组轻度误报都在开场:布什父母「距离感明显」「偏外,见面少」,齐达内健康「不是能硬扛的类型」。
- 根因(已核实):
AFFLICTION_RANGE_RULE写的是「这一段」,开场「第一句就是结论」不受它约束。 - 修复:
natalAnswerShapeBody开场句换成任务书 D5 定稿句(开场同样守受冲规则,不替用户断定在不在身边、几段感情、有没有病,不写程度结论);系统提示product-voice.ts同一规则补同义一句(原句保留)。 - 验证:
frontend/tests/consult-career-yoga-functional-20261002.test.ts锁定稿句、断言natalAnswerShapeBody不再有「第一句就是结论」。第三轮回测:对照组轻度误报 6 → 3(开场 1 份、段中 2 份),通过线 ≤ 2 仍差 1。 - 防复发:合同测试锁句;名人回测。
- 相关记录:BUG-1164、BUG-1132
- 复发自:无
- 修复版本:
codex/consult-career-yoga-functional-20261002
BUG-1178 | 一件未来日期或「今年」的经历让之后每次重新比较都失败,校正一直追问、不出时间卡
- 状态:resolved(2026-10-02 Claude 直接执行;真引擎 + 真库验证)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
v9/engine-client.ts::engineRequestBody(score / diagnostics / block_scan / vedastro-validate 共用)、mastra/rectification-v9-tools.ts(record-evidence-batch、propose-evidence)、v9/agent-run-support.ts/agent-run-attempt.ts/host-fallback.ts(回复补句)、user-copy.ts。所有生时校正用户。 - 用户现象(staging 真机测试者):第一件经历把年份打成「2028 年借出一笔钱」,之后每轮回复都是「记下了:…这次没有重新比较,稍后再比一次。」,范围不动、不出时间卡,测试者「都不想回答了」。
- 触发条件:账本里有任何一件开始日期晚于今天的经历;或任何「今年 / 这个月」的经历(年 / 月精度的结束日 12-31 / 月底晚于今天);或完全在出生前的经历。
- 根因:引擎
scripts/rectification/contracts.py要求每件经历的日期都在 [出生日, 今天] 内,否则整个请求 400(events[i] dates must be between birth_date and today)。前端证据入口与数据库都不校验日期上限,未来年份照常记下且确认;之后每次重算都把它带上,整次失败,回复走 rescoreSkipped 文案。本机真引擎实测:2028 年、今年(年精度)、本月(月精度)都 400,去年 200。 - 修复:(1)
boundEngineEventDates:发给引擎前丢掉开始晚于上限(UTC 昨天,避免两端时钟差)或结束早于出生日的经历,其余裁进窗口;全部被丢则按既有no_scorable_evidence处理。已在账本里的未来经历不再挡住比较,该测试者的 Case 下一轮即可恢复。(2) 入口:record-evidence-batch 与 propose-evidence 对开始日期晚于今天(UTC)的项不写库,返回future_date;回复由服务端追加「有一件事的日期在今天之后,这件先不算;如果年份打错了,直接告诉我正确的年份就行。」(3) 顺带修 batch 结果编号:已写入行的index改为模型原始条目序号(此前是写入序号,被拒条目在前时「记下了」复述会读错位置)。 - 验证:
frontend/tests/rectification-event-date-bounds-20261002.test.ts(裁剪六种日期、请求体只带裁剪后事件且全部被丢时报 no_scorable_evidence、入口判定、batch 中 2099 项不写库且其余条目序号与复述正确、回复补句只出现一次;去掉裁剪时请求体用例红)。真引擎 + 本机 PG17:虚构案例账本中加入 2028 年与今年的经历后重算成功(修前引擎 400)。 - 防复发:任何发给引擎的事件都经
engineRequestBody;新增事件入口须做日期上限校验。旧版mastra/rectification-tools.ts(仅 legacy session 引用)未改,记入进度。 - 相关记录:BUG-1084(打字事件不计分研究)、BUG-1179。
- 复发自:无
- 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1179 | 用户说「刚才年份打错了」改不了:更正工具要求问题焦点,Agent 也拿不到经历编号
- 状态:resolved(2026-10-02 Claude 直接执行;产品 10-02 选 A;真库验证;真实模型调用为环境缺口)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
mastra/rectification-v9-tools.ts::rectification-revise-evidence、v9/turn-decision.ts(relevant_evidence_summary)、mastra/agentic-rectification.ts(提示规则)。 - 用户现象:测试者问「回答错了这个能返回吗」,产品答「目前不能」。实际上已有
rectification-revise-evidence,但用不上。 - 根因:(1) 工具 schema 强制
focusId,而随口更正一件打字经历时没有对应的问题焦点;(2) 修订后的新记录是 pending,要第二次调用确认才生效、才重算;(3) Agent 的经历摘要不带evidence_id,无从指定改哪条;(4) 提示词没有「更正走修订、不要新记一条」的规则。 - 决策记录:产品 10-02 选 A——先做「改经历」:用户说出正确年份即改正并重新比较;「撤回上一道选择题」不做(选项 B 未选)。推翻 08-14 v10 运行时「修订必须绑定问题焦点」的约束,改由「quote 必须落在本轮用户原话里」(与新事件同一校验)保证改动来自用户本人。
- 修复:
focusId改为可选;有焦点走revise_..._v10,无焦点走revise_agentic_rectification_evidence(真库确认 service_role 可调用);随后在同一调用里confirm_..._v10确认新修订并重算,返回 rescore / range_after_rescore;quote 未落在本轮用户消息里→quote_mismatch不写;更正后的日期仍在未来→future_date不写。经历摘要带evidence_id并排除已被替代的记录;提示规则:用户更正之前说过的事走修订工具。 - 验证:
frontend/tests/rectification-v9-evidence.test.ts新增「无焦点更正:修订→确认、未落原话不写、未来日期不写」;三条既有合同用例按三栏改写(schema 允许省略 focusId 但仍须 evidenceId;修订后多一次确认)。本机 PG17:无焦点修订 + 确认后旧记录 superseded、新记录 confirmed。真实模型是否按新规则调用修订工具:本机无模型凭据,留待 staging 真机(docs/testing/rectification-event-dates-20261002.md)。 - 防复发:经历的任何改动必须带本轮用户原话;修订后立即生效,不得停在 pending 等第二次调用。
- 相关记录:BUG-1178。
- 复发自:无
- 修复版本:
codex/rectification-post-adopt-verify-20261001。
BUG-1180 | Pancha Mahapurusha 检测把火星、土星自己算作「凶星合相」,Ruchaka / Shasha 永远判失效
- 状态:resolved(修复随上游移植
57782aea,本轮补回归测试) - 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/pancha_mahapurusha.py_has_malefic_aspect/detect_pancha_mahapurusha;读它的scripts/jyotish_api_server.py_detect_yogas(排盘自带瑜伽,只收is_valid的五大人格格局)与/api/pancha_mahapurusha(assess_pmc_strength)。 - 现象:火星在白羊 / 天蝎 / 摩羯坐角宫(Ruchaka)、土星在天秤 / 摩羯 / 水瓶坐角宫(Shasha),即使同宫没有任何别的凶星,也被标「受凶星合相: Mars / Saturn — Yoga部分失效」,
is_valid = False,排盘自带瑜伽里永远不出现这两个格局。 - 触发条件:任何 Ruchaka / Shasha 候选。
- 根因(已核实代码):
_has_malefic_aspect(planet_sign, all_planets)用pn == planet_sign跳过自身——把行星名和星座名比较,永远不等;火星、土星本身在NATURAL_MALEFICS里,于是自己与自己同宫就算一次「凶星合相」。木星、水星、金星不是自然凶星,不受影响。 - 修复:函数加
planet参数,按pn == planet跳过本星(上游e9beae6b原样移植,57782aea)。别的凶星同宫仍判失效。 - 验证:新增
tests/test_pancha_mahapurusha_self_conjunction.py(火星独坐白羊 1 宫 → Ruchaka 成立、无失效原因;土星天秤 10 宫 → Shasha 成立;同宫另有土星仍失效且只点名土星;木星与火星同宫仍失效)。用57782aea^的旧文件跑同一输入:Ruchakais_valid = False(红),新文件为真(绿)。两份普通对话 golden 重生成后瑜伽字段无变化(九位名人没有触发这两个格局的独坐配置)。 - 防复发:回归测试锁「自身不算合相」;跳过自身必须按行星名比较。
- 相关记录:BUG-1159、BUG-1174、BUG-1175(传统格局栏读的是规则引擎结果,不经本模块)。
- 复发自:无
- 修复版本:
57782aea;测试codex/consult-upstream-functional-v2-20261002(未推送)。
BUG-1181 | 功能吉凶 v2 改了生时校正打分,算法身份没跟着升:旧分数会被当作当前结果复用;「日期窗合同」写死只认 scoring-9
- 状态:resolved(代码与测试;部署、迁移与真机为待办)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/rectification/scoring_service.pyALGORITHM_VERSION;前端v9EngineVersion()缺省串、engine-client.ts(score / diagnostics 的dated判定)、tool-service.ts(候选行解析、legacy 段选择判定)、score-persist.ts(是否先补日期窗)、rectification-candidate-result.ts;数据库函数validate_dated_rectification_candidate。 - 现象:
57782aea之后tests/test_rectification_engine_memoization.py8 项失败(同一输入候选 12:00 的分数 8.6274 → 8.4977)。功能吉凶分组经*_functional_benefic_auxiliary/*_functional_malefic_auxiliary规则进入事件矩阵,同输入出不同分;而ALGORITHM_VERSION仍是 scoring-9,cachedEngineScoreIsReusable按算法身份 + 证据指纹判断能否复用,证据未变的历史 Case 会把旧分组算出的分数当作当前结果。 - 触发条件:任何功能吉凶分组变化的上升(v2 改了 10 个上升的至少一颗星)且证据未变的校正 Case。
- 根因:一,先例规定「打分语义变化必须 bump
ALGORITHM_VERSION」(TASK-rectification-engine-convergence-20260901硬红线 3;跨午夜修复 7→8→9 同此办理,BUG-981~985),功能吉凶移植时未同步。二,前端五处、数据库一处用=== "rectification-v5-matrix-scoring-9"判断「这是带日期窗合同的结果」:直接升到 scoring-10,新结果会被当成旧的同日结果解析(跨午夜候选丢日期),数据库的防伪守卫也会对 scoring-10 失效。 - 决策记录:按先例升到
rectification-v5-matrix-scoring-10(同输入分数变了就升,策略版本rectification-candidate-policy-v3与输入合同 v5 不变;不改 Skill、不重标历史结果)。「日期窗合同」改为按代数判断(≥ 9),避免下一次升版再踩同一处。 - 修复:Python 常量升到 scoring-10;前端新增
isDatedScoringAlgorithmVersion(core/candidate-window.ts,正则取代数 ≥ 9),五处字面量判断全部改用它,v9EngineVersion()缺省串同步;迁移20261002010000_rectification_dated_algorithm_generation.sql只重建validate_dated_rectification_candidate函数体,守卫改为~ '^rectification-v5-matrix-scoring-(9|[1-9][0-9]+)$',其余逐字节与20260920020000相同(不动表、列、权限、历史行)。按 ERR-110 重新冻结校正研究记录(标签functional_v2_2026_10_02):sealed_holdout_rerun.py/reported_offset_sweep.py指向新记录,PRODUCTION_FILES补scripts/functional_benefics.py(ERR-114:它不在冻结身份里,57782aea改分数时门禁没红),references/rectification_sealed_holdout.v1.json的 current / previous 照 09-29 先例轮换,旧记录逐字节保留。影子 20 例与 09-29 逐条相同(指标不变);原生 reported-offset 900 例 230 例变化,窗口内平均头名不变(±15/30/60:0.0636/0.06/0.05),交付覆盖 0.9864/0.99/1.0 → 0.9955/0.9967/1.0。 - 验证:记忆化 golden 按文件自带
write_golden新写tests/golden/rectification_engine_memoization_v2.json(scoring-10,源码57782aea),v1(scoring-8)保持逐字节不变并以 sha256 钉住,新增测试证明当前分数已偏离 v1;文件 22 项全过。前端:新增rectification-dated-algorithm-generation-20261002.test.ts(代数判定 9/10/11/100 真、8/09/空/非字符串假;源码里不再有=== "…scoring-N";SQL 守卫;scoring-9 历史结果在 scoring-10 下打开为只读并给「重新比较」、候选仍带日期);真实引擎新生成 scoring-10 跨午夜 golden(midnight_date_anchor_regression.py --golden,同机 A/B 3×7 字段逐位相等)并让跨午夜贯穿测试对 scoring-9 / scoring-10 两份 golden 都跑;把判定临时改回字面量,scoring-10 那一份即红。数据库:本机 PG17 + docker 替身跑database-adopted-birth-date(scoring-10 / 11 去掉日期合同被拒、scoring-8 仍按旧同日结果返回空);去掉迁移文件即红 2 项、恢复后绿。npm run test:db全量 80 项:基线57782aea与本分支都是 74 过 / 6 败,失败名单逐条相同(替身不支持的 pg_dump、部署脚本等)。重新冻结后test_rectification_validation_integrity_gate.py等 8 项转绿,rectification-confirmation-gate.test.ts改读新报告(三栏在注释)。历史打开:rectification-*.test.ts1,872 项(含 BUG-621 历史打开、BUG-984 只读与重新比较)0 失败。 - 防复发:打分语义变化必须升
ALGORITHM_VERSION,记忆化 golden 新增版本文件、旧文件冻结,校正研究记录按 ERR-110 重新冻结;test_functional_roles_are_part_of_the_production_identity锁功能吉凶文件在冻结身份里;「是否带日期窗合同」只能用isDatedScoringAlgorithmVersion/ SQL 正则判断,源码扫描测试拦字面量比较。 - 待办:部署前核实 staging / 生产环境没有设置
RECTIFICATION_ENGINE_VERSION/RECTIFICATION_ALGORITHM_VERSION锁旧身份(deploy/、.gitea/未设);迁移在部署前自动应用,对当前已部署代码向后兼容(只放宽到 ≥ 9 的守卫)。 - 相关记录:BUG-1158、BUG-621、BUG-733、BUG-981、BUG-982、BUG-983、BUG-984、BUG-985;错误台账 ERR-110、ERR-114。
- 复发自:无(跨午夜那次升版同时引入了「日期窗合同」,当时只有 scoring-9 一个代,字面量判断没有暴露)。
- 修复版本:
codex/consult-upstream-functional-v2-20261002(未推送)。
BUG-1182 | 第四轮回测残留:受冲两个以上的母亲被写成「在管、在安排、近」,逆行被写成「拖出来」,全篇两处请求
- 状态:resolved(文本层;第五轮回测父母严重冲突 3 → 0、禁句 1 → 0、两处请求 3 → 0)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
frontend/src/lib/consultation-thinking-plan.tsAFFLICTION_RANGE_RULE;frontend/src/lib/consultation-condensed-checklist.ts通用读法第 5 行;frontend/src/mastra/product-voice.ts不揣测一条、父母题与是非题 Good 例;frontend/docs/VOICE.md。 - 现象:第二~四轮回测,梦露父母 ×2「把心力压在你的前途上,方式是安排、催」「妈妈这条有实际照顾的一层」、琵雅芙父母 ×1「她这条线的重心是管和安排」——三份都已写出受冲范围,又在同一段或「他们怎么对待我」一段把母亲写成在场、在管;行动写「给妈妈打电话」「挑一件事去问爸爸」。琵雅芙健康 r2 出现「8 宫那颗土星还是逆行的,意思也偏『拖出来的问题』」;三份答案全篇两处请求。
- 根因(已核实):一,系统提示里有三处把「4 宫主落 10 宫」直接配上「母亲的注意力在你的前途上 / 离你近、上心」(不揣测一条、父母题 Good 例、是非题 Good 例),梦露盘 4 宫主正落 10 宫,模型照抄;BUG-1165 修示例时只去掉了「逆行」,这条读法留着。二,
AFFLICTION_RANGE_RULE只禁「有分量」「关系稳」这类安慰词,没有禁「拿宫主落宫、宫里的吉星去描写这个人在做什么」,也没有管行动建议。三,通用读法只写了「逆行不等于反复、打回来」,模型换成「拖出来」。四,AFFLICTION_RANGE_RULE要求「这一段末尾」请用户说一句,和事业段末的那一问在同一篇里叠加。 - 修复:
AFFLICTION_RANGE_RULE加「不拿宫主落哪一宫、宫里有吉星去描写这个人对你做什么(『在管』『在安排』『心思在你的前途上』『照顾是实的』都预设人在身边,宫主落宫和吉星只说这条线另有支撑的迹象),这一方的行动建议也不写给他打电话、去问他」与「问过之后别的段落不再另问」;三处示例括号改为「(母亲这条线没有受冲;4 宫主落 10 宫)」/「盘上有证据、且这条线没有受冲时可以写……」;通用读法第 5 行改为「逆行不等于『反复、打回来』,也不等于『拖得久、拖出来』,推论里不拿逆行当理由(依据里可以列)」。都是单一定义处的文字,不加任何对模型输出的正则后处理。 - 验证:
frontend/tests/consult-upstream-functional-v2-20261002.test.ts锁四句;改动的既有断言(consult-no-presupposition-20261001.test.ts的AFFLICTION_RANGE_TEXT与是非题例、consultation-voice-contract.test.ts两处)三栏写在测试注释。第五轮回测:父母 18 份严重冲突 0(梦露、琵雅芙四份母亲段只写「迹象 + 范围 + 另有支撑的迹象」,「他们怎么对待我」写「盘上看不出谁具体怎么对你」);禁句 0;72 份每份恰好一处请求。残留:琵雅芙父母 r2 行动「挑一件他们习惯替你做决定的小事」仍预设父母在场(记通过·弱)。 - 防复发:示例括号不得放「宫位 → 人物行为」的对应;受冲规则覆盖开场、正文、「他们怎么对待我」与行动四处;名人回测父母项。
- 相关记录:BUG-1164、BUG-1165、BUG-1177、BUG-1176。
- 复发自:BUG-1165(示例读法被照抄)。BUG-1165 的合同测试只白名单了「逆行」一词,没有检查示例括号里「宫位 → 行为」的读法,所以未拦住。
- 修复版本:
codex/consult-upstream-functional-v2-20261002(未推送)。
BUG-1183 | 婚姻计数法输出「一次终身关系,忠诚度较高」「两段重要关系,可能再婚」等确定性关系断语,婚恋证据按次数记 positive
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/marriage_counting.py_interpret_marriage_count、_marriage_recommendations、note;scripts/jyotish_api_server.py_derived_marriage_evidence(Marriage-counting条目)与婚恋裁决「对象筛选」轴;全读modules.marriage_counting(PL9 导出包原样带出)。 - 现象:全读与
/api/thematic_report婚恋主题里,婚姻计数条目写「婚姻/认真关系数量:N」「解读:一次终身关系,忠诚度较高」「第一段关系由吉星主导,质量较好」等;计数 ≤ 1 时 sentiment 记positive,details 带marriage_count与 D9 质量评级(「婚姻稳定」「强烈建议婚前咨询」),后者还被拼进裁决文字。 - 触发条件:任一张能算出 D1 第 7 宫主与其 D9 落点的盘。
- 根因:方法把「从 A 数到 B 的星座距离」直接当成关系次数,并按次数给出先后、质量、忠诚度断语;消费方再按次数给情绪分。上游 yinduzhanxing
0afa2780已改为只描述几何指数。 - 修复(TASK-upstream-sync4-20261002 T1,决策 2,文案为我方中文,不搬上游原文):解读改为「关系几何指数 N:……是这个方法里的星座距离,不代表婚姻或关系的次数」,同星座 / 对冲只说位置关系;建议改为对所有指数相同的三句中性说明;
note改为几何口径;结果新增geometric_index(marriage_count兼容保留、同值)。Marriage-counting证据一律neutral,details 只有geometric_index;裁决「对象筛选」轴不再读 D9 质量评级。 - 验证:
tests/test_marriage_counting_geometry_only.py(12 个指数逐一检查禁句与同一套建议);tests/test_api_server_security.py::test_marriage_counting_evidence_is_neutral_geometry_index_only(虚构盘 1990-06-15 10:30 北京,neutral、只有几何指数、婚恋主题全文无禁句)。rg "再婚|段关系|第一段|终身关系|忠诚度" scripts/marriage_counting.py无命中。模型视图实测:普通对话/api/consultation_workflow婚恋路由的主题证据来自reused_chart_modules(full_reading_used: false),本条目根本不出现,toModelOutput后的模型视图里也没有(见进度记录 T1)。 - 防复发:上述两条测试锁禁句与 sentiment。遗留:
_assess_d9_marriage_quality的评级文字(「婚姻稳定」等)仍在全读原始模块里,未进证据条目,进度记录列为待产品决定。 - 相关记录:BUG-1184。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1184 | 婚姻计数遇到「7 宫主与定位星同星座」时抛 UnboundLocalError,模块整段缺失;Parivartana 判定实际判的是合相
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/marriage_counting.pymarriage_counting_method、_check_parivartana;全读modules.marriage_counting(异常被cmd_full_reading吞进errors,模块缺失)。 - 现象:虚构输入「金星为 7 宫主、金星与火星同在白羊」时,旧代码在写 Parivartana 警告时引用尚未赋值的
distance,抛UnboundLocalError。 - 触发条件:7 宫主落在他人星座,且该星座主星与它同星座(合相),且传入了宫位数据(全读总会传)。
- 根因:两处。一,距离在 Parivartana 段之后才计算,警告里先用了它;交换后的重算结果随后又被覆盖。二,
_check_parivartana比较的是「定位星与 7 宫主是否同星座」,这是合相,不是互换。 - 修复:距离在 Parivartana 段之前算出,交换重算结果生效;互换判定改为「定位星落在 7 宫主掌管的星座」。
- 验证:
tests/test_marriage_counting_geometry_only.py::test_real_parivartana_does_not_crash_and_conjunction_is_not_exchange(真互换不崩、指数按交换后星座;合相不再判互换);旧代码在同一输入上复现UnboundLocalError。 - 防复发:同上测试。
- 相关记录:BUG-1183。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1185 | 双重过运相位按 0 起数比 1 起数的相位号,整体错一宫;修正后结果顺序随 PYTHONHASHSEED 变化
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/jyotish_engine.py_check_pac、cmd_double_transit_pac;全读 / 报告的double_transit_pac,普通对话数据卡的双重过运结论(scripts/consultation_native_layers.py)。 - 现象:土星 7 宫相位判到第 8 宫,木星 5/9 判成 6/10,火星 4/8 判成 5/9。三张公开 AA 盘 × 12 宫的 D1 / 月亮上升层:578 个叶子条目里 413 个变化,36 行统计全部变化,同层双重过运从 0 处变为 D1 15 处、CL 6 处。
- 触发条件:任何一次双重过运计算。
- 根因:
((t_house - p_house + 12) % 12) == offset的宫距从 0 起,而相位号从 1 起。BUG-1060 / 1061 审计同一命令时只核对了目标取值与跨层配对,没有核对相位判定本身,所以没拦住;当时的 golden 是在错误相位下采的,等于把错一宫锁成了标准答案。上游 yinduzhanxing2dd6062a(09-24)已修。第二处:修正后同层重叠第一次出现,而重叠与跨层配对的循环遍历 Python 集合,结果列表顺序随PYTHONHASHSEED变(seed 1、2 下 golden 测试失败)。 - 修复:
42d1582f(Claude 移植上游一行+ 1);db5a0509五处循环改为按名字排序遍历。罗计仍投 5/7/9(占星师清单第 7 题,未改)。 - 验证:
tests/test_double_transit_pac_aspect_count.py(土 3/7/10、木 5/7/9、火 4/7/8 正反例);tests/fixtures/double_transit_d1_cl_golden.json用测试自带--capture(PYTHONHASHSEED=0、缓存 TTL 0)重采,seed 0/1/2/5 下都通过;tests/test_double_transit_cross_layer_pairing.py三处断言按三栏更新(乔布斯 7 宫摘要改为 D1 层优先、第 9 宫反例换成乔布斯 9 宫、同宫命中集合重算)。 - 防复发:相位计数测试;golden 不再依赖哈希顺序。
- 相关记录:BUG-1060、BUG-1061、ERR-111。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1186 | API 格局列表截到前 10 条;已评估为空的格局模块被旧排盘预览「复活」
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/jyotish_api_server.py_detect_yogas、_chart_from_full_reading。 - 现象:超过 10 条的格局被丢;
modules.yoga.yogas = [](已评估、确实无格局)时取到chart.yogas的旧预览。 - 根因:
return yogas[:10];chart.get('yogas') or yoga_module.get('yogas') or …按真值而不是「字段存在」取第一份列表,且预览排在完整模块前面。上游 yinduzhanxinge9beae6b已修。 - 修复:
96767418(Claude 移植):不截断;按「是 list」依次取yoga_module.yogas→yoga_module.detected_yogas→chart.yogas。同文件另三处取格局的or写法(_compute_thematic_report的full_modules.get('yoga') or collect(...)、enriched['yogas']两处拼接)是拼接或补算,不是「预览顶替已评估结果」,未改,列进进度记录。 - 验证:
tests/test_api_yoga_list_presence.py(12 条全返回;空列表不回退;顺序)。两个方法都不读 handler 状态,测试以 unbound 方式调用,未新增JyotishAPIHandler.__new__;growth contract 通过。 - 防复发:同上。
- 相关记录:BUG-1174、BUG-1175。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1187 | 月亮类 / 太阳类格局把罗睺计都算作「有星」,同一张盘同时出现「月亮孤立」与「月亮有星」
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
references/yoga_rules.jsonbvr_002/003/004(Sunaphaa / Anaphaa / Duradhara)、bvr_016/017/018(Vesi / Vosi / Ubhayachara)、kemadruma_yoga;scripts/yoga_engine.pyRETIRED_RULE_IDS;scripts/yoga_expansion.pydetect_kemadruma(本来就只计五星)。 - 现象:泰勒、齐达内盘排盘预览有 Kemadruma,规则引擎同时判 Anaphaa 等;太阳第 2 宫只有计都也判 Vesi。
- 根因:规则只排除日或月,未排除罗计;
kemadruma_yoga计罗计而detect_kemadruma不计。经典定义只计火水木金土:BPHS 第 37 章第 7–10 颂(Sunapha / Anapha / Duradhara:「a Grah other than Sūrya」)、第 11–13 颂(Kemadruma)、第 38 章第 1 颂(Vesi / Vosi / Ubhayachari:「Barring Candr, if a Grah among Mangal etc.」),B.V. Raman《Three Hundred Important Combinations》第 2–5 条。PyJHora 4.8.7yoga.py的同名函数用SUN_TO_KETU,计入罗计,且 Sunaphaa / Anaphaa 在太阳同宫时整格不成立——与经典不符,按决策 3 不跟。 - 修复:
f3ed9509(Claude 移植上游2dd6062a六条规则 + 我方 Kemadruma 也不计罗计 + 六条同名重复规则退役);0a4e6d0a补回移植时丢掉的六条规则combo_template_en(英文版报告这几格没有组合说明),Kemadruma 组合文字改为「月亮1/2/12宫无火水木金土(太阳与罗计不计),且Lagna角宫仅月亮」。角宫解除条件未动。 - 验证:
tests/test_lunar_solar_yogas_count_five_planets.py、tests/test_lunar_solar_yoga_templates_kemadruma_nodes.py(计都单独在月亮后一宫不成 Sunaphaa 且不解除 Kemadruma,规则版与detect_kemadruma一致;土星在同位则相反;英文组合文字齐全)。名人回测 golden 重生成后:泰勒、齐达内卡上的格局命中只有 Vosi / Ubhayachara,Anaphaa 不再命中,Kemadruma 不在任何一张卡上,卡面不再矛盾。 - 遗留:
chart.yogas预览(_detect_yogas→yoga_expansion.detect_kemadruma,无角宫解除)对泰勒、齐达内仍给 Kemadruma,且同名出现两条;规则版带角宫解除、对这两张盘不成立。两套 Kemadruma 定义的差别(角宫解除)是占星师清单待答项,未改。 - 相关记录:BUG-1174、BUG-1175。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1188 | Shadbala 四处计算错:Ayana 南北用未取模经度、时主在儒略日正午换日、平运动按周对齐且截断、Chesta 中点跨 0° 出错
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/shadbala.py_ayana_bala、_calendar_lords、_surya_mean_longitude、calc_chesta_bala_precise;全读 / 报告 / 星盘页 / 数据卡的 Shadbala 时力、动力分量与排名。生时校正打分只用 Sthana / Drik / Naisargika 三个分量,不受影响(见下)。 - 现象:两张虚构盘改前改后:盘 A 金星 9.37 → 8.37 Rupa、火星 5.37 → 6.56,排名第 3、4 位互换;盘 B 金星 7.19 → 7.61,排名第 2、3 位互换(逐星表在进度记录)。V.P. Jain 公布值对照:容差内 34 → 36 项。
- 根因:见标题四条。上游 yinduzhanxing
0afa2780已修。 - 修复:
08376cf0(Claude 移植算法修正与四条上游测试;存档 PyJHora Kala 对照测试按上游改法写三栏)。 - 验证:
tests/test_shadbala_ayana_wrap.py、test_shadbala_hora_civil_weekday.py、test_shadbala_mean_motion_continuity.py、test_chesta_circular_midpoint.py;tests/test_vp_jain_shadbala_benchmark.py三栏更新(火星 / 木星 / 土星 Chesta 进容差,金星 27.23 → 30.03 出容差)。生时校正:记忆化 golden v2 分数不变,v5 77 例评测与a18f8d4e逐字节相同,reported offset 900 例 0 例变化,故不升算法版本;因scripts/shadbala.py在冻结打分身份里,研究记录按 ERR-110 重新冻结(标签upstream_sync4_2026_10_02)。 - 防复发:四条移植测试。
- 相关记录:BUG-1189、ERR-110、ERR-111。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1189 | Shadbala 太阳最低要求写成 5.0 Rupa,BPHS 是 6.5(390 Virupa)
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/shadbala.pyMIN_REQUIRED(我方代码里唯一一张最低要求表;.get(pname, 5.0)的 5.0 是未知天体的默认值,vendored jyotishganit / dashaflow 的表已是 6.5);太阳的ishta_bala_pct与强弱档(数据卡shadbala.Sun.level、报告 Shadbala 表)。 - 现象:太阳的「达标百分比」虚高约 30%,强弱档高一档。名人回测 9 位全部降一档(例:充足 → 略弱、强 → 充足、略弱 → 弱)。
- 根因:表抄错。BPHS 第 27 章(Evaluation of Strengths)第 32–33 颂:「390, 360, 300, 420, 390, 330 and 300 Virupas are the Shad Bal Pindas, needed for Sūrya etc. to be considered strong」。上游 yinduzhanxing
0a6696c5仍是 5.0,需告知上游。 - 修复:
09c0efc2,代码注释引原颂。产品 2026-10-02 追加授权(原任务书写「不在本单」,已由产品改为本分支修)。 - 验证:
tests/test_shadbala_sun_minimum_bphs.py(整表等于原颂 Virupa / 60;太阳 ishta 百分比按 6.5 计)。生时校正打分不读最低要求,分数不变。 - 防复发:同上测试。
- 相关记录:BUG-1188。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1190 | Arudha Lagna / Upapada 有三个并行算法,同一张盘给出不同星座
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/yogas_doshas.py(全读modules.yogas_doshas,接口响应与 PL9 导出包原样带出);scripts/special_lagnas.py与全读modules.special_lagnas的Arudha_Lagna/Upapada_Lagna(报告特殊上升点表、PL9 文字、orchestrator_bridge);scripts/jyotish_engine.py_calc_transit_multi_reference的 AL 参考点。数据卡与生时校正本来就用jaimini.calc_arudha_padas。 - 现象:虚构盘 1990-06-15 10:30 北京:jaimini AL 白羊、UL 处女;
yogas_doshas给 AL 狮子、UL 摩羯;过运多参考点表给 AL 双子。1990-06-15 00:30 北京(命主在第 4 位):jaimini AL 射手,special_lagnas给双子。 - 根因:
yogas_doshas把「第几宫」当白羊起算的星座序号且无 1/7 例外;special_lagnas的 1/7 例外「从落点数第 10」(上游口径),jaimini「从源宫数第 10」,命主在源宫第 4 / 10 位时两者不同;过运参考点把「上升度数 + 12 宫主度数」当 AL。 - 修复(决策 4:三处都可见,改为取 jaimini 的值;jaimini 现行口径不改,上游「从落点数」不移植):删除
yogas_doshas.calc_arudha_lagna / calc_upapada_lagna与special_lagnas.calculate_arudha_lagna / calculate_upapada_lagna(及其 CLI 参数);yogas_doshas的两个字段、全读special_lagnas.Arudha_Lagna / Upapada_Lagna、过运参考点的 AL 都从jaimini.calc_arudha_padas取。 - 验证:
tests/test_arudha_single_source.py(10:30、00:30、02:30 三张虚构盘上四处 AL / UL 全等于 jaimini;并行函数已不存在)。 - 遗留:
special_lagnas的 A10(Karma Pada)仍用「从落点数」的例外,与 jaimini A10 在同类盘上不同;不在决策 4 范围,进度记录列为待产品决定。 - 相关记录:无。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1191 | 定位星链多星互换时硬选一颗「最终定位星」;Dasa 收敛写死无标定的概率
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/jyotish_engine.py全读final_dispositors(PL9 定位星表)、_calc_dasa_convergence。 - 现象:火星金星互换时,表上只写其中一颗为最终定位星;报告出现「85-92%」「75-85%」等概率。
- 根因:取「最后一个不重复元素」当终点;概率是写死的经验值,没有标定依据。上游 yinduzhanxing
e9beae6b已修。 - 修复:
bc9b9f89(Claude 移植final_dispositor_from_chain:只有自循环才有终点,多星循环写Cycle: A -> B -> A;probability = None并加probability_basis)。 - 验证:
tests/test_dispositor_cycle_resolution.py。 - 防复发:同上。
- 相关记录:无。
- 修复版本:
codex/upstream-sync4-20261002(未推送)。
BUG-1192 | 亡灵格(Preta Yoga)把第 11 宫写死为 Badhaka 宫;诅咒格「家族影响」与一条格局效果写早夭 / 早逝
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/curse_yoga_detector.py(/api/yogas凶星合相层、健康主题证据「Curse-yoga-risk」的严重度);references/yoga_rules.json母亲不利格的效果列表(格局卡、报告)。 - 现象:同一引擎里 Badhaka 宫有两套:
yoga_engine.YogaContext.is_badhaka按动 / 定 / 变取 11 / 9 / 7,curse_yoga_detector的 Preta Yoga 加重宫位写死[8, 11, 6]并注释「11宫=Badhaka」;固定、双体上升的盘被按错误的宫位加重。诅咒格「家族影响」写「子女早夭」「家族血脉停止」,母亲不利格效果第三条是「母亲早逝」。 - 根因:诅咒格表是早期照讲稿抄的,没有调用引擎已有的 Badhaka 规则;措辞未经「不作确定性死亡预测」边界审查。上游
b40c7fde/28f41912只改了 meaning 一列(本仓533c3a30,见 BUG-1197)。 - 修复:
7fb165ec。yoga_engine.badhaka_house()成为唯一定义(变动 11、固定 9、双体 7、上升未知返回 None),is_badhaka与诅咒格都调用它;Preta 两条的加重宫位改为占位符按上升展开,D9 用 D9 上升,上升未知时不加重。「家族影响」改为责任 / 传承主题并写明不能据此断定;母亲不利格删去「母亲早逝 / Early loss of mother」。 - 验证:
tests/test_curse_yoga_badhaka_house.py(12 个上升全取对、上升未知不加重、两份诅咒格源码与实际输出无早夭 / 早逝 / 死亡风险字样);上游tests/test_badhaka_rules.py(765ff66c)。前端两份报告阅读 fixture(report-reader-main-fictional*.json)是历史引擎输出,仍含「母亲早逝」,未重采。 - 防复发:新测试按正则扫源码与输出;同一规则只留一个函数。
- 相关记录:BUG-1197。
- 修复版本:
codex/upstream-sync5-20261002(未推送)。
BUG-1193 | 生时校正交付门槛三处写 3 件 2 域、政策文件写 4 件 3 域(产品 10-02 定为 4 件 3 域)
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:生时校正何时出时间范围卡、何时开始出选择题;Python 决策回执
gates.event_quality/domain_diversity;采集轮「还差」文案。 - 现象:
references/rectification_policy.v1.json写minConfirmationEvents 4 / minConfirmationDomains 3,但core/rectification-decision.tsMIN_STANDALONE_DATED_*、v9/evidence-model.tsMIN_ACCEPTANCE_*、core/types.tsMIN_TRAINING_EVENTS与scripts/rectification/decision_policy.pyMIN_ACCEPTANCE_*都是 3 件 / 2 域(后者只数训练件)。3 件事、2 个领域就会给范围;采集文案永远说「再来一件……就能开始筛」。 - 根因:区间交付门槛与唯一分钟确认门槛分开定义,四处各写常数;政策文件只被确认门引用。
- 修复(产品决策 TASK-upstream-sync5 §2.2):
758fee95。四处全部读政策文件同一对值,按「训练 + 留出」的全部带日期主经历计数;Python 回执的事件 / 领域门改数全部可计分经历,POLICY_VERSIONv3 → v4(候选 UUID 命名空间随之变化);收敛判定与推断后的训练门同口径;采集缺口文案写准还差几件、还缺几个方面(VOICE.md 同步);遗留表单birth-time-life-events.tsx(无引用方)文案同步。打分不变(77 例 A/B 逐分相同),ALGORITHM_VERSION维持 scoring-10;记忆化 golden v3、v2 冻结。 - 验证:前端 30 个校正测试文件(夹具补到 4 件 3 域保持原场景,或改断言写三栏)、
tests/test_rectification_confirmation_and.py、test_rectification_engine_memoization.py(v2 冻结 + 分数相等);npm test 失败名单与基线相同。 - 遗留(待产品):留出规则不变,第 3 个领域若只有一件常被留作 holdout,刚达标时实际参与打分的常是 2 个领域 3 件事。
- 防复发:门槛只在政策文件定义;
rectification-convergence-budget.test.ts断言常数与政策同源。 - 相关记录:BUG-647(训练门与留出解耦)、BUG-1048。
- 修复版本:
codex/upstream-sync5-20261002(未推送)。
BUG-1194 | 8 星制 Chara Karaka 把罗睺按正向度数排位
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/jaimini.pycalc_chara_karaka_8(全读 Jaimini 模块、报告 Karaka 表)。生时校正只用 Arudha,不读 Karaka。 - 现象:罗睺参与 8 星制时按星座内正向度数排序,罗睺常被排错位。
- 根因:传统规则罗睺逆行,排位用 30° 减度数;旧代码未反转。上游 yinduzhanxing
197672e3。 - 修复:
cb9d942b(PM 移植):排位用有效度数,原始度数与rahu_reverse_applied同时保留。 - 验证:
tests/test_jaimini.py新用例。 - 防复发:同上。
- 相关记录:无。
- 修复版本:
codex/upstream-sync5-20261002(未推送)。
BUG-1195 | 燃烧容许度全引擎不一致:不含月亮、不分顺逆行
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/jyotish_engine.pyCOMBUSTION_ORBS/is_combust(全读行星表、普通对话数据卡的 graha drishti 层)、scripts/pancha_mahapurusha.py(五大人格格局燃烧取消)。 - 现象:水星容许度 12°(应顺行 14° / 逆行 12°),月亮不判燃烧,逆行行星与顺行同一容许度;五大人格模块另有一张表。
- 根因:两张表各自手写;上游
080c286c、eaf6918b统一为顺 / 逆两列。 - 修复:
cf78d4ca、bdbff99a(PM 移植,含普通对话层按逆行传参);03d60e29测试锁两张表相等并在每个容许度边界上验证两个函数。 - 验证:
tests/test_consultation_native_layers.py、tests/test_combustion_orb_tables_agree.py;名人回测 golden 只有一位逆行金星由燃烧改为不燃烧。 - 防复发:两表相等测试。
- 相关记录:BUG-1154(graha drishti 燃烧共用一个判断)。
- 修复版本:
codex/upstream-sync5-20261002(未推送)。
BUG-1196 | Yogini Dasha 起运不按月宿
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/yogini_dasha.py(全读条件大运、报告时间系统表)。 - 现象:起始 Yogini 不是按出生月宿
(nakshatra + 3) mod 8取。 - 根因:上游
cb7fe017已修。 - 修复:
5172620c(PM 移植;上游对本仓不存在的测试文件的改动未带)。 - 验证:
tests/test_yogini_seed_profile.py。 - 防复发:同上。
- 相关记录:无。
- 修复版本:
codex/upstream-sync5-20261002(未推送)。
BUG-1197 | 诅咒格 / Rashi Tulya 的含义写「过早死亡风险」「死亡之神」等确定性断语
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/curse_yoga_detector.py、scripts/rashi_tulya_navamsa.py的 meaning 字段(凶星合相层、orchestrator_bridge的 RTN 证据)。 - 现象:Yama / Preta Yoga 含义写「死亡之神 sitting」「过早死亡风险」「可触发危险疾病或事故」。
- 根因:照讲稿抄入,违反「不作确定性死亡预测」。上游
b40c7fde、28f41912。 - 修复:
533c3a30、9959e514(PM 移植);同类残留(家族影响、Badhaka 写死)见 BUG-1192。 - 验证:
tests/test_curse_yoga_claim_boundary.py、tests/test_curse_yoga_badhaka_house.py。 - 防复发:两条测试扫源码。
- 相关记录:BUG-1192。
- 修复版本:
codex/upstream-sync5-20261002(未推送)。
BUG-1198 | 水星燃烧时 Budha-Aditya 仍按完整吉格输出
- 状态:resolved(分支验证;未部署)
- 首次发现 / 最近更新:2026-10-02 / 2026-10-02
- 影响面:
scripts/yoga_engine.py格局列表(报告、数据卡格局行)。 - 现象:日水合相即出 Budha-Aditya 全部吉效果,不看水星是否燃烧。
- 根因:上游
994638f0已修(本仓只取 Budha-Aditya 一段)。 - 修复:
45d6dbf6(PM 移植):水星燃烧时格局仍可见,strength=conditional、效果移到traditional_effects、附combustion_witness。 - 验证:
tests/test_budhaditya_combustion_gate.py。 - 防复发:同上。
- 相关记录:BUG-1195。
- 修复版本:
codex/upstream-sync5-20261002(未推送)。
BUG-1199 | 报告「Declination / Kranti / Speed」表的 Kranti 列整列为「-」
- 状态:resolved(已部署 staging
ae18952e:/api/healthdeployment.gitCommit=apiGitCommit=ae18952e,/login200、未登录/api/account401;真机清单docs/testing/report-chart-blank-columns-20261003.md待产品) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 影响面:
scripts/pl9_reader_export.pyp33 表(中英两版报告正文与目录)。 - 现象:产品真机截图,个人报告「Declination / Kranti / Speed」表七行 Kranti 全是
-,像没算出来。 - 触发条件:任何一份报告。
- 根因:reader 读
strengths.kranti_local.planets[*].kranti_degrees,全仓没有任何 producer;引擎 materialskranti自标missing_in_local / blocked(no independent Kranti producer)。表照样留了这一列。 - 修复:产品选删列:表改为
Planet | Degree | Declination | Speed,标题Declination / Speed。不按古典公式补算(无 PL9 原值可对照,Declination 已是真实赤纬)。materialskranti: blocked审计口径与full_report_quality_gate.RESTRICTED_MATERIAL_IDS不变。 - 验证:
tests/test_report_chart_blank_columns.py::test_declination_table_has_no_kranti_column(中英两版:无 Kranti、四列、无-)。 - 防复发:没有 producer 的字段不进读者表格;要加列先有来源。
- 相关记录:BUG-1200~1203(同一张截图)。
- 复发自:无
- 修复版本:
ae18952e(staging)
BUG-1200 | 报告「Ashtakavarga 完整宫位分数」把含上升合计标成 SAV,另两列为「-」
- 状态:resolved(已部署 staging
ae18952e:/api/healthdeployment.gitCommit=apiGitCommit=ae18952e,/login200、未登录/api/account401;真机清单docs/testing/report-chart-blank-columns-20261003.md待产品) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 影响面:
scripts/pl9_reader_export.pyp53 表(数据源scripts/ashtakavarga.py::_map_to_houses_values,未改)。 - 现象:表头「House | Sign | SAV | BAV/承接 | Full with 上升」,后两列全
-;「SAV」列 12 宫合计 386,与同页「Sign | SAV score」表(合计 337)对不上。 - 触发条件:任何一份报告。
- 根因:
_map_to_houses_values(full_sav, …)只写sign+sav_score,传入的是full_sav = sav + lagna_sav(337 + 49);reader 用row.get('sav') or row.get('sav_score')取到含上升合计标成 SAV,bav/full_with_lagna字段不存在。数字标错,不只是缺列。 - 修复:在 reader 拆分,
ashtakavarga.py不改——它在生时校正冻结身份(sealed_holdout_rerun.PRODUCTION_FILES)里,最初按任务书改它新增字段,快速门test_rectification_validation_integrity_gate4 条转红(ERR-110);改为 reader 用引擎已有的按星座 SAV(sav.scores)与上升 BAV(bav.Lagna.bindus)拆出,且要求SAV + 上升 BAV == house_scores_full.sav_score逐宫成立才填,否则 SAV / 上升两列写-、不出合计行,绝不把合计填进 SAV 列。sav_score语义与数值不变(生时校正在读)。表改为House | Sign | SAV | Lagna BAV | SAV + Lagna(中文版「上升 BAV」「SAV + 上升」),末行合计。 - 验证:
tests/test_report_chart_blank_columns.py:每行 SAV + 上升 BAV = 最后一列、合计 337 / 49 / 386(中英两版);SAV 列 = 按星座 SAV、上升列 = Lagna bindus、末列 =house_scores_full.sav_score;删掉 Lagna BAV 时 SAV / 上升两列为-且无合计行。验证完整性门禁与封存评测新鲜度测试保持全绿(冻结身份未变)。 - 防复发:报告表格列只读语义明确的字段,不用
or链兜到别的口径。 - 相关记录:BUG-1199。
- 复发自:无
- 修复版本:
ae18952e(staging)
BUG-1201 | 星盘页本命表上升行的星宿 / 宿主 / pada 一直是「—」
- 状态:resolved(已部署 staging
ae18952e:/api/healthdeployment.gitCommit=apiGitCommit=ae18952e,/login200、未登录/api/account401;真机清单docs/testing/report-chart-blank-columns-20261003.md待产品) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 影响面:
scripts/jyotish_engine.py::compute_chart_data(/api/chart的ascendant)、frontend星盘页 D1 / 月亮盘表上升行。 - 现象:上升行星宿、宿主、pada 三格为「—」,九曜都有。
- 根因:
compute_chart_data给九曜按nak_span算了nakshatra / nakshatra_pada / nakshatra_lord,result["ascendant"]漏了;前端按「引擎不给就不推」留空。 - 修复:抽
_nakshatra_fields(sidereal_lon),九曜、计都、上升共用;/api/chart缓存cache_schema_version4 → 5(缓存 TTL 900 s,改版本只影响查找键;指纹 / 绑定只用出生资料,不受影响)。前端只改注释。 - 验证:
tests/test_report_chart_blank_columns.py(边界 0°、13°20′ 两侧、360°;golden 盘上升 Uttara Phalguni 1 足、九曜与 golden 逐星一致);frontend/tests/chart-varga-table.test.tsx上升行断言;旧缓存无字段仍渲染「—」。goldenchart-view-golden.json用_nakshatra_fields重录(命令写在sourceCommand)。 - 防复发:同一个量(星宿)只有一个计算函数,新增盘点直接复用。
- 相关记录:BUG-1076(D1 表逐字节基线:本次放开上升三格,见进度记录三栏)。
- 复发自:无
- 修复版本:
ae18952e(staging)
BUG-1202 | 星盘页分盘表「庙旺」只做三档,敌宫 / 中性等一律「—」
- 状态:resolved(已部署 staging
ae18952e:/api/healthdeployment.gitCommit=apiGitCommit=ae18952e,/login200、未登录/api/account401;真机清单docs/testing/report-chart-blank-columns-20261003.md待产品) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 影响面:
/api/varga_full(新模块scripts/varga_dignity.py)、jyotish_engine.varga_dignity_level、frontend/src/lib/chart-view-{varga,mapper,contract}.ts、vedic-chart-tables.json。 - 现象:D9 表只有入旺 / 自宫 / 落陷有字(太阳在金牛=敌宫、月亮在摩羯=中性都是「—」),产品以为没算。
- 根因:BUG-1076 / 1099 有意只做三档,前端自带一份
vargaDignity表;引擎 Vimsopaka 路径早有完整判定没接过来。 - 修复(产品 10-03 拍板,有限推翻 BUG-1076「三档庙旺」与 BUG-1099「三档庙旺保留」;BUG-1099 能力声明规则不变):抽
varga_dignity_level(planet, varga_planets)(暂时关系取该分盘自身位置,与 Vimsopaka 一致),Vimsopaka 与/api/varga_full共用;/api/varga_full每星加dignity_level;前端只校验并用 D1 状态列同一措辞(DIGNITY_LABELS)显示;删除前端vargaDignity及只为它服务的四张表。旧缓存无等级时整列隐藏,不自算。 - 验证:Vimsopaka 抽函数前后同机 A/B(
PYTHONHASHSEED=0,3 张虚构盘cmd_full_reading的 vimsopaka / varga_full / jaimini / 行星 / 上升)逐字节相同;tests/test_api_server_security.py::test_varga_full_standard_divisions_carry_the_vimsopaka_dignity_level(与varga.calc_all_vargas的 Vimsopaka 输入逐星同座同级);tests/test_chart_page_vedic_tables_contract.py(真实 TS mapper 投影 = 引擎等级,golden 覆盖入庙 / 落陷 / 中性 / 入敌 / 入友;措辞表 =DIGNITY_LABELS);frontend/tests/chart-varga-table.test.tsx、chart-view-varga-dignity.test.ts。 - 防复发:分盘庙旺只有引擎一套;页面不得再写第二套判定。
- 相关记录:BUG-1076、BUG-1099。
- 复发自:无(范围扩展,不是回归)。
- 修复版本:
ae18952e(staging)
BUG-1203 | 报告末尾「分盘核对」把正文已画过的分盘再画一遍
- 状态:resolved(已部署 staging
ae18952e:/api/healthdeployment.gitCommit=apiGitCommit=ae18952e,/login200、未登录/api/account401;真机清单docs/testing/report-chart-blank-columns-20261003.md待产品) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 影响面:
frontend/src/components/personal-report/report-calculation-charts.tsx、personal-report-page.tsx、src/lib/report-chart-block.ts、report-export-blocks.ts、scripts/pl9_reader_export.py(提示句)。 - 现象:正文「本命与分盘北印度图盘」已画全部分盘,正文后又出现「分盘核对」+ 提示句 + D1、D2… 第二遍,在目录外、样式散。
- 根因:页面在 Markdown 正文后无条件挂
ReportCalculationCharts;导出早已按正文围栏去重,页面没用同一规则。 - 修复:抽
fencedReportChartIds,页面与导出共用;页面只画正文没有围栏的结构化分盘(新报告整段消失,老报告缺哪张补哪张)。提示句由引擎写到正文分盘标题下(英文版走术语表),组件里的提示删除。 - 验证:
frontend/tests/report-calculation-charts.test.tsx(真实 golden 正文 → 整段不渲染;无围栏正文 → 照旧且不带提示;页面与导出跳过同一批盘);导出原有测试(含冻结哈希)全绿;tests/test_report_chart_blank_columns.py::test_chart_section_carries_the_divisional_caveat(中英)。 - 防复发:同一张盘页面只画一次;页面与导出共用去重函数。
- 相关记录:BUG-607 / 616(报告图盘渲染)。
- 复发自:无
- 修复版本:
ae18952e(staging)
BUG-1204 | 8 星制 Chara Karaka 排位把子女星、配偶星、父亲星放错位置
- 状态:resolved(分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 的配套核对(对照原典的笔误,不需裁决);任务书
TASK-astrologer-rulings-batch1-20261003T6。 - 影响面:
scripts/jaimini.pyKARAKA_8;下游报告 Jaimini Karaka 表(pl9_reader_export、jyotish_engine参考版)。生时校正不读 Karaka(打分路径 grep 无引用)。 - 现象:报告 8 星制表第 5 行写「子女星 PK」、第 7 行「配偶星 DK」、第 8 行(度数最低)「父亲星 PiK」。
- 根因:
KARAKA_8第 5/7/8 位写成 PK/DK/PiK。BPHS 第 32 章 8 星制次序为 AK、AmK、BK、MK、PiK、PK、GK、DK。上游 yinduzhanxing 同样写错。 - 修复:
0cf2249e按 BPHS 改表;7 星制不变;BUG-1194 的罗睺 30° 反转保留。 - 验证:
tests/test_chara_karaka_8_bphs_order.py(表次序;公开 AA 盘 Steve Jobs Raman/平均交点下 PiK=罗睺、PK=月亮、DK=火星;报告 8 星制行按 BPHS 次序)。 - 防复发:同上测试锁表与真盘;已在
docs/research/上游通知中记录上游同错。 - 相关记录:BUG-1194、BUG-489。
- 复发自:无
- 修复版本:待发布
BUG-1205 | 落陷取消自动叫「王者格」(Neecha Bhanga Raja Yoga),条件不列
- 状态:resolved(分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 乙14;任务书 T8。
- 影响面:
references/yoga_rules.json四条neechabhanga_*;consultation_native_layers.TRADITIONAL_YOGAS;frontend/src/lib/personal-report-generation.ts类别映射;报告瑜伽总表。 - 现象:卡片
traditional_yogas写「Neecha Bhanga Raja Yoga」;报告把落陷取消列在「王权/事业」类,组合条件只写「落陷取消格局」,看不出哪条成立。 - 根因:数据驱动规则与咨询闭列表都把落陷取消命名为 Raja Yoga;唯一带条件清单的
yogas_doshas.calc_nicha_bhanga_raj_yoga(条件 A–F)没有接到用户可见面。 - 修复:
df21e591。新scripts/main_yoga_detectors.py以calc_nicha_bhanga_raj_yoga为唯一条件来源(新增结构化cancellation_conditions),生成「落陷取消」行(中英),由YogaEngine.detect追加;四条neechabhanga_*标reference_only(保留、改名、不再求值);卡片闭列表改为「落陷取消(Neecha Bhanga)」并附「成立条件:…」;报告类别「落陷取消」、归混合组;个人报告类别映射neecha_bhanga → special。 - 验证:
tests/test_neecha_bhanga_conditions.py(规则 reference_only 且名称无 Raja;公开 AA 盘 Obama 条件与来源一致;卡片标签;报告混合组、全文无 Neecha Bhanga Raja;前端src/grep 无)。 - 防复发:同上测试对
frontend/src与yoga_rules.json做 Raja 文案 grep。 - 相关记录:BUG-1175、BUG-1159。
- 复发自:无
- 修复版本:待发布
BUG-1206 | Kemadruma 四套实现;算法版月亮邻宫仍计罗计,同盘同时出 Kemadruma 与 Anapha
- 状态:resolved(分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 甲7;任务书 T2。
- 影响面:
references/yoga_rules.jsonkemadruma_yoga(角宫解除 ky2)、kemadruma_variant(邻宫计罗计);scripts/yoga_engine.py_detect_solar_lunar_yogas;scripts/divisional_yoga.py;卡片chart.yogas与报告瑜伽总表。 - 现象:同一张盘卡片有 Kemadruma,报告却因角宫有星判解除;泰勒盘报告写「月后瑜伽(计都在月亮第 12 宫)」同时卡上有 Kemadruma;齐达内卡上同时出现 Kemadruma 与「Kemadruma (Empty Houses)」。
- 根因:Kemadruma 由四处各写一套。BUG-1187 只把 JSON 规则改成「只数五星」,遗漏了
yoga_engine._detect_solar_lunar_yogas这段算法版(它把太阳、罗计计入月亮/太阳邻宫),所以复发;BUG-1187 的测试只覆盖规则 ID(bvr_*、kemadruma_yoga),不覆盖算法版产出的Anapha Yoga/Sunapha Yoga/Durudhura Yoga名字,因此没拦住。分盘版按分盘里不存在的house字段比相邻,永远判「无邻星」,且把太阳算邻星。 - 修复:
6916bbae。主检测器为yoga_expansion.detect_kemadruma(只数火水木金土、不启用角宫解除、角宫只有罗计也不解除),经main_yoga_detectors进规则引擎;两条 JSON 规则reference_only;算法版太阳/月亮邻宫格局只数五星;分盘版改为按星座相邻、同一口径。 - 验证:
tests/test_kemadruma_main_detector.py(角宫只有罗计不解除;角宫有星不解除;计都单独在邻宫不出 Anapha;九位公开名人盘 Kemadruma 与 Sunapha/Anapha/Durudhura 不同盘共存;分盘同口径)。九盘报告与卡片一致(见进度记录)。 - 防复发:同上测试按产出名字(不是规则 ID)断言。
- 相关记录:BUG-1187(遗漏,关联原号)。
- 复发自:BUG-1187(遗漏面:算法版邻宫格局)
- 修复版本:待发布
BUG-1207 | 五大人格四套实现:燃烧从不检查、逆行判失效、被主检测器判掉的格局经规则引擎回到卡上
- 状态:resolved(分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 甲5;任务书 T1。
- 影响面:
scripts/pancha_mahapurusha.py、jyotish_api_server._detect_yogas、references/yoga_rules.json(mahapurusha_*、*_own、moolatrikona_*、hamsa_trikona、bvr_maalavya_yoga)、yogas_doshas.calc_pancha_mahapurusha_yoga、jyotish_engine.cmd_yoga(行星行)、卡片traditional_yogas、报告瑜伽总表。 - 现象:泰勒盘木星在 9 宫巨蟹(三方宫,不在角宫)卡片仍写「Hamsa Yoga」(来自非经典的
hamsa_trikona);逆行就判失效;凶星同宫直接消灭格局;燃烧从不检查。 - 根因:
_detect_yogas调主检测器时不传太阳度数,燃烧检查不执行;主检测器把逆行、同宫判 invalid;规则引擎与yogas_doshas的同名规则不做任何破格判断,按名字合并后谁先到算谁。 - 修复:
de793901。主检测器按定稿:只从命宫角宫算;逆行不破格;燃烧(共用燃烧表)、凶星同宫、凶星照(太阳 7、火星 4/7/8、土星 3/10/7;罗计不照)只记reduced(部分减弱)并列原因;D9 落陷判不成立保留现状(只在调用方给navamsa_sign时检查,产品路径不给;定稿未提,列入下轮问题)。_detect_yogas改用主检测器并取太阳经度;同名 JSON 规则与yogas_doshas旧聚合改reference_only;规则引擎追加主检测器行;cmd_yoga行星行补经度与逆行,使报告与卡片的燃烧判断一致;卡片标签附「部分减弱:…」。 - 验证:
tests/test_pancha_mahapurusha_main_detector.py(逆行不破格、燃烧 reduced、凶星照 reduced、罗计不照、被判掉的格局不经其他路径回来、九位公开名人引擎/卡片路径与主检测器一致、报告路径与 API 路径燃烧一致);tests/test_pancha_mahapurusha_self_conjunction.py、tests/run_all.py断言按三栏更新。 - 防复发:同上;
tests/test_combustion_orb_tables_agree.py继续锁燃烧表。 - 相关记录:BUG-1180、BUG-1175、BUG-1195。
- 复发自:无
- 修复版本:待发布
BUG-1208 | Shadbala 网站自定分档冒充强弱结论
- 状态:resolved(分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 甲13;任务书 T3。
- 影响面:
chart.shadbala(卡片)、报告 Shadbala 表、证据快照排名、健康叙述。 - 现象:卡片与报告只写「极强 / 强 / 充足 / 略弱 / 弱 / 极弱」,看不出这是本站按最低要求百分比(150/125/100/75/50)自定的档,也不先说是否达到 BPHS 最低要求。
- 根因:
shadbala.calc_shadbala的strength_level原样进各面,没有来源标注。 - 修复:
5fdf2f74。shadbala.py不动(冻结身份),新scripts/shadbala_display.py在展示层生成min_required、meets_minimum、minimum_check(达到/未达到 BPHS 最低要求)、level_source: site_band、level「…(网站分档)」;报告表加「BPHS minimum」「Site band」两列(中英);卡片每星只留rupas/minimum_check/level(0d6af77c,控制卡片体积)。 - 验证:
tests/test_shadbala_minimum_first.py;frontend/tests/consult-evidence-card-v2-20260927.test.ts新增卡片断言。 - 防复发:同上测试。
- 相关记录:BUG-1189。
- 复发自:无
- 修复版本:待发布
BUG-1209 | 八分法净化没接上,报告用的「Sodhita」其实是扣掉日火土贡献,还标「BPHS标准」
- 状态:resolved(分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 乙8;任务书 T7。
- 影响面:
scripts/ashtakavarga.py(删calc_sodhita_av)、新scripts/ashtakavarga_shodhana.py、/api/ashtakavarga、全量解读、报告 SAV/BAV/宫位表、卡片sav_by_house/ashtakavarga、个人报告savScores、事实表。 - 现象:报告、卡片、宫位表只有原始 SAV/BAV;报告参考版的「Sodhita」是从每行 BAV 扣掉太阳、火星、土星的贡献,却标「BPHS标准」。
- 根因:标准的三方净化、单宫主净化函数(
calc_trikona_shodhana/calc_ekadhipatya_shodhana)只有测试在调用;展示面一直读原始值。 - 修复:
ea78804b。逐行 BAV 三方净化 → 单宫主净化 → 净化后 SAV(七行之和),占星宫只数七星;删去旧函数;报告/卡片/个人报告/事实表主值用净化后、原始值并列且两列都有标签。过运打分仍用原始点数(决策记录)。 - 验证:
tests/test_ashtakavarga_sodhita_chain.py(PVR 书例 Chart 7 BAV 的三方净化逐行对照规则并与 PyJHora 4.8.7 一致;单宫主净化六条规则;真盘链路与宫位字段);tests/test_report_chart_blank_columns.py三栏更新;frontend/tests/report-fact-tables.test.ts、report-interpretive-facts.test.ts。 - 防复发:同上。
report-density-fictional-engine.json只重采该叶(scripts/research/refresh_density_fixture_ashtakavarga.py),其余叶逐字节不变。 - 相关记录:BUG-1200、ERR-115。
- 复发自:无
- 修复版本:待发布
BUG-1210 | 报告「Bhava Bala」表是自定加减分(吉星 +2、凶星 −1.5),正式三分量没接到用户面
- 状态:resolved(分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 甲13;任务书 T4。
- 影响面:
scripts/shadbala.py(改名calc_local_bhava_score,移出calc_shadbala结果)、scripts/bhava_bala.py(新bhava_bala_from_chart)、全量解读modules.bhava_bala、报告 p32 表、参考版表与财富/摘要行。 - 现象:报告 Bhava Bala 表的分数来自无出处的本地加减分,列「关键因素」写「Jupiter in H5 (Benefic): +2」。
- 根因:同名两套:
shadbala.calc_bhava_bala(本地评分)驱动报告;正式三分量bhava_bala.calc_bhava_bala只接在/api/bhava_bala。 - 修复:
6feb1a4a。报告改用正式三分量(Bhavadhipati = 宫主 Shadbala Virupa、Bhava Dig、Bhava Drishti;等宫),列 Total(Virupa / Rupa);本地评分改名、不再出现在任何结果里。其他使用者:无(补救、健康叙述只读 Shadbala Rupa)。 - 验证:
tests/test_bhava_bala_formal.py;tests/test_report_longform_gaps2.py、tests/test_shadbala_complete.py按三栏更新。 - 防复发:同上测试对报告全文 grep「(Benefic): +2」。
- 相关记录:BUG-1208。
- 复发自:无
- 修复版本:待发布
BUG-1211 | 罗睺本宫三处说法;瑜伽检测里天蝎/水瓶宫主取「较强者」
- 状态:resolved(分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 甲12;任务书 T5。
- 影响面:
jyotish_engine._get_dignity_level(D1 状态列、Vimsopaka、varga_dignity)、yoga_engine.YogaContext(宫主、入庙判断)。 - 现象:罗睺在处女显示「入庙(Own Sign)」;规则引擎里天蝎宫主有时是计都、水瓶宫主有时是罗睺,而宫主链、功能吉凶、前端固定取火星、土星。
- 根因:引擎写罗睺处女/计都双鱼为本宫,瑜伽引擎沿用 PyJHora 动态共主,
narayana_dasha另写水瓶/天蝎。 - 修复:
b0329ef0(单独提交,便于回退)。删去罗计「本宫」判定(不新增罗计庙旺);瑜伽主路径默认固定传统主星,_stronger_co_lord只在dynamic_co_lords=True(Jaimini 变体)时用;narayana_dasha不动。 - 验证:
tests/test_node_own_sign_and_fixed_lords.py。生时校正:v5 77 例与 reported-offset / sealed holdout 重放见进度记录。 - 防复发:同上测试。
- 相关记录:BUG-1158。
- 复发自:无
- 修复版本:待发布
BUG-1212 | 年运年主没说明是暂用 Muntha 主星(占星师口径为 Panchavargiya)
- 状态:resolved(只加标注;分支
codex/astrologer-rulings-batch1-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师定稿 2026-10-03 甲11;任务书 T9。
- 影响面:咨询年运层
annual_tajika、卡片年运段、报告两处年运表。 - 现象:卡片与报告只写年主(Varshesha),不说明选法;报告选法行写「Muntha 所在星座的守护星(当前本地口径)」。
- 根因:Panchavargiya 选法的原生生产者仍 blocked,现行年主取 Muntha 主星,未在显示处标注。
- 修复:
eb00b0f7。年运层加varshesha_basis「暂用 Muntha 主星;占星师口径为 Panchavargiya,待接入」,卡片与投影白名单带上;报告两处年运表都有「Selection basis」行(中英)。Muntha 维持「本命上升 + 已满年数」,muntha.calc_muntha_from_sun_sign不在任何主路径。 - 验证:
tests/test_year_lord_basis_label.py。 - 防复发:同上;Panchavargiya 接入另开单。
- 相关记录:BUG-1028。
- 复发自:无
- 修复版本:待发布
BUG-1213 | 五大人格 D9 落陷判「不成立」,与占星师裁定(只算减弱)不符;且产品路径从未传 D9,这条一直没执行
- 状态:resolved(分支
codex/astrologer-rulings-batch2-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师第四轮裁定 2026-10-03 问 1;任务书
TASK-astrologer-rulings-batch2-20261003T1。 - 影响面:
scripts/pancha_mahapurusha.py(主检测器)、scripts/main_yoga_detectors.py(规则引擎行)、jyotish_api_server._detect_yogas(chart.yogas)、/api/pancha_mahapurusha、卡片traditional_yogas、报告瑜伽总表。 - 现象:主检测器写着「D9 落陷 →
is_valid: False」,但只在调用方给navamsa_sign时检查;咨询、报告、规则引擎都不给,所以这条从未生效(BUG-1207 已记)。占星师裁定 D9 落陷只算减弱。 - 根因:规则写成破格,且 D9 来源依赖调用方,调用方都不传。
- 修复:
bc03bba2。D9 落陷时格局仍成立(status: formed),加d9_dignity_modifier: "weakened"、d9_sign,原因列「D9 落陷,兑现减弱」(英文debilitated in D9: delivery weakened),强度为部分减弱;只有 D1 条件(命宫角宫 + 入庙/入旺)不成立才是not_formed(include_not_formed=True时返回该行)。没给navamsa_sign时由黄道经度推出 D9,所以卡片、报告、/api/pancha_mahapurusha都检查。 - 验证:
tests/test_pancha_mahapurusha_main_detector.py(D9 落陷只减弱、D1 不成立为 not_formed、卡片与规则引擎同一行同一原因;既有两条按三栏改写)。9 位公开名人盘:没有一张的五大人格星在 D9 落陷,卡片与报告结论不变(golden 只多出d9_dignity_modifier: null)。 - 防复发:同上测试。
- 相关记录:BUG-1207。
- 复发自:无
- 修复版本:待发布
BUG-1214 | 年运年主一直取 Muntha 主星;Panchadhikari 选法上游已实现但没同步(另:本地 PyJHora 查询函数引用了未定义的名字)
- 状态:resolved(分支
codex/astrologer-rulings-batch2-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师第四轮裁定 2026-10-03 问 3;任务书 T2。移植来源:上游
yinduzhanxingc7117f92。 - 影响面:
scripts/tajika.py(新增calc_panchadhikari_year_lord、Tri-Rasi 主星表、PLANET_TO_INDEX/INDEX_TO_PLANET、带岁差参数的 PyJHora 查询函数)、references/tajika_panchavargiya_rules.json(新增)、新scripts/tajika_year_lord.py、solar_return.solar_return_full_report与jyotish_engine.cmd_tajika(两个年运生产者)、annual_tajika_pack、咨询年运层varshesha_basis、报告两处「Selection basis」、Mudda Dasha / Tri-Pataka(从年主起)。 - 现象:卡片与报告的年主就是 Muntha 星座的主星,只标「暂用」(BUG-1212)。
- 根因:上游
calc_panchadhikari_year_lord(五类候选 → 吉相位照年盘上升者中取 Panchavargiya 最强 → 否则凶相位者中取最强 → 否则全体最强)没有同步到本仓;BUG-1037 只删掉了引用它的死代码。另:本仓_external_pyjhora_panchavargiya_score*用了未定义的PLANET_TO_INDEX/INDEX_TO_PLANET/_PYJHORA_PVB_SIDECAR_CACHE,因为没有调用方一直没暴露。 - 修复:
0afcc2d9。按上游原样移植函数、依赖与规则 JSON(blobe8c15ac3一致)。本地差异两处:① 加关键字参数muntha_sign_idx——上游的 Muntha 主候选用「年盘上升 + 年数」,本仓 Muntha 一律「本命上升 + 已满年数」(问 3 维持),生产调用方传入;不传时与上游行为相同。② 产品路径不把年盘 JD 交给 PyJHora(AGPL,API 镜像与 CI 都没有;本机有时会选出与生产不同的年主,且进程内调用不设岁差、同输入跨进程分数不同),Panchavargiya 一律native_proxy,标注「Panchavargiya 为本地近似计算」;JYOTISH_YEAR_LORD_PYJHORA_REFERENCE=1恢复上游查询,仅供研究。两个生产者共用select_annual_year_lord,冲突门照常比较。输出candidates/candidate_list(每个候选的分数、engine、吉凶相位)、selection_basis、一句话依据(中英)、muntha_basis。卡片varshesha_basis与报告「Selection basis」改为这句话,去掉 BUG-1212 的暂用标注。 - 验证:
tests/test_tajika_panchadhikari_year_lord.py(四种选法分支、Muntha 参数、规则 JSON 与上游 blob 一致、两个生产者同盘同年主、研究开关)、tests/test_year_lord_basis_label.py、tests/test_report_reader_main.py(三条按三栏改写)。9 位公开名人 2026 年年盘:同输入下与上游函数逐项相同 9/9(native);生产结果与上游年主不同 2/9,原因都是 Muntha 起点(本命 vs 年盘)。上游tests/test_tajika.py没有 Panchadhikari 用例;上游 CLI 冒烟用例依赖私有本地盘,未移植。 - 防复发:同上测试。
- 相关记录:BUG-1212、BUG-1037、BUG-1028。
- 复发自:无
- 修复版本:待发布
BUG-1215 | 第二时间轴 Narayana 一律从上升起、一律顺行,各 profile 只改标签;Sanjay Rath 版起运宫未能复现原书样表
- 状态:investigating(标注已修;Rath 版起运宫未复现原书 Table 17 / 18,按红线未接任何用户可见面与打分;T5 试算未做)
- 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师第四轮裁定 2026-10-03 问 4;产品选方案 A;任务书 T3、T4(T5 未做)。移植来源:上游
yinduzhanxingc7117f92(规则账本 JSON / md、原书样表 JSON)。 - 影响面:
narayana_dasha.narayana_dasha_full_report的使用方(咨询modules.narayana_dasha、报告 Step 4.13、cmd_narayana_dasha)、卡片timing.narayana、参考版报告 Narayana 段与 dasha 家族表;新scripts/narayana_rath.py(研究用,未接)。 - 现象:
calc_narayana_mahadasha始终从上升起、顺黄道;select_narayana_seed_sign只写进seed_selection元数据。 - 根因:第二时间轴从未按任何作者的规则实现。
- 修复:
- T4
9eb22f63、693467bf:新narayana_legacy_label(放在消费层,narayana_dasha.py在冻结身份里不动)给结果加profile: legacy_reference_only、algorithm_note「旧算法(从上升起、一律顺行),待替换为 Sanjay Rath 版」(中英)与短标签algorithm_label「旧算法(从上升起、一律顺行)」;卡片timing.narayana.algorithm原样抄短标签(全文会让乔布斯婚恋卡超 12,000 字预算,卡片里 Narayana AD/PD 去掉可由起止年龄推出的years,最大卡 11,979 < 基线 11,983)、投影白名单algorithmlabel、参考版报告 Narayana 段和 dasha 家族表、CLI 输出都带这句。删掉没有使用方的narayana-dasha --variant-profile(唯一把 Chara Dasha 作者名当作 Narayana profile 的地方)。全仓用户可见面没有「K.N. Rao / kn_rao」挨着 Narayana。 - T3
7cb332bc:narayana_rath.calc_narayana_rath(Table 10 顺序、土星在起运宫 Table 11、计都反转、年数jaimini_odd_footed_dignity_v2+ 双主星jaimini_dual_lord_source_v1、子运 12 等分 Table 13/11/14、起运宫用现有select_narayana_seed_sign、平局输出parameter_sensitive并列两种起法)。给定原书起运宫时,三张样表 36/36 个大运星座与年数全对;但起运宫:Table 15 对(巨蟹),Table 17 选出处女(原书双鱼),Table 18 平局(原书巨蟹)。
- T4
- 验证:
tests/test_narayana_legacy_label.py、tests/test_narayana_rath_book_tables.py(含「没有任何脚本导入 narayana_rath」)、frontend/tests/consult-evidence-card-20260927.test.ts新增一条断言。 - 未解决:起运宫强弱规则缺两处口径,见进度记录「下轮问占星师」。答复前 Rath 版不接用户可见面、不进打分;T5 试算不做。
- 后续(2026-10-03):第五轮裁定后起运宫规则重做、Rath 版与旧算法并列显示,见 BUG-1218。
- 防复发:同上测试。
- 相关记录:BUG-1211(
narayana_dasha双主星不动)、BUG-1218。 - 复发自:无
- 修复版本:待发布(标注部分)
BUG-1216 | 年内 Mudda Dasha 从年主起排,年主一变全年月份分段跟着变;占星师裁定应从出生月宿推算
- 状态:resolved(分支
codex/astrologer-rulings-batch3-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师第五轮裁定 2026-10-03 问 7(起运星 =(已满年数 + 出生月宿序号 1–27 − 2)mod 9,余 1 日、2 月、3 火、4 罗、5 木、6 土、7 水、8 计、0 金,与年主无关);任务书
TASK-astrologer-rulings-batch3-20261003T1。移植来源:上游yinduzhanxingc7117f92scripts/tajika.py:calc_mudda_with_dasha_balance(原样)。 - 影响面:
solar_return.solar_return_full_report、jyotish_engine.cmd_tajika、全量解读 Step 9(modules.tajika)三个年运生产者;annual_tajika_pack的mudda_dasha/monthly_windows;咨询年运层annual_tajika.mudda_dasha(数据卡);参考版报告「Mudda Dasha 年度阶段」表。 - 现象:第二批改年主选法后,9 位公开名人里 6 位的年内各月分段跟着变(任务书实证)。按月宿起运后,9 位中 8 位的起运星与旧口径(从年主起)不同,只有小布什恰好都是月亮。
- 根因:旧
tajika.calc_mudda_dasha把年主当第一运;上游已有按出生月宿起运、带余额的实现但从未同步。 - 修复:T1 提交(见进度记录)。
calc_mudda_with_dasha_balance原样移植(函数文本 sha256 钉在测试里,并与上游c7117f92逐字比对);新scripts/tajika_mudda.py:annual_mudda_dasha是唯一产品入口:余额取本命月亮(moon_at_birth_time),年长具名solar_year_365_25(产品默认,与上游默认 365.25 一致)/savana_360(仅研究),记录annual_start、annual_end、annual_duration_days、completed_years、natal_moon_longitude、natal_moon_nakshatra_number、balance_profile、independent_of_year_lord;periods裁到年度窗口内并带months供原有月份窗口使用,上游的 10 段原样留在periods_full。calc_solar_return_chart新增返回birth_moon_longitude。cmd_tajika与全量解读 Step 9 合用_tajika_annual_core(Step 9 原来用calc_year_lord取 Muntha 主星当年主,也一并改为 Panchadhikari)。数据卡分段按引擎给的起止日期重新锚定到本次咨询的年度,并在下一次太阳返照处截断。旧calc_mudda_dasha不再被任何scripts/*.py调用。 - 验证:
tests/test_tajika_mudda.py(27 个月宿 × 6 个年数的起运公式;只改年主 Mudda 表逐项不变;两个生产者同表;数据卡分段来自引擎日期;无产品路径调用旧函数;移植函数逐字一致);tests/test_tajika_panchadhikari_year_lord.py一条按三栏改写。9 位公开名人 2026 年年盘起运星与公式 9/9 一致(表见进度记录)。咨询 golden 用真实引擎重采。 - 防复发:同上测试。
- 相关记录:BUG-1214、BUG-1212。
- 复发自:无
- 修复版本:待发布
BUG-1217 | 年主比强弱用的是 D1/D2/D3/D9/D12 庙旺近似分,不是 Tajika 正式五分力量
- 状态:resolved(分支
codex/astrologer-rulings-batch3-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师第五轮裁定 2026-10-03 问 5;任务书 T2。移植来源:上游
yinduzhanxingc7117f92scripts/tajika.py:_calc_native_panchavargiya_bala及其依赖_tajika_relation、_panchavargiya_component、_hudda_lord、_tajika_drekkana_lord、_navamsha_sign_idx、_tajika_uchcha_bala、_round_half_up(原样)。 - 影响面:
tajika.calc_panchadhikari_year_lord(候选比较)、tajika_year_lord(依据句与候选表)、卡片varshesha_basis、报告两处「Selection basis」、英文版对照词表。 - 现象:BUG-1214 接入的年主选法在拿不到 PyJHora 时退回
native_proxy(五张分盘庙旺分),依据句注明「Panchavargiya 为本地近似计算」。 - 根因:上游正式五分法(Kshetra/graha 30、Uchcha 20、Hadda 15、Drekkana 10、Navamsa 5,Tajika 友敌按年盘宫距 3/5/9/11 友、2/6/8/12 中、1/4/7/10 敌;原始满分 80,score = 原始 / 4,满分 20;规则
references/tajika_panchavargiya_rules.json)存在但年主选法没有调用(上游同样没调用,见 BUG-1219);第二批没有移植这组函数。 - 修复:T2 提交。原样移植上述函数;
calc_panchadhikari_year_lord一次算出 7 星五分力量,候选panchavargiya_engine: tajika_native_five_fold,另带panchavargiya_raw_total;native_proxy不再参与选年主——年盘缺星时返回 blocked(由select_annual_year_lord记为 Muntha 主星 fallback 并写明原因),不再退回近似分。依据句标注改为「(按 Tajika 五分力量)」/「(by Tajika five-fold strength)」,英文版词表同步。研究开关JYOTISH_YEAR_LORD_PYJHORA_REFERENCE=1行为不变。 - 验证:
tests/test_tajika_five_fold_panchavargiya.py(手算一例:太阳白羊 5° 分量 22.5 / 19.44 / 11.25 / 7.5 / 2.5、原始 63.19、score 15.80;300 张随机盘各分量不超上限、原始 ≤ 80、score = 原始 / 4;候选分数即五分法;近似函数被调用即失败;缺星 blocked;8 个函数与上游逐字一致);tests/test_year_lord_basis_label.py两处、tests/test_tajika_panchadhikari_year_lord.py两处按三栏改写;tests/test_report_english_dictionary.py通过。9 位公开名人 2026 年年主变 3/9(泰勒 水星→火星、梦露 月亮→火星、卡罗 土星→月亮),Mudda 不受影响(BUG-1216)。 - 未改:年运强度层
calc_tajika_strength_layers(报告「Harsha / Panchavargiya」强度表)仍用庙旺近似,不在本单范围,列入进度记录。 - 防复发:同上测试。
- 相关记录:BUG-1214、BUG-1219。
- 复发自:无
- 修复版本:待发布
BUG-1218 | Sanjay Rath 版 Narayana 起运宫按第五轮 7 级通则重做,与旧算法并列显示;Table 18(Indira)是原书内部矛盾
- 状态:resolved(分支
codex/astrologer-rulings-batch3-20261003,未推送;待验收)。Rath 版只作并列展示,打分与时间轴结论仍用旧算法;是否替换等 T5 试算结果由产品决定。 - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师第五轮裁定 2026-10-03 问 1–4(Rath《Narayana Dasa》p.35–36、p.48、p.50 说明 2、p.51、p.53 Table 12、p.54 Table 14、p.63、p.65–66);产品负责人裁定土星与计都同在起运宫时计都优先(PyJHora 做法);产品负责人 2026-10-03 晚修改红线 3(依据只读审计
docs/research/shared_engine_reuse_audit_2026_10_03.md,da95d92d;任务书59bb7da3)。任务书 T3、T4、T5。移植来源:上游yinduzhanxingc7117f92scripts/narayana_dasha.py(select_narayana_seed_sign(strength_profile="rath_p36_role_conjunction_v1")的一、二级计数,build_narayana_sign_sequence的rath_ketu_table12_v1分支,_RATH_CLASSICAL_MOOLATRIKONA_SIGNS);上游测试test_rath_role_strength.py的 p36 用例、test_varsha_narayana_native.py的土星起运宫用例(大运部分);上游chara_narayana_rath_wadiyar_table25_replay_2026_09_07.json的子运行(写进测试)。 - 影响面:
scripts/narayana_rath.py;展示层scripts/narayana_legacy_label.py(rath视图)、咨询modules.narayana_dasha.rath、全量解读 Step 4.13、narayana-dasha命令行、参考版报告「Narayana Dasha 交叉时间轴」段、timing / annual 数据卡base.timing.narayana_rath、英文对照词表。冻结身份文件narayana_dasha.py未改。 - 现象:第二批的起运宫判定只有前两级、且第二级不数同宫,Table 17 选出处女、Table 18 平局;产品面只有旧算法。
- 根因:上游按原书写好的分级函数没有同步,第二批从零重写,规则缺级。
- 修复:
- T3
b526be47、ad1d65b1:compare_signs七级通则(① 占据星数,含罗计;② 木星 / 水星 / 宫主三角色同宫或星座相位,每角色一次、不按星体去重;③ 占据星最高尊贵 旺 3 > Moolatrikona 2 > 本宫 1,再比有尊贵的星数;④ 双体 > 固定 > 活动;⑤ 宫主星座内度数,罗计取 30 − 度数;⑥ 宫主落在与所主星座奇偶相反的星座;⑦ 大运年数长者强),上一级分出即停,输出strength_level_decided;计都在起运宫按 Table 10 方向反转、保留步进,土星在起运宫 Table 11 顺行,二者同在计都优先、method_note「PyJHora 兼容:计都优先」;子运比较大运星座与第 7 宫取强者,从其宫主(双主按jaimini_dual_lord_source_v1)所在星座起,12 等分,Table 13 / 11 / 14,记录antardasha_start_rule。Indira:BOOK_EXAMPLES["rath_p66_indira_ak"](book_internal_contradiction: true与证据句),只在显式指定巨蟹起运宫时复现并标book_example_exception,同时原样输出通则自己的裁决(摩羯,第 5 级);七级全平时的 AK 裁决保留为显式开关,产品路径不用。 - T4
935e26fb:并列展示,见影响面;数据卡只上 timing / annual(其余单领域卡已在 11,979 / 12,000);golden 真实引擎重采。
- T3
- 验收(修订后红线 3):Table 15 巨蟹(第 1 级)、Table 17 双鱼(第 2 级)通则复现,12 个大运顺序与年数全对;Table 18 显式巨蟹 12/12;原书 5 张 D1 软件裁决表 30 对 28/30(两处 Wadiyar 双子/射手、处女/双鱼在第 5 级不符,测试钉住),双主星 8/10;Table 25 子运 24/36(双子段 0/12,随强弱判定)。
- 验证:
tests/test_narayana_rath_rules.py(7 级每级构造盘、全平与 AK 开关、计都反转、土星顺行、土计同宫、一二级与上游函数逐项相同、上游 p36 与 Varsha 土星用例);tests/test_narayana_rath_book_tables.py(三张样表、软件表、Table 25;两条按三栏改写,「没有脚本导入」改为「只展示层导入、冻结打分文件与 rectification/ 不导入」);tests/test_narayana_legacy_label.py(新增 3 条:旧字段不变、报告并列与英文对照、golden 带rath);frontend/tests/consult-narayana-rath-card-20261003.test.ts(timing / annual 卡带 Rath 大运且取自引擎原值、其他领域卡不带)。v5 77 例改前改后逐项相同。T5 试算见进度记录。 - 未解决:Wadiyar 两对、两处双主星、子运方向(p.47 正文 vs Table 25)、第 3 级比法、第 1 级是否数罗计等,见进度记录「下轮问占星师」。
- 防复发:同上测试。
- 相关记录:BUG-1215、BUG-1211。
- 复发自:无
- 修复版本:待发布
BUG-1219 | 上游两处年主缺陷:选年主没用正式五分法;Panchadhikari 的 Muntha 候选用年盘上升推
- 状态:upstream(只记录,不改上游;本仓已各自处理)
- 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:占星师第五轮裁定 2026-10-03 问 5、问 6(确认是上游 bug);任务书 T6。上游
yinduzhanxingc7117f92。 - 现象:① 上游
calc_panchadhikari_year_lord经_resolve_year_lord_panchavargiya_score比强弱,拿不到 PyJHora 时退回_calc_panchavargiya_bala_for_planet(D1/D2/D3/D9/D12 庙旺近似),同文件里的正式五分法_calc_native_panchavargiya_bala只被年运强度层用。② 上游 Muntha 候选 = 年盘上升 + 年数,而 Muntha 应为本命上升 + 已满年数(第三轮定、第五轮问 6 维持)。 - 本仓处理:① BUG-1217 改用正式五分法;② BUG-1214 起生产调用方传
muntha_sign_idx(本命上升 + 已满年数),不传时与上游一致。 - 相关记录:BUG-1214、BUG-1217。
- 复发自:无
- 修复版本:—(上游)
BUG-1220 | 太阳返照时刻偏 3–7 分钟:出生太阳与求解器不在同一参考系,年盘还截到整分钟
- 状态:resolved(staging 已部署
01929bae,2026-10-03 22:36/api/health的gitCommit与apiGitCommit均为该提交;/login200、未登录/api/account401) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:共享引擎复用核对
docs/research/shared_engine_reuse_audit_2026_10_03.md§S1;任务书TASK-annual-return-frame-20261003。移植来源:上游yinduzhanxing7cc6425d(出生太阳改在求解器参考系重算)、46ed9399(年盘保留秒),只取这两处。 - 影响面:
solar_return.calc_solar_return_chart→solar_return_full_report、jyotish_engine.cmd_tajika、全量解读 Step 9、annual_tajika_pack、咨询年运层(数据卡)、参考版报告年运段:年盘上升、年主、Sahams、Mudda 起点时刻。生时校正不用年盘,不受影响。 - 现象:同一张盘同一岁差,本站返照时刻与上游、PyJHora 差几分钟(Rath 标准盘 2025 年差 6 分 16 秒)。9 位公开名人 × 2000–2026 年共 243 张年盘里,年盘上升星座错 13 张、年主错 11 张,昼夜判定 0 张受影响。
- 触发条件:任何盘。偏差大小取决于出生时刻的章动等参考系差(约 ±17″ 以内,折合返照时刻约 ±7 分钟);年盘上升恰在星座边界附近的年份会换星座。
- 根因:① 目标经度取
compute_chart_data的出生太阳degree_raw,求解器_find_solar_return_swe用_get_sun_lon_jd(swe.calc_ut+sidereal_flags)逐步逼近,两套参考系对出生太阳差 15.1″(标准盘)。② 起年盘时用整点分钟调用compute_chart_data,丢了秒。上游 9 月 7–8 日已修,本站引擎同步停在f2241463(9 月 3 日),之后solar_return.py只按 BUG-1095 补过本地化,漏了这两处(ERR-109 同类)。 - 修复:
calc_solar_return_chart中目标经度改为_get_sun_lon_jd(birth_jd_ut),拿不到才退回出生盘值;返回solar_return.target_sun_frame(swe_sidereal_flags/birth_chart_degree_raw)。起年盘传second=sr_dt_ut.second。上游同期加的position_mode等研究回放参数未搬;本地化保留本站实现。 - 验证:
tests/test_solar_return_frame.py:三例返照 JD 与 PyJHoradrik.next_solar_date参照值(只存数字)差 ≤ 0.5 分钟,实测 0.0 / 0.021 / 0.006 分钟;目标经度等于求解器参考系出生太阳(< 1″);年盘时刻与返照差 < 1 秒。换回旧代码这 5 条全部失败。243 张年盘前后对比、v5(三段与基线逐项相同)、快速门、前端失败名单比对见进度记录。咨询两份 golden 用真实引擎重采后逐字节不变(三张盘 2026 年的年盘上升与年主恰好不变,日期只存到天)。 - 补修(2026-10-04):
tests/test_report_reader_main.py::test_reader_annual_conflict_keeps_both_real_producer_values在01929bae转红(他会话二分发现)。该测试不在快速门里,本单验收只跑了快速门和定向测试,没跑 Python 全量,漏了。虚构盘 2026 年返照从 08:48:04 变 08:41:55(PyJHora 差 0.09 分钟),年盘上升从金牛 1.3° 落到白羊 29.6°,年主由火星变木星;断言按三栏改为木星。补跑 Python 全量:修前3c27c38e与修后各 62 个失败,名单逐条相同(均为既有环境失败)。 - 防复发:上述测试;同步上游太阳返照时,核对目标经度与求解器同一参考系(已补进 ERR-109)。改引擎计算值的轮次,验收要跑 Python 全量并与基线比对失败名单,不能只跑快速门。
- 相关记录:BUG-1095、BUG-1026~1028、ERR-109。
- 复发自:无(ERR-109 同类漏同步)
- 修复版本:staging
01929bae(生产待提升)
BUG-1221 | Rath 版 Narayana 当前大运只上年运、时运卡,婚恋、事业等领域卡看不到
- 状态:resolved(分支
codex/astrologer-rulings-batch4-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:第三批进度记录「下轮问占星师」第 9 条(产品问题);产品负责人 2026-10-03 决定;任务书
TASK-astrologer-rulings-batch4-20261003T1。 - 影响面:
frontend/src/lib/consultation-evidence-card.tsbaseTiming(base.timing.narayana_rath);三份字数断言(consult-evidence-card-20260927、consult-evidence-card-v2-20260927、consult-condensed-checklist-20260927)。 - 现象:BUG-1218 T4 把 Rath 当前大运只放进 timing / annual 卡(
RATH_NARAYANA_CARD_DOMAINS),婚恋、事业、财富、健康、父母等单领域卡只有旧算法 Narayana。 - 触发条件:任何非年运 / 时运的单领域或双领域对话。
- 根因:字数预算。单领域卡最大 11,979 / 12,000(乔布斯婚恋,含清单),Rath 大运约 +174 字会超(BUG-1160/1161 的 12,000 / 18,000 口径)。
- 决策(产品负责人 2026-10-03,修改 BUG-1160/1161 的预算决定):全部领域卡都加 Rath 当前大运;卡片预算 12,000 → 12,500,加方法清单的总量 18,000 → 18,500。
- 修复:删掉
RATH_NARAYANA_CARD_DOMAINS与withRath,baseTiming对所有卡都放 Rath;字段按任务书红线 2 收短为algorithm(引擎短标签「Sanjay Rath 版(并列参考,未进打分与时间轴结论)」)、seed_sign、md: {sign, start_age, end_age};大运宫主、年数(= 止 − 起)、起运宫第几级、子运留在报告。旧算法narayana与「旧算法」标签不变。不改提示词。三份字数断言改为 12,500 / 18,500(三栏写在测试注释与进度记录)。 - 验证:
frontend/tests/consult-narayana-rath-card-20261003.test.ts三条:timing / annual 卡、三张 golden 盘 × 12 个领域、9 位公开名人回测 golden × 4 条路由 × 12 个领域,每张卡都带 Rath 当前大运、字段只有上述三项且取自引擎原值、不含子运、旧算法标签仍在。9 盘 × 12 领域卡长度表(改前 / 改后 / 余量)见进度记录:契约 + 卡最大 10,284 → 10,244(年运卡因收短字段反而少 40),含清单 total 最大 13,734 → 13,694;单领域非清单重口径最大 11,913(巴拉克·奥巴马综合);乔布斯婚恋 total 11,979 → 12,113。 - 防复发:上述测试;预算断言 12,500 / 18,500。
- 相关记录:BUG-1160、BUG-1161、BUG-1218。
- 复发自:无
- 修复版本:待发布
BUG-1222 | 年运强度层的 Panchavargiya 一列与年主选法不是同一套分数
- 状态:resolved(分支
codex/astrologer-rulings-batch4-20261003,未推送;待验收) - 首次发现 / 最近更新:2026-10-03 / 2026-10-03
- 来源:第三批进度记录「下轮问占星师」第 10 条(产品问题);产品负责人 2026-10-03 决定;任务书
TASK-astrologer-rulings-batch4-20261003T2。 - 影响面:
scripts/tajika.calc_tajika_strength_layers→solar_return_full_report的tajika_strength(/api/annual、/api/tajika的report.tajika_strength,solar-return命令行 JSON)、varshaphala的tajika_strength。scripts/solar_return.py未改。 - 现象:BUG-1217 起年主改用 Tajika 正式五分法比强弱,同一份年运结果里的「Harsha / Panchavargiya」强度层 Panchavargiya 一列仍是
blocked(unified_varga_core_and_golden_oracle_parity_required),函数里还留着 D1/D2/D3/D9/D12 庙旺近似的旧实现(return之后不可达)。两处口径不一致。 - 实际情况(开工核对,与任务书事故实证 2 的出入):本仓没有渲染出来的「强度表」——参考版 / 读者版报告与年运包(
annual_tajika_pack)都不带tajika_strength,前端没有使用方;该层只出现在上面列的 API / 命令行 JSON 里。近似分没有上表,上表的是blocked。 - 根因:第二、三批只改了年主选法(BUG-1214 / 1217),强度层停在更早的「等 oracle 对齐」阻塞状态;上游
c7117f92这里已改用五分法。 - 决策(产品负责人 2026-10-03):报告年运强度表统一改用 Tajika 正式五分法,不再显示庙旺近似分。
- 修复:
calc_tajika_strength_layers的panchavargiya_bala每星取_calc_native_panchavargiya_bala(与年主选法同一函数、同一输入planet_lons):status: usable、engine: tajika_native_five_fold、label「Tajika 五分力量」/label_en「Tajika five-fold strength」、score(满分 20)、raw_total(满分 80)、五个分量(graha / uchcha / hudda / drekkana / navamsha)、component_trace、规则来源;层级字段panchavargiya_method/_en。年盘缺星时该列 blocked 并写原因,不退回近似分。删掉函数里不可达的庙旺近似旧实现。Harsha Bala 不变。Harsha + Panchavargiya 合成强度没有裁定,仍 blocked(原因改为combined_tajika_strength_not_ruled)。 - 仍用庙旺近似的地方:
tajika._resolve_year_lord_panchavargiya_score在native_scores未传时(上游原行为,无产品调用方);_calc_panchavargiya_bala_for_planet本身保留供该路径与「近似函数被调用即失败」的测试使用。_strength_interpretation/_strength_headline原只被不可达代码调用,现无调用方,未删。 - 验证:新增
tests/test_annual_strength_five_fold.py(3 张虚构盘 × 2025 / 2026:年主每个候选的分数、原始分、五个分量与强度层同一颗星逐项相同;强度层没有近似分的分盘字段、满分 20、score = 原始 / 4;Harsha 形状不变);tests/test_tajika.py改写一条、新增两条(近似函数被调用即失败、缺星 blocked);tests/test_api_server_security.py年运端点一条。9 位公开名人 2026 年强度层前后对比见进度记录:年主、候选分、Harsha 9/9 不变,Panchavargiya 列由 blocked 变为五分法分数且与候选分逐项相同。 - 防复发:上述测试。
- 相关记录:BUG-1217、BUG-1214、BUG-1219。
- 复发自:无
- 修复版本:待发布
BUG-1223 | 年主选不出时用 Muntha 主星顶替,输出一个伪年主
- 状态:resolved(分支
codex/astrologer-rulings-batch5-20261004,未推送;待验收) - 首次发现 / 最近更新:2026-10-04 / 2026-10-04
- 来源:任务书
TASK-astrologer-rulings-batch5-20261004T5;产品负责人 2026-10-04「年主选不出时不再用 Muntha 主星顶替,与上游对齐」;上游yinduzhanxing03bea6eddocs/research/tajika_shared_entrypoint_repair_2026_10_03.md第 3 条。 - 影响面:
scripts/tajika_year_lord.select_annual_year_lord、jyotish_engine._tajika_annual_core(cmd_tajika、全量解读 Step 9)、solar_return.solar_return_full_report(年主与 Tri-Pataka)、annual_tajika_pack(年主字段)、咨询年运层(数据卡)、读者版 / 参考版报告年运段。 - 现象:Panchadhikari 被 block(年盘缺太阳 / 月亮、五分力量算不出、五类候选都不在年盘)或返照盘算不出时,年主写成 Muntha 主星(
selection_basis: fallback_muntha_sign_lord_after_panchadhikari_block,依据句「年盘不全,暂用 Muntha 主星」),Tri-Pataka 也拿它算。 - 触发条件:年盘不全。正常年盘不触发(9 位公开名人 2026 年 0/9)。
- 根因:BUG-1214 接入 Panchadhikari 时保留了 BUG-1212 以前的 Muntha 主星兜底,只是写明 fallback;上游已改为直接 blocked。
- 修复:选不出时返回
status: "blocked"、reason、year_lord: None、selection_basis: panchadhikari_blocked_no_year_lord,依据句「年主暂无法确定(原因)」及英文对照(tajika_year_lord.blocked_text/blocked_year_lord);cmd_tajika返照盘算不出时同样 blocked(不再调calc_year_lord)。没有年主时 Tri-Pataka 写blocked: year_lord_blocked(不再用空串或默认木星)。annual_tajika_pack两个生产者都 blocked 时字段保留生产者原因(reason、blocked_detail),年运卡不因年主 blocked 整卡失效:varshesha为空、varshesha_basis为该句;报告年主表「Selection basis」显示该句,英文词表补齐 6 句。Mudda 与年主无关(BUG-1216),不受影响。 - 验证:新增
tests/test_year_lord_blocked_no_fallback.py7 条(选法 blocked 无替身;太阳返照生产者年主与 Tri-Pataka blocked、Mudda 照常;cmd_tajika无年盘 blocked;年运包与数据卡显示该句;中英报告;英文对照;生产者里不再出现 fallback 标记)。9 位公开名人 2026 年两个生产者的年主、Tri-Pataka、Mudda 改前改后逐字节相同(脚本见进度记录)。 - 防复发:上述测试。
- 相关记录:BUG-1212、BUG-1214、BUG-1216、BUG-1217。
- 复发自:无
- 修复版本:待发布
BUG-1224 | Rath 版 Narayana 第 3 级不计罗睺、计都的尊贵,也不计落陷
- 状态:resolved(分支
codex/astrologer-rulings-batch5-20261004,未推送;待验收) - 首次发现 / 最近更新:2026-10-04 / 2026-10-04
- 来源:占星师第六轮裁定(2026-10-04)问 2,书页 Rath《Narayana Dasa》Table 9 p.41–42、p.67;任务书 T2。
- 影响面:
scripts/narayana_rath._occupant_dignity→sign_strength第 3 级 → 起运宫、子运起点(只在 Rath 版内部)。本命庙旺表不动(BUG-1211「罗计不设本宫」在 D1 主路径仍适用)。 - 现象:
_occupant_dignity对罗睺、计都直接返回 0;落陷与无尊贵同为 0。原书 p.67(Indira 盘)「计都落陷,按第一来源 Rule (3) 弱于火星」复现不出:狮子(火星)对双子(计都)此前在第 4 级按双体宫 > 固定宫判双子。 - 根因:第五轮没有罗计尊贵的裁定,实现时按「不计」处理;落陷没有单列档位。
- 修复:罗计按 Rath Table 9:本宫罗睺水瓶 / 计都天蝎,Moolatrikona 罗睺处女 / 计都双鱼,旺取 Phalita 运那一组(罗睺双子 / 计都射手;金牛 / 天蝎那组只用于 Ayur 运),落陷在旺宫对宫。第 3 级档位:旺 3 > Moolatrikona 2 > 本宫 1 > 无 0 > 落陷 −1,仍先比最高档、再比有尊贵的星数(第六轮问 2「比法维持现状」)。落陷档对九颗星一律适用(Rule 3 是通则;这一点列入下轮问题)。
- 验证:
tests/test_narayana_rath_rules.py新增:罗计与火星各档位 16 例;p.67 原书盘狮子对双子在第 3 级判狮子;构造盘同一结论;罗计尊贵只在 Rath 模块内。既有第 7 级构造盘因太阳落陷天秤会在第 3 级分出,日月互换后仍演示第 7 级(三栏写在测试注释)。验收底线不变:Table 15、17 通则自动复现,Table 18 显式 12/12,软件表 28/30,Table 25 子运 24/36。12 张 golden 盘的 Rath 起运宫与大运不变(golden 只多了source文字)。 - 防复发:上述测试。
- 相关记录:BUG-1211、BUG-1218。
- 复发自:无
- 修复版本:待发布
BUG-1225 | Rath 版 Narayana 子运方向:p.51 例外未核对,书内另两种说法未标注
- 状态:resolved(分支
codex/astrologer-rulings-batch5-20261004,未推送;待验收) - 首次发现 / 最近更新:2026-10-04 / 2026-10-04
- 来源:占星师第六轮裁定问 7(Table 25 的做法 + p.51 土星 / 计都例外;p.47 正文、J.S. 2.4.31–32 标为书内分歧);任务书 T3。
- 影响面:
scripts/narayana_rath.antardasha(antardasha_start_rule.direction)、narayana_legacy_label.rath_report_lines_zh(参考版报告一行)、pl9_reader_english_terms。 - 核对结果:
_ad_order_profile与裁定一致,未改:默认 Table 13(方向看子运起点星座奇偶,12 个起点逐一验证);大运星座有土星 → Table 11(一律顺行);有计都 → Table 14(Table 13 的反向);同在 → 计都优先(产品已定)。 - 缺口:输出里没有写方向依据,也没有标出原书另两种说法(p.47 / J.S. 1.1.34「看大运星座奇偶」;J.S. 2.4.31–32「大运星座为奇或其宫主落奇宫则顺行」)与 Table 25 不符。
- 修复:每个子运规则带
direction:实际方向、规则(中英)、book_divergence两条(来源、说法中英、该说法给出的方向、是否与实际不同、applied: false)。参考版报告当前大运子运表上方加一行「子运方向:…(书内分歧,未采用:…)」,英文词表补齐。 - 验证:新增测试:12 个起点的 Table 13 / 14 / 11 方向;计都在大运宫的构造盘(反向、与 Table 13 同起点首步相反)、土计同在走 Table 14 带「计都优先」;
book_divergence两条字段;原书 Table 25 金牛大运(土星在内)12/12 且 p.47 说法会给逆行(differs: true)。英文版 Rath 行无中文(既有测试)。 - 防复发:上述测试。
- 相关记录:BUG-1218。
- 复发自:无
- 修复版本:待发布
BUG-1226 | Wadiyar 盘两对强弱与原书软件判定不同,输出未说明原因
- 状态:resolved(分支
codex/astrologer-rulings-batch5-20261004,未推送;待验收) - 首次发现 / 最近更新:2026-10-04 / 2026-10-04
- 来源:占星师第六轮裁定问 6b(p.36、p.95);任务书 T4。
- 影响面:
scripts/narayana_rath(BOOK_SOFTWARE_DIFFERENCES、calc_narayana_rath输出known_book_differences)。 - 现象:原书 Table 25(Krishnaraja Wadiyar IV)软件判定取双子(对射手)、处女(对双鱼);7 级通则在第 5 级(水星 1.3° 对木星 10.0°)取射手、双鱼。此前只在测试里列为「预期不符」。
- 根因:占星师确认这是原书软件用了第 7 级 Rule 8,属软件特例;通则无误。
- 修复:保持通则结果;登记
book_software_rule8_exception(表、两对、原书 / 通则各取哪宫、第几级、中英说明、裁定出处),每次 Rath 输出都带known_book_differences。 - 验证:
tests/test_narayana_rath_book_tables.py新增:软件表 30 对里不符的恰是登记的两对,且都在第 5 级判出通则结果;输出带该登记。 - 防复发:上述测试。
- 相关记录:BUG-1218。
- 复发自:无
- 修复版本:—(标注)
BUG-1227 | Rath 版双主星(天蝎、水瓶)没按原书 p.43 (a)–(e) 选
- 状态:resolved(第七轮裁定问 1 选 A,推翻第五批红线 2 的 Table 17 年数底线;分支
codex/astrologer-rulings-batch6-20261004) - 首次发现 / 最近更新:2026-10-03 / 2026-10-04
- 来源:第三批 BUG-1218 验收(软件表双主星 8/10);占星师第六轮裁定问 6a(p.43 (d)、p.116);任务书 T1。
- 影响面:
scripts/narayana_rath.resolve_dual_lord(第五批仍调冻结的jaimini_dual_lord_source_v1)→ 天蝎 / 水瓶大运年数、第 2 级宫主角色、第 5–7 级、子运起点。展示层narayana_legacy_label。不进打分。 - 现象:原书 5 张软件表的双主星对上 8/10:Table 23 水瓶原书土星、我们罗睺;Table 25 天蝎原书计都、我们火星。
- 原书原文(PDF Page 40 / 41,书页 p.43;BPHS 46.158–163):(a) 两主都在本宫 → 12 年;(b) 两主同在另一宫 → 从本宫数到该宫;(c) 一主在本宫、另一主在别处 → 取在别处的那颗;(d) 两主分落别的星座 → 比两主所落星座的力量(脚注 33 指第 1 章力量来源),强者给年数;(e) 两宫力量相等 → 取给出年数较多的那颗(原书例:计都射手、火星处女、都无同宫 → 取火星 10 年)。p.116:两主同宫时经度高者强。
- 试算(研究,未落地;实现见研究分支
codex/narayana-rath-rectification-trial-20261004的--dual-lord p43):(a)–(e) 按上文,(d) 用本模块 7 级compare_signs(叠加 BUG-1224 罗计尊贵)。软件表双主星 9/10(Table 23 水瓶改对,土星:摩羯 2 角色 > 双子 1 角色,第 2 级;Table 25 天蝎改对,计都:双鱼计都 Moolatrikona,第 3 级),新错 Table 23 天蝎:处女(火星)对射手(计都),射手得木星两角色、第 2 级判计都,原书判火星。Table 17 年数 12/12 → 10/12:同一张标准盘的原书 Table 17 / Chart 3 天蝎取火星 10 年、水瓶取罗睺 8 年,(d) 给计都 1 年、土星 1 年。强弱 28/30、Table 25 子运 24/36 不变。 - 根因(原书内部不一致):标准盘(Table 17 / 23 同一出生资料)三处说法互相冲突——p.43 (e) 的例句与 Chart 3 把「计都射手、火星处女」当作力量相等(取火星 10 年),但按第 2 级(第二来源)射手得木星两角色、按 BUG-1224 计都在射手入旺,任何逐级比较都判计都;Chart 3 水瓶取罗睺 8 年(注「土星在本宫摩羯 Rule 5(c)」,即把土星落自己另一宫当作 (c) 的「在本宫」),而同书 Table 23 软件判定土星强。没有一条通则能同时满足 Table 17 年数与 Table 23 双主星。占星师答复里「Table 23 水瓶:金牛 3 星 > 处女 1 星」的推算对应的是 Table 25(Wadiyar)的水瓶(土星金牛、罗睺处女),Table 23 本盘是土星摩羯、罗睺双子。
- 处理(第五批,已推翻):当时按红线 2 停下,
_lord仍走冻结的jaimini_dual_lord_source_v1(双主星 8/10,Table 17 年数 12/12)。 - 修复:第七轮书面回复经产品负责人采用,问 1 选 A(正文优先,不倒改通则去凑 Table 17)。
narayana_rath.resolve_dual_lord在本模块实现 p.43 (a)–(e):(d) 用本模块 7 级compare_signs;(c) 只认天蝎 / 水瓶本身;(a)(b) 同宫身份按 p.116,只倒算计都,经度全同返回未决;比较途中再遇到同一双主星则返回未决。不改冻结的_resolve_narayana_lord。软件表双主星 9/10,唯一不符是 Table 23 天蝎(书火星、第 2 级计都)。Table 17 年数 10/12(天蝎计都 2 = 基本 1 加旺 1,水瓶土星 1),不再作通过线。 - 验证:
tests/test_narayana_rath_book_tables.py、tests/test_narayana_rath_rules.py(火星白羊 / 土星摩羯不触发 (c);同宫经度;计都 0° 比较值为 30;全同未决;遇环未决)。 - 防复发:上述测试钉住 9/10 与 Table 23 天蝎这一条不符;年数断言钉新值,不钉书印。
- 相关记录:BUG-1218、BUG-1224、BUG-1228、BUG-1229。
- 复发自:无
- 修复版本:待发布
BUG-1228 | Rath 版第 5 级把罗睺度数也倒算
- 状态:resolved(分支
codex/astrologer-rulings-batch6-20261004) - 首次发现 / 最近更新:2026-10-04 / 2026-10-04
- 来源:第七轮裁定问 3(p.71 正文与脚注 42、p.116 Rule 2)。任务书 T2。
- 影响面:
scripts/narayana_rath._strength_degree(第 5 级宫主度数,以及同宫双主星比经度)。Chara Karaka 八星排序不改。 - 现象:强弱比较里罗睺、计都都用 30 − 宫内度数。原书脚注 42 写明强弱计算只倒算计都。
- 根因:第 5 级沿用了 Chara Karaka 对罗睺的倒算。
- 修复:实体星与罗睺用原始宫内度数,逆行也不倒算;只有计都用 30 − 宫内度数。计都在 0° 时比较值为 30,不再取模成 0。完全相等返回未决。
- 验证:原书软件表 30 对的胜者与改前相同,仍是 28/30(不符仍是 Wadiyar 两对,第 5 级)。新增同宫经度测试。
- 防复发:
tests/test_narayana_rath_rules.py的经度用例。 - 相关记录:BUG-1227。
- 复发自:无
- 修复版本:待发布
BUG-1229 | Rath 版罗计为宫主时,入旺不加算、落陷不减年
- 状态:resolved(分支
codex/astrologer-rulings-batch6-20261004) - 首次发现 / 最近更新:2026-10-04 / 2026-10-04
- 来源:第七轮裁定对 p.40 (a) 的推论(产品负责人采用问 1 的「正文优先」)。原书 Chart 3 水瓶罗睺 8 + 1 = 9 年。任务书 T3。
- 影响面:
scripts/narayana_rath.rath_period_years(大运年数与强弱第 7 级)。冻结的calculate_narayana_period_years不改,七颗实体星仍走它。 - 现象:年数旺陷表只有七颗实体星。罗睺在双子、计都在射手不加 1 年;罗睺在射手、计都在双子不减 1 年。
- 根因:Phalita 旺陷没有接到 Rath 版年数上。
- 修复:基本年数仍用奇偶足计数。罗计按 Phalita 入旺 +1、落陷 −1(罗睺旺双子 / 陷射手,计都旺射手 / 陷双子),再截在 0–12。输出分栏
lord、base_years、dignity_adjustment、years、adjustment_source。 - 验证:Table 17 天蝎计都基本 1、修正 +1、最终 2;Table 18 水瓶罗睺基本 2、修正 −1、最终 1。Chart 3 若取罗睺,水瓶为基本 8、修正 +1、最终 9,与书注一致;本单按 A 取的是土星,最终 1,差异记在进度记录。
- 防复发:
tests/test_narayana_rath_rules.py::test_node_dignity_adjusts_the_period_and_clamps。 - 相关记录:BUG-1224、BUG-1227。
- 复发自:无
- 修复版本:待发布
BUG-1230 | 门禁 validate 单步日志过大,失败页打不开
- 状态:resolved(分支
codex/gate-log-volume-20261004) - 首次发现 / 最近更新:2026-10-04 / 2026-10-04
- 来源:第六批
baf4ae8b的门禁失败(Gitea task 6691、run 3170)。产品负责人打开 job 页时,「Validate backend, package, frontend, and database contracts」这一步日志打不开,看不到报错。任务书docs/tasks/TASK-gate-log-volume-20261004.md。当时 staging 仍部署153c6e99。 - 影响面:
.gitea/workflows/backend-quality-gate.yml的 validate job;scripts/run_quality_gate.py的默认输出;frontend的npm test在 CI / Gitea 下的输出。触发条件、paths 过滤、job 依赖、runner、密钥、超时、发布与部署未改。 - 现象:这一步把 ruff、py_compile、快速门、隐私扫描、
python -m build、npm test、lint 合成一个 run(非 push 还有前端构建)。写任务书时本机测得:npm test约 26,800 行,快速门约 14,000 行,python -m build约 2,900 行,合计 4.4 万行以上、2 MB 以上。成功的检查也刷屏,失败点埋在中间,网页渲染不出来。 - 根因:门禁把全部明细当默认输出,并且把 7 个检查挤在同一个 step。
- 修复:快速门默认改为捕获子进程输出。成功只打一行;失败打失败标记、退出码和最后 200 行,完整输出写到
gate-logs/并打印路径。--verbose恢复原来的透传。没有给子进程新加超时。前端npm test在本机仍输出 TAP;在 CI / Gitea 用点号进度,完整 TAP 写到gate-logs/frontend-tests.tap,日志末尾打 tests / pass / fail / cancelled,以及每条失败的名字和报错正文(每条最多 60 行)。点号报告自带的整段堆栈不再重复打进日志。退出码仍是测试进程的退出码。workflow 把原来的一个 step 拆成 7 个顺序 step;python -m build的明细写入gate-logs/python-build.log,失败时打最后 200 行再以原退出码退出。 - 验证:本机测量见
docs/tasks/PROGRESS-gate-log-volume-20261004.md。快速门精简输出 18 行且退出码 0。--verbose打出 27,093 行;同一次里有一条既有测试失败,单独重跑通过。CI=true npm test仍能看到失败名字和报错正文,退出码为 1(本机既有失败,不是这次改出来的)。合同测试锁住 7 步顺序、set -euo pipefail和构建重定向。Gitea 网页是否打得开要等推送后第一次门禁,本记录不声称页面已经打开。 - 防复发:
tests/test_run_quality_gate_output.py;frontend/tests/staging-backend-workflows.test.ts里原有两条测试加上步序、禁止continue-on-error、构建重定向。没有新增test()。 - 相关记录:BUG-995(前端测试要同时看 cancelled 和退出码)。
- 复发自:无
- 修复版本:待发布
BUG-1231 | 咨询链校正闸同步白等 VedAstro 官方快照,失败还不缓存
- 状态:resolved(分支
codex/consult-latency-quickwins-20261005) - 首次发现 / 最近更新:2026-10-04 / 2026-10-05
- 来源:10-04 只读审计。任务书
docs/tasks/TASK-consult-latency-quickwins-20261005.md。写任务书时,在装了 vedastro SDK 的沙箱网络下按产品路径测得每个领域约 4.6 秒,第二轮仍是 4.6 秒。这组秒数不是本轮在这台机器上重测的。 - 影响面:普通对话引擎里校正闸附带的
rectification.vedastro_gateway。顶层前台 VedAstro(BUG-301)不改。直接调用/api/rectification_gate、请求里没有咨询标记时,仍走原来的官方快照。 - 现象:同一份咨询响应里,顶层
vedastro_gateway可以已经是official_verified(BUG-727 的当日缓存),校正闸里的official_closure_reason仍是official_raw_response_missing。校正闸每次再起一个最多 4 秒的子进程。失败结果不进正缓存,同一天每一轮都再等。屏蔽外网时引擎每个领域约 0.75 秒,这 4 秒是额外等待。生产机器是不是也在等,本轮没能在 staging 上确认(进度记录 T0)。 - 根因:咨询工作流把「外部证据可以后做」这个标记只传给顶层前台任务。校正闸仍调用原来的官方快照。出生时间进校正闸时被规范成浮点,和正缓存的键对不上,所以当天已经有的官方结果也复用不到。
- 修复:只在校正闸、且请求带了这个标记时,改走咨询专用路径。先用原始请求里的出生数据、岁差、交点、UTC 日期查正缓存,命中已验证结果就复用;否则查当天的负缓存;都没有就记
official_closure_reason = deferred_in_consultation,不起子进程,也不标成已验证。负缓存单独存放,只认当天的 UTC 日期。没有这个标记的校正请求仍调用原来的 gateway。 - 验证:
tests/test_vedastro_consultation_snapshot.py。咨询路径不调用快照子进程,也不调用原来的 gateway。负缓存同日命中、次日失效,邮箱不进缓存。当天已验证快照优先于负缓存,整数小时和规范后的浮点小时靠原始请求对齐。没有标记的校正面,返回包与现场结果逐项相同。增长合同通过,没有新增类方法。本机没有 vedastro SDK,4.6 秒降到 1 秒以内没有重测。v5 打分文件没有改动,本轮没有重跑 77 例。 - 防复发:上述测试。开关只放在校正闸里,不放进顶层 gateway,避免把 BUG-301 的前台核对也跳掉。
- 相关记录:BUG-727(当日正缓存)、BUG-301(顶层前台照旧,本单不推翻)。
- 复发自:无
- 修复版本:待发布
BUG-1232 | 普通对话看不到第 0/1 步耗时和推理 token,分类耗时进不了用量
- 状态:resolved(分支
codex/consult-latency-quickwins-20261005) - 首次发现 / 最近更新:2026-10-04 / 2026-10-05
- 来源:10-04 只读审计。任务书同上。审计写答题步大约 1 万推理 token、大约 45 秒,但线上记录里没有逐步耗时和推理 token,后面的「限制推理强度」「精简说明」对不上数。这组数字不是本轮实测。
- 影响面:
[agent-observability]日志,以及用量账本里已有的 JSON 字段。不改表,不加迁移。不改提示词和数据卡。 - 现象:已有首字节、工具耗时、答案首输出、整轮耗时、总 token。没有第 0 步和第 1 步各自的耗时,也没有推理、输出、输入、缓存输入 token。分类耗时没有进用量记录。没有「第 1 步开始到第一个正文字」的时间。
- 根因:流式过程没有按步骤记下供应商返回的 usage。用量记录和用量页都没有这些字段。
- 修复:流式过程记下第 0 步和第 1 步的耗时和供应商 usage。供应商没返回的数写空,不估算。
answer.reasoning_ms从第 1 步开始计到第一个非空白正文字;第 0 步的旁白不计。分类耗时同时写入可观测事件和用量记录。步骤数字写入用量记录的modelSteps和answer.reasoning_ms。用量页多一列「分段」。各领域工具耗时保持原字段。正文、提示词、出生资料、用户标识不写入这些字段。 - 验证:
frontend/tests/consult-latency-timing-20261005.test.ts4 项通过。模拟步骤的日志和用量字段齐全,不含植入的正文。流式一轮把计时留在运行状态上,公开回执和响应正文里没有这些字段,也没有那段正文。tsc --noEmit0 错。npm run lint0 error,既有 warning 未动。用量页本轮没有登录打开。 - 防复发:上述测试锁住对话路由、用量列表查询和页面列名。可观测结构是严格对象,多出来的正文字段进不了事件。
- 相关记录:10-04 咨询耗时审计(任务书事故实证第 2 条)。
- 复发自:无
- 修复版本:待发布
BUG-1240 | 秒级 / 纳迪校准从未经过检验
- 状态:closed_by_design(2026-10-05 离线研究:预先登记的 N1、N2、N3、N5 都不过门;换岁差时第 5 层和等分 D150 的变化超过 20%,对外不说秒级)
- 首次发现:2026-10-05
- 最近更新:2026-10-05
- 影响面:生时校正对外精度口径。主链 Vimshottari 只评前三层。第 4 层、第 5 层和 D150 不进入计分。
- 用户现象:校正给出时间范围,不能把结果说成某一秒。公开评价集里 52/77 例的记录分钟是 5 的倍数,记录本身没有秒。
- 触发条件:无用户路径。离线研究,任务书
docs/tasks/TASK-rectification-nadi-seconds-research-20261005.md。 - 根因:在 77 例公开 AA 上,预先登记的规则没有胜过安慰剂。第 5 层在记录分钟上的拟合率 0.221,日期平移安慰剂 0.216,跨例换日期 0.225。六项主检验只有「第 4 层对日期平移」一项达到 p < 0.05,过门要求六项都达到。留出一件的提升区间含 0。六题后的区间里用第 5 层重排,±10 分钟从 25/77 降到 5/77。换一种岁差,第 5 层约 86%–92% 的日事件换主星,等分 D150 为 73/73 例换段。±15 秒已经让约六成日事件换掉第 5 层。
- 修复:不改生产计分,不立实现单。研究记录见
docs/research/rectification_nadi_seconds_2026_10_05.md。 - 验证:
tests/test_nadi_seconds_research.py10 项通过(前三层对账 0 差,77 例 964 件事件)。结果 JSON 由scripts/research/nadi_seconds_run.py写出。预登记提交57480db324f1602cfc422ff8c774d0d6e2113af6在第一次 v5 计分之前。两次复跑的字节核对写在进度记录里。 - 防复发:再提秒级或纳迪校准时,先复跑这份预登记;不得用看过结果之后新加的规则判过门;不得重调 BUG-1091 已关闭的权重。
- 相关记录:BUG-560、BUG-1090、BUG-1091、BUG-1105、BUG-1141。
- 复发自:无
- 修复版本:研究分支
codex/rectification-nadi-seconds-research-20261005,2026-10-05 Claude 验收后合入 staging(只有研究脚本与文档,没有运行时改动)
BUG-1241 | 校正交付后同一轮又补出第二段回答
- 状态:resolved(分支
codex/rectification-delivery-dup-adopt-20261006) - 首次发现 / 最近更新:2026-10-06 / 2026-10-06(验收修复单后)
- 来源:10-06 测试环境真机。任务书
docs/tasks/TASK-rectification-delivery-dup-adopt-20261006.md;验收修复单docs/tasks/TASK-rectification-delivery-dup-adopt-fix-20261006.md。 - 影响面:生时校正答完最后一题后的交付。不改引擎,不改数据库。
- 现象:同一轮出现两段助手回答。前一段是采用旁白的改写。后一段是确定性兜底,带分钟范围和「还剩几个候选」。刷新后两段都在。
- 触发条件:答题路径已经把改写后的交付句写入本轮;收尾补位找不到已问轮次编号,又用子串去比对。改写句不是兜底原文的子串,于是再写一条。
- 根因:BUG-1153 的防重只看「上一条是否包含确定性原文」。当时的测试把上一条写成已经包含这段原文,所以没覆盖「上一条是另一句改写」。60 秒内不重复记账的标记也没有拿来比对这句已经写下的交付。
- 修复:答题回复在落库前记下这句交付原文。收尾补位若发现上一条助手原文(去掉空白后)就是这句,就不再写。子串比对保留。没有任何回复承载这句时,补位仍写 1 条。验收修复(b977f482 之后):首版要求上一条「等于」这句,但答题回复真实落库是「已记录,…」+ 空行 + 改写句(
persistApplied在skippedNextInterview时拼接答题回执),于是事故原样仍补出第二条;改为「上一条包含这句」(去空白比子串,不足 8 字不匹配)。不把旁白函数传进收尾,也不改旁白提示词。选择题 JSON 在已有轮次编号时带上x-rectification-turn-id,作为额外保险;真库验收不依赖这个头。 - 验证:
frontend/tests/rectification-delivery-dup-adopt-20261006.test.ts覆盖「上一条是改写句则 0 条新写入」和「上一条不是这句则仍写 1 条」;修复单补「回执前缀 + 改写」两种回执各 0 条、「只有回执」仍 1 条。frontend/tests/rectification-answer-choice.test.ts「a rewritten delivery reply is not followed by a second exit fill-in turn」走真实答题路径(applyRectificationChoice+ 改写旁白)取实际落库文本再调收尾:b977f482 上 1 条(红),修后 0 条。BUG-1153 原测试仍通过。真 PostgreSQL 17 上,种好的并列卷宗走同一条收尾后,助手交付消息恰好 1 条。本机没有星历引擎,收尾里的刷新失败得很快,失败之后也没有写出第 2 条。 - 防复发:上述测试。跳过条件是「上一条包含已记下的交付句」,比对对象必须取自真实答题路径的落库文本,不得手写只含改写句的夹具(首版回放就是这样漏掉回执前缀的)。
- 相关记录:BUG-596、BUG-1149、BUG-1153。
- 复发自:BUG-1153。旧测试的上一条已经包含确定性原文,改写句走不到那条断言。首版修复的测试与真库回放都只写了改写句、没有答题回执前缀。
- 修复版本:待发布
BUG-1242 | 点采用被拒,因为采用和读取没用答题时的出生日期
- 状态:resolved(分支
codex/rectification-delivery-dup-adopt-20261006) - 首次发现 / 最近更新:2026-10-06 / 2026-10-06
- 来源:同上,同一轮真机。卡片上能点采用,接口返回 409
adoption_not_allowed。 - 影响面:生时校正的采用与案例读取。不放宽采用门,不改成年下限,不改数据库。
- 现象:答题当时判定可以按代表时间采用。点采用时同一份卷宗被判成还在区分候选,
can_adopt为假,返回 409。案例读取和采用走的是同一种缺出生日期的判定,和答题不一致。 - 触发条件:证据指纹已经对上,卷宗里还有一道低于成年下限的区分探针。答题路径带了出生日期,探针被拿掉,结论是可以采用。采用和读取只传指纹,缺出生日期时这道探针仍像能问,结论翻成不可采用。只补「快照仍是当前」不能对齐;出生日期能对齐。过期指纹上强行标成当前也会改结论,生产重算不会对着过期指纹这样做。
- 根因:BUG-598 只把成年下限接到答题和巡检回退。GET 与 accept 仍各自调用判定,不带出生日期。BUG-680 的成对测试没有把出生日期算进「两边必须相同」的入参。
- 修复:出生快照读出校验过的日期,答题、GET、accept 以及其余生产判定调用共用这一份入参。读不到快照就失败关闭:accept 返回 503「暂时无法采用」,GET 仍是「暂时无法读取校正记录」,不退回缺日期的相反结论。快照是否当前仍由卷宗自己算,答题路径不再单独传 true。采用门的集合没有加成员。
- 验证:对拍测试覆盖有无未成年探针、并列与不并列。修前那种只传指纹的调用仍是不可采用;共享入参后答题、采用、GET 都是可以按代表时间采用。源码合同要求生产代码里新增的判定调用旁边出现共享入参函数。真 PostgreSQL 17 上同一份并列卷宗:共享入参
can_adopt为真,GET 与之相同,旧的只传指纹仍为假。 - 防复发:上述对拍与源码合同。
collect_evidence、discriminate_candidates、validate_holdout的公开can_adopt仍必须为假。 - 相关记录:BUG-399、BUG-417、BUG-497、BUG-598、BUG-680。
- 复发自:BUG-598。关联 BUG-680:成对测试没有覆盖出生日期这一个入参。
- 修复版本:待发布
BUG-1243 | 不能采用时卡片仍给出采用按钮,拒绝文案是英文代码
- 状态:resolved(分支
codex/rectification-delivery-dup-adopt-20261006) - 首次发现 / 最近更新:2026-10-06 / 2026-10-06
- 来源:同上。卡片在交付结果上显示采用,点下去被拒,界面带出
adoption_not_allowed。 - 影响面:盘型卡和旧分钟卡。不新增结果字段,不新增第二张卡。
- 现象:交付结果里只要卡片出现,按钮也出现,不看公开的
can_adopt。拒绝时原文把机器代码给到界面。快照不刷新,按钮还在。 - 根因:BUG-681 让交付结果都显示卡片,按钮却没有使用和采用接口相同的判据(采用集合且
can_adopt为真)。客户端把 409 的代码直接显示出来。 - 修复:盘型卡和分钟卡都只在采用集合且
can_adopt为真时显示按钮和「采用后按…排盘」。不能采用时卡片仍在,没有按钮,也没有那一行。已采用仍显示「已采用」。409 的error改为「这次的结果还不能采用,已刷新到最新状态」,code仍是adoption_not_allowed。客户端先重拉快照,再显示这句中文。 - 验证:组件测试覆盖交付但不可采用、可以采用、分钟卡三种。渲染结果里没有
adoption_not_allowed。409 的假请求会重拉快照,错误文案是那句中文。本轮没有在浏览器或手机上点过按钮。 - 防复发:上述组件测试和 409 测试。
frontend/DESIGN.md校正状态表写明不能采用时卡片仍在、没有按钮。 - 相关记录:BUG-497、BUG-681。
- 复发自:无。BUG-681 要求卡片出现,没有要求按钮和公开
can_adopt一致;BUG-497 禁止把不可采用的结果放进采用集合,本轮没有放宽。
BUG-1244 | 普通对话首轮按固定小标题汇报,读起来不像聊天
- 状态:fixed-pending-verify(分支
codex/consult-conversational-answer-20261006;部署和真机清单完成前不标 resolved) - 首次发现 / 最近更新:2026-10-06 / 2026-10-06
- 来源:产品看普通对话首轮,反馈像机器在汇报。任务书
docs/tasks/TASK-consult-conversational-answer-20261006.md。 - 影响面:普通咨询本命 / 无出生分钟 / 申报时段的首轮写作指令、续写提示、思考计划里的「这周可以做什么」、没有标题时的写作行。不改证据卡、工具、模型、输出长度上限、答题时钟、计费。每日星语和「深入看今日」的骨架不动。生时校正的口气不动。
- 现象:首轮按「开场 → 按对象的小标题 → 依据节 → 时间节 → 行动节」往下写。行动清单放在固定的最后一节。问到几个人时,段首是二级标题。申报时段还会要求先写「先回答你的问题」这个标题。
- 根因:2026-10-01 的形状(BUG-1132)把「按对象分段」做成了二级标题,并保留三个固定节。2026-09-27 的「行动只在最后一节出现一次」(BUG-1073 的标题部分)仍要求交那一节。模型按标题填空,读起来是汇报。
- 修复:形状只在
consultation-thinking-plan.ts定义一处。首轮不写二级标题;问到几个人就分段落,段首点名。行动不强制,盘上有具体指向才顺口一句,用户问怎么办时那一轮可以给几条。首轮通常三到六段,不设硬截断。追问仍讲清楚为准;换领域或要求完整看时回到同样的段落形状。思考计划去掉行动节。正文没有二级标题时,思考节在流式中不保持进行中,结算后一起完成。写作行走「组织回答」。历史消息里已经写出的标题仍能拆开。REPORT_HEADING只留在拆旧消息和思考节的 heading 字段里,不再拼进提示词。 - 验证:合同测试见进度记录。本机没有模型凭据,四道离线题没有真跑。真机八步见
docs/testing/consult-conversational-answer-20261006.md。staging 尚未部署这次改动。 验收复核(Claude,Linux + Node 22.14,2026-10-06):首版28113fa0全量失败名单比基线86cafe68多一条chat-answer-detail合同测试(锁「Every H2 must start its own line」),执行方在 Windows 上没跑到;修复后全量 4941 项 fail 24,名单与基线逐条一致(基线另一条 70 s 计时测试是并发抖动,单独跑 3/3 通过);next build后/仍 Static,首屏 gzip 567,642 → 567,007 B(-0.1%)。 - 防复发:
consultation-thinking-plan.test.ts、consultation-voice-contract.test.ts、consultation-run-timeline.test.ts锁住「不写 ## 节名」「段首点名」「行动不强制」「三到六段」,以及无标题首轮的思考节与写作行。改断言写了原值 / 新值 / 原因。 验收补修:chat-answer-detail.test.ts改为不再要求 H2 那句;consultation-voice-contract.test.ts新增「示范段落空一行、指令写明段落之间空一行」——聊天正文.message p是 pre-wrap,单个换行只折行、没有段间距,去掉标题后几段话会挤成一块。 - 相关记录:BUG-1073(同一件事只说一遍,保留)、BUG-1132(10-01 的按对象标题与三个固定节,本记录推翻其标题部分)。
- 复发自:无。这是产品改口径,不是 1073 / 1132 的同一缺陷复发。
- 修复版本:staging
e62d0a32(含da226db1;2026-10-06 部署核对:health gitCommit=e62d0a32,/api/account401,/login200;da226db1自身门禁 run 1742 测试步失败,日志需 token 未读到,后续 run 1743 同代码通过)。真机清单未走,状态保持 fixed-pending-verify
BUG-1245 | 提示词里的行动示范被回答逐字照抄
- 状态:fixed-pending-verify(分支
codex/consult-conversational-answer-20261006;部署和真机清单完成前不标 resolved) - 首次发现 / 最近更新:2026-10-06 / 2026-10-06
- 来源:同上任务书。产品指出示范里的具体做法会出现在真实回答里。
- 影响面:
product-voice.ts的 Good 示范。不改计算,不改证据卡。 - 现象:不同人的回答里出现同一套可以照抄的生活建议,例如书面确认、这周去看小毛病、给妈妈打电话先问近况,以及「这周」行动节的标题。
- 根因:Good 示范把具体行动句和固定标题写进了提示词。模型把示范当答案抄。
- 修复:父母题和事业题的 Good 示范改成段落,段首点名,括号里只留依据占位。删掉全部具体行动句。另加一条只示意结构、不给可抄句子的汇报体 Bad。提示词里不再出现那三句旧行动,也不再出现「## 这周」。
- 验证:
consultation-voice-contract.test.ts的「pinned examples carry no copyable action and no weekly heading (BUG-1245)」。真机第 3 步换人再问,看建议是否还是同一模子。本机没有模型凭据,没有对照跑新旧提示词。 - 防复发:上述测试锁住三句旧行动和「## 这周」不得出现在提示词里。
- 相关记录:BUG-1165、BUG-1168、BUG-1182(示范被照抄的前几轮)、BUG-1244。
- 复发自:BUG-1165 的「示范被当成规则照抄」。旧测试锁的是读法和职业形态,没有锁行动句,所以这次换了一类句子仍能抄出去。
- 修复版本:staging
e62d0a32(含da226db1;2026-10-06 部署核对:health gitCommit=e62d0a32,/api/account401,/login200;da226db1自身门禁 run 1742 测试步失败,日志需 token 未读到,后续 run 1743 同代码通过)。真机清单未走,状态保持 fixed-pending-verify
BUG-1252 | iPhone 上回答右侧一条空白,输出时正文抖动
- 状态:fixed-pending-verify(分支
codex/consult-readable-20261006;真机确认前不标 resolved) - 首次发现 / 最近更新:2026-10-06 / 2026-10-06
- 来源:产品 iPhone 真机截图(staging,普通对话婚恋题)。
- 影响面:所有对话里的助手回答正文(普通咨询、生时校正旁白);首次引导气泡另有规则,未动。
- 现象:每行在约 19–20 个汉字处断开、连词也拆开,右侧留出固定空白;流式输出时已显示的行跟着换行位置变化,看起来在跳。
- 触发条件:iOS 上任何浏览器(均为 WebKit),回答段落较长时更明显(10-06 改为大段落后加重)。
- 根因:
globals.css的.message p, .message-markdown带text-wrap: pretty(2026-07 起)。WebKit 对整段做优化,把各行调成接近等长,代价是每行短于可用宽度;每追加一个字就重算整段折行。Chrome 只调最后几行,所以桌面不明显。用本机 Chrome 按手机宽度渲染真实样式确认容器占满整行(排除宽度上限);WebKit 测试浏览器缺系统库未能起,未在 WebKit 内亲眼复现。 - 修复:答案正文去掉
text-wrap: pretty与word-break: auto-phrase(后者 WebKit 不支持),普通折行。 - 验证:
tests/consult-plain-speech-20261006.test.ts锁定该规则不含text-wrap,且其他回答正文选择器不再加回pretty。全量 4966 项 fail 24,与基线 d2c22ca2(4962 / 24)逐条一致;build 后/Static,首屏 gzip 567,175 → 567,175 B。 - 防复发:同上测试;
frontend/DESIGN.md字体段写明答案正文例外。 - 相关记录:BUG-1244(首轮改大段落,放大了此现象)。
- 修复版本:staging
52d14e8f(含171a3f25;2026-10-06 23:49 部署核对:health gitCommit=52d14e8f、各项 ok,/api/account401,/login200)。真机未走,状态保持 fixed-pending-verify
BUG-1253 | 情感等回答把内部分析清单原样念给用户,看不懂
- 状态:fixed-pending-verify(分支
codex/consult-readable-20261006;无模型凭据,真机复核前不标 resolved) - 首次发现 / 最近更新:2026-10-06 / 2026-10-06
- 来源:同 BUG-1252 截图。
- 影响面:普通咨询各领域回答的措辞;工具结果里的精简清单、通用读法;首轮与追问轮指令。不改证据卡、工具、打分。
- 现象:「先说心动这一层」「再看成对居住的那一层」「宫主火星落在从它数起的第 6 位」「这条只说这颗星自己有劲,不说明事情就顺」「合力星」;括号里一次列四条信号。
- 根因:①婚恋精简清单写「分三层说:①心动接触 ②关系成对 ③社会法律落地」,是给模型分步判断的,模型把层名当成说法;②通用读法的受冲条件「宫主落在从该宫数起的第 6、8、12 宫」、规则句「旺弱只说明这颗星自己有没有力气」被搬进正文;③证据卡字段
yogakarakas只有英文名,名词规则又禁音译,模型自造「合力星」;④AFFLICTION_RANGE_RULE要求「括号里写是哪几条」,与「每段最多一处括号、一两条依据」冲突,模型选了列全。清单比「人话自检」更具体,压过了它。 - 修复:
consultation-thinking-plan.ts新增CHECKLIST_ANALYSIS_ONLY_NOTE(清单开头一行:分析用,不写进回答)与PLAIN_SPEECH_RULE(清单只用来想;括号最多两条;没有日常叫法的字段白话对照,不自造译名),进首轮、追问、ANSWER SHAPE 与英文形状摘要;婚恋三层改为「遇到喜欢的人、谈恋爱 / 定下来、长期在一起 / 结婚、领证」并写「不报层名和编号」,事业、财运、健康同样注明;受冲规则括号改为「只写最能说明问题的一两条,用白话,不列全」。 - 验证:新增
consult-plain-speech-20261006.test.ts4 条;改断言 4 处(三栏写在测试里)。无模型凭据,没有真跑新提示词,真机复核见docs/testing/consult-readable-20261006.md。 - 防复发:测试锁清单不含旧层名(心动接触 / 关系成对 / 社会法律落地 / 分三层说 / 压力窗 / 恢复窗)、每份清单以用途说明开头、白话对照与括号上限存在。
- 相关记录:BUG-1148(名词只用中文;本次补上没有中文叫法时怎么说)、BUG-1244、BUG-1160~1163(通用读法与精简清单)。
- 修复版本:staging
52d14e8f(含171a3f25;部署核对同 BUG-1252)。真机未走,状态保持 fixed-pending-verify
BUG-1254 | staging 门禁偶发红:数据库测试排队等 PostgreSQL 槽位超时
- 状态:fixed-pending-verify(分支
codex/postgres-fixture-queue-20261006;需在门禁上连续通过才标 resolved) - 首次发现 / 最近更新:2026-10-06 / 2026-10-06
- 来源:门禁 run 1746(
171a3f25)「Frontend and database tests」红;同日 run 1742(da226db1)同一步红,随后同代码的 run 1743(e62d0a32)通过。 - 影响面:Gitea
backend-quality-gate.yml的npm test --prefix frontend;frontend/tests/helpers/postgres-fixture.ts。与被测提交的改动无关时也会红,阻断部署。 - 现象:
not ok - corrective migration clears untrusted clocks and serializes concurrent fourth/fifth people,原文timed out waiting for a PostgreSQL fixture slot (2 concurrent compose networks). Docker address pools cannot host one network per parallel test file.,ERR_TEST_FAILURE。 - 触发条件:全量并行
npm test时,使用startPostgresFixture()的测试文件(2026-10-06 共 46 个)同时排队抢 2 个槽位,每个文件最多等FIXTURE_SLOT_WAIT_MS= 5 分钟;排在后面的文件等满 5 分钟即失败,谁失败取决于调度,所以同一代码时红时绿。 - 根因:BUG-281 为防 Docker 地址池耗尽把并发限到 2 个槽位,但等待上限是固定 5 分钟;数据库测试文件从当时约 20 个增长到 46 个,排队总时长已超过 5 分钟。属于 BUG-281 修复的容量假设失效,不是新代码引入。
- 修复:产品要求根治(2026-10-06)。
postgres-fixture.ts的等待从「从开始排队起固定 5 分钟」改为「按进展续期」:每次轮询记录各槽位的持有者(slot:pid),持有者有变化(有人释放或接手)就把截止时间推到现在 +FIXTURE_SLOT_STALL_MS(10 分钟);只有连续 10 分钟没有任何槽位易手(持有者疑似卡死)才失败,报错写明这一点。槽位上限 2 不变(BUG-281 的地址池保护不动);不改.gitea/workflows/**。曾考虑把数据库测试拆成单独串行一轮,未采用:改动面大、门禁更慢,且排队本身已由槽位串行化。 - 验证:本机无可用 Docker,未复现;依据为门禁日志原文与
postgres-fixture.ts的槽位 / 等待常量。postgres-fixture-contract.test.ts新增 1 条(续期纯函数三种情形 + 取槽循环每次轮询重算截止时间),4/4 通过;全量 4967 项 fail 24,与171a3f25名单逐条一致;tsc 0、lint 0 error。门禁上的实际效果待推送后看。 - 防复发:等待不再与 fixture 文件数量挂钩;契约测试禁止再出现固定等待常量
FIXTURE_SLOT_WAIT_MS。若门禁总时长逼近 validate 的 45 分钟上限,再考虑提高槽位数(先查地址池余量)。 - 相关记录:BUG-281(槽位上限的来源)、BUG-266(runner 资源回收)。
- 复发自:BUG-281(同一机制的容量问题以另一种形式出现)。
- 修复版本:staging
52d14e8f(修复后门禁 run 1747、1750、1753、1756、1759、1762 连续六次一次通过;再观察后标 resolved)
BUG-1255 | 回答口吻与首页不一致、读心一刀切禁掉;人设规则互相打架
- 状态:fixed-pending-verify(分支
codex/consult-voice-sister-20261007;无模型凭据,真机复核前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品审阅当前回答规则清单后的决定(非线上报错)。
- 影响面:普通咨询三种模式共用的口吻(
product-voice.ts的productConversationVoice)、本命合同的 Never 列表、追问指令、mastra/index.ts末尾一句。校正 Agent 口吻、首页开场语不动。 - 现象:回答人设是「嘴有点毒但靠谱的同事、带一点锋利」,首页开场语 10-01 已改成知心姐姐口吻,同一产品两种性格;「不揣测」禁止任何替用户或当事人陈述感受的句子,产品要读心;「希望纪律:每次都要说清能做什么」与「行动不强制」冲突;英文 Five principles 里「restate the same constraint in different words」与「同一件事只说一遍」冲突、「say progress when the numbers are on the table」是校正场景遗留;「Answer naturally and concisely」与「讲清楚为准、首轮三到六段」冲突。
- 根因:口吻规则分几轮叠加(09-17 同事人设、09-27 不揣测、10-01 开场语、10-06 形状),旧句子没有随新决定删掉。
- 决策(产品 2026-10-07):口吻定知心姐姐;读心两种都要(用户的心 + 当事人的心)。推翻 BUG-1070(09-27)「不揣测」中禁止陈述感受的部分与 10-01「问别人时不写那个人的心思」;保留 BUG-1070 的「是非题先答」「不改写成需求题」,保留 BUG-1182 受冲口径。
- 修复:人设改知心姐姐(不顺照实说但说法顾着对方、不说教不催、一句接住的话、温柔不靠语气词);「不揣测」改为「读心(可以读,守三条)」:先答再读、可能/多半一句不当事实(用户全篇最多一句)、当事人要有盘上依据每人最多一句且只在未受冲时写暖的一面,受冲只给范围;希望纪律改为「不顺的时候说清这段时间可以怎么过」;英文 Five principles 删冲突两条,余下译成中文「说话的方式」;「Answer naturally and concisely」改为「讲清楚为准,首轮通常三到六段」;示范的是非题 Good 加读心两句,追问 Good 去掉「说直接点」。
- 验证:改断言 4 个文件(三栏写在测试里);新增
consult-voice-sister-20261007.test.ts2 条。全量 4969 项 fail 24,与基线52d14e8f(4967 / 24)逐条一致;tsc 0、lint 0 error。无模型凭据,读心是否过度未真跑,见docs/testing/consult-voice-sister-20261007.md。 - 防复发:测试锁人设关键句、读心三条、禁用断定句、受冲只给范围,且
index.ts不再有「concisely」、voice 不再有「restate / 嘴有点毒 / 锋利」。 - 相关记录:BUG-1070(被部分推翻)、BUG-1182(受冲口径保留)、BUG-1244(行动不强制)、BUG-1253。
- 修复版本:staging
b8385adb(2026-10-07 08:13 部署核对:health gitCommit=b8385adb、各项 ok,/api/account401,/login200;门禁 run 1750 validate 15 分钟一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1256 | 普通对话系统提示 5.5 万字符:同一形状说 4 遍、方法书带进工程记录与冲突的输出合同
- 状态:fixed-pending-verify(分支
codex/prompt-slim-20261007;部署与真机前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品问「提示词规则 2 万多字能不能优化」,Claude 实测体量后产品授权直接执行,并提供临时模型 key 做改前改后对比。
- 影响面:本命对话与申报时段对话的系统提示(
mastra/index.ts、product-voice.ts合同、consultation-thinking-plan.ts的 natal 标题规则、skill-binding.ts的方法块)。不改证据卡、工具、领域清单、用户轮指令、模型与时钟。 - 现象:本命系统提示 55,242 字符(约 2.2 万 token),每轮实际输入约 2.46 万 token。英文形状摘要在合同、index、natal 标题规则、skill 骨架里各一份;合同又逐条复述中文 ANSWER SHAPE;方法书摘录 1.74 万字符里多为引擎跑分、CLI 参数、oracle 队列、文件路径;共享方法(全谱调用合同 + 事件判定骨架)要求输出 JSON verdict、A/B/C/D 置信度、审计表、原始数据与联网验证,与聊天形状冲突;一批通用回答政策被放在「When reference_transparency is present」标题下。
- 根因:形状与方法块分轮叠加,每轮只加不删;方法块按 SKILL.md 的整节标题摘取,SKILL.md 主要是引擎与报告手册。
- 修复:第一步(去重、不改意思):形状摘要只留合同一份,删合同里复述中文形状的条目,通用政策挪到「Always」标题下。第二步:聊天方法块只摘 SKILL.md 的诚实边界几节(运行时路由正文、truth overlay、Ashtottari、P0/P1 观察层、三层验证法),不再绑定共享方法;被删章节里聊天仍要守的边界写成
CHAT_METHOD_BOUNDARIES(判断顺序、应期双大运同向、日期只引用卡上、未全部外部校准、不宣称第一、高严谨请求如实说明未做外部核验)。SKILL.md 与共享方法文件不动,报告与 skill_read 照用。 - 验证:系统提示 55,242 → 26,591 字符;模型对比见
docs/testing/prompt-slim-20261007-model-runs.md(flash 10 题平均输入 24,560 → 14,025 token、耗时 37.8 → 30.3 s、思考 6,975 → 5,683 token,自动检查与人工通读无退化;高严谨请求如实说明未做外部核验)。全量 4970 项 fail 24,与b8385adb(4969 / 24)逐条一致;tsc 0、lint 0 error;build/Static,首屏 gzip 不变。改断言 2 个文件(三栏写在测试里),新增 2 条。 - 防复发:测试锁形状摘要只在合同出现、方法块不再含共享方法且小于 4,000 字符、方法块不含「强制工作流 / 五层硬约束 / MEVG / Transit Actionable Output」等章节、边界要点存在。
- 相关记录:BUG-1253(清单原句被念给用户,同属「给模型分析用的文字进了说话层」)、BUG-1255。
- 修复版本:staging
c76fdda7(2026-10-07 09:08 部署核对:health gitCommit=c76fdda7、各项 ok,/api/account401,/login200;门禁 run 1753 一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1257 | 生时校正口吻与普通对话不一致;开场 / 只读轮整本 Skill 进模型
- 状态:fixed-pending-verify(分支
codex/rectification-voice-slim-20261007;部署与真机前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品要求把生时校正也按普通对话的方式优化、口吻统一由 Claude 直接做。
- 影响面:
mastra/agentic-rectification.ts两版系统提示的开头;v9/skill-slice.ts的按轮次切片。不改校正打分、出卡、采用门、工具、服务器写的固定文案、Skill 文件。 - 现象:普通对话口吻已改为知心姐姐(BUG-1255),校正仍是「懂行、可靠、说人话」;两版系统提示各写一份相同的身份与口吻。开场轮、只读轮把整本 Skill(10.0.33 为 11,812 字符)发给模型,含长会话摘要字段说明(只在 full_diagnostics 里有)、上游同步、触发条件等本轮用不上的章节。
- 根因:证据轮切片(TASK-rectification-grounding-20260927 T6)只覆盖了证据轮;口吻随普通对话改动时校正侧未同步。
- 修复:身份与口吻提为共用的
RECTIFICATION_VOICE_HEAD(知心姐姐;不催、不评判记不清,「记不清也没关系」最多一句;不说教、无语气词;校正不读心)。开场轮只附「必须先读、OpeningPolicy、ConversationFocus、可调用工具」,只读轮只附「必须先读、Case 状态、可调用工具、候选语言、输出与停止」,两者有「持久产品身份」章节时一并附上(可选章节缺失不迫使发全文);必需章节缺失的旧版(9.0.0)照旧整本发送(BUG-621)。按分钟 / 按盘型两版规则正文不动。 - 验证:全部 35 个历史 Skill 版本切片检查:10.0.x 全部按章节切,9.0.0 整本;10.0.33 开场 11,812 → 4,576、只读 → 6,571 字符。DeepSeek 模拟(deepseek-flash,手工构造的工具结果,不入库):开场轮输入 7,433 → 3,892 token;证据轮基本不变;「记不清」场景两版都温和、无语气词;开场重复题干例子改前 1/7、改后 1/7,判为模型随机性。全量 4970 项 fail 24,与
c76fdda7逐条一致;tsc 0、lint 0 error;build/Static,首屏 gzip 不变。改断言 2 个文件(三栏写在测试里)。 - 防复发:测试锁两版共用口吻头、开场 / 只读切片章节与长度、旧版整本发送。
- 相关记录:BUG-1255(普通对话口吻)、BUG-1256(普通对话提示词精简)、BUG-621(历史版本须可打开)。
- 未做:每轮强制第一步
rectification-read-case(一次额外的模型调用)——改流程,待产品给真实步耗时数据后另定;服务器写的固定文案(如「已记录。这题先不计分,换一件事问。」)口吻未改。 - 修复版本:staging
61d6f248(2026-10-07 部署核对:health gitCommit=61d6f248;门禁 run 1756 一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1258 | 生时校正服务器固定句口气生硬(「已记录。」回执、「请……」、内部词)
- 状态:fixed-pending-verify(分支
codex/rectification-copy-voice-20261007;真机前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:BUG-1257 后产品要求把服务器写死的提示语也改成知心姐姐口吻;Claude 出 15 句样稿,产品认可后全量改。
- 影响面:
user-copy.ts、v9/choice-action.ts、v9/post-adopt-verdict.ts、v9/adopt-narration.ts(及其旁白提示词里引用的同一句)、v9/run-diagnostic.ts的重复文案、rectification-chat-choice-run.ts兜底、rectification-agentic-chat.tsx两句输入框提示。不改系统报错、写给模型的规则、题库与盘型说明、数字与诚实边界。 - 现象:选择题回执「已记录。这题先不计分,换一件事问。」「已记录你的选择。这是盘外核对,不会改候选分数。」等系统回执腔;「请再说一次」「请先到资料里改」;「候选」「交付代表性工作时间」等内部词;采用后叫人「新建对话」。
- 根因:固定句分轮加入,没有统一口吻规范;Agent 口吻改了,服务器句没跟上。
- 修复:约 30 处按 VOICE.md「生时校正服务器固定句」规则改写(收到 / 好、麻烦、时间、不提新建对话)。防重复相关:
turn-narration.ts的CHOICE_ACK_RE加入新回执说法(旧说法保留),模型重复服务器回执仍被丢弃;delivery-turn-guard.ts是按交付句包含判断,不依赖回执开头,只改注释。「这次没有重新比较」开头保留(被检测)。 - 验证:改断言 12 个测试文件约 35 处(每处三栏注释);新增
rectification-copy-voice-20261007.test.ts3 条(回执不以「已记录」开头、无「新建对话 / 请再说」、模型重复新回执被丢)。全量 4973 项 fail 24,与61d6f248逐条一致;tsc 0、lint 0 error;build/Static,首屏 gzip +26 B。 - 防复发:上述新测试;VOICE.md 新增本节规则与对照表。
- 相关记录:BUG-1257、BUG-1153 / BUG-1241(回执与交付句防重复,确认未受影响)、TASK-rectification-in-chat-step1-20261006 C8(同样不再叫人新建对话)。
- 修复版本:staging
6ec8efd5(2026-10-07 部署核对:health gitCommit=6ec8efd5、各项 ok,/api/account401,/login200;门禁 run 1759 一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1247 | 首页生时校正按钮与文字口令:入口在对话之外、对话里只靠固定口令跳转
- 状态:fixed-pending-verify(分支
codex/rectification-in-chat-step1-20261006;部署与真机清单完成前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品决定生时校正并入对话(任务书
docs/tasks/TASK-rectification-in-chat-step1-20261006.md,P1–P3、C1–C8),产品要求直接执行。 - 现象:首页有独立「生时校正」pill;普通对话里只有打出「生时校正 / 先完成生时校正」这类原话才会新开一条校正会话,原问题只剩一行展示。
- 根因:入口按产品面划分,不按对话需要;对话侧用前端正则
isRectificationHandoffQuestion判断意图(违反禁正则判意图口径)。 - 修复:删首页 pill、
rectificationEntryHint、rectificationEntryLabels/resolveRectificationEntryAction;删isRectificationHandoffQuestion及发送路径里的交接分支与pendingConsultationQuestion展示。历史里的校正会话照常打开,只读历史里的「再次校正」保留。 - 验证 / 防复发:见 BUG-1248 汇总;断言更新 14 个测试文件(三栏注释)与 Python
test_daily_and_rectification_entrypoints.py。 - 相关记录:BUG-976/977(禁正则判意图)、BUG-1115(交接前保存来源会话,已无此路径)。
- 修复版本:staging
11333460(含6ad0bea3;2026-10-07 部署核对:health gitCommit=11333460、各项 ok,/api/account401,/login200;门禁 run 1762 一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1248 | 对话里没有「推得不准」时的校正提议
- 状态:fixed-pending-verify(分支
codex/rectification-in-chat-step1-20261006;部署与真机清单完成前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品决定生时校正并入对话(任务书
docs/tasks/TASK-rectification-in-chat-step1-20261006.md,P1–P3、C1–C8),产品要求直接执行。 - 现象:用户在普通对话里说过去的事对不上、出生时间是估的,agent 没有任何校正相关的指令或入口。
- 修复:
RECTIFICATION_OFFER_RULE(consultation-thinking-plan.ts,只定义一处)进首轮、追问与申报时段指令:先分读法 / 时间问题,时间问题才在写回答前调用offer-birth-time-rectification;同会话提议过且用户没接不再主动提(userAsked=true例外)。工具只记录本轮提议,能否出卡由服务端decideRectificationOffer决定(本人、isProductEnabled("rectification")、无进行中 case、feature_pricing价格);提议随回答一起经complete_consultation_response存进消息,并在run.completed事件里带给浏览器。卡片RectificationOfferCard挂在该回答下。 - 验证:新增
rectification-in-chat-20261007.test.ts14 条(服务端决策 5 种结果、工具一轮一张卡与会话内不重复提议、事件带提议、规则进三种指令、卡片 / 分隔线 / 小字渲染、浏览器回读保留字段等)。DeepSeek(deepseek-flash)模拟 4 场景各 2 次:只是不认同解释 2/2 不调用;过去的事对不上 + 时间是估的 2/2 调用;家人档案 2/2 调用后被拒、一句话说明;已提议又被拒 2/2 不再推;成功后正文提到卡片 3/3(工具说明加强后复测)。全量见进度记录。 - 修复版本:staging
11333460(含6ad0bea3;2026-10-07 部署核对:health gitCommit=11333460、各项 ok,/api/account401,/login200;门禁 run 1762 一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1249 | 校正不知道是从哪条对话开始的
- 状态:fixed-pending-verify(分支
codex/rectification-in-chat-step1-20261006;部署与真机清单完成前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品决定生时校正并入对话(任务书
docs/tasks/TASK-rectification-in-chat-step1-20261006.md,P1–P3、C1–C8),产品要求直接执行。 - 修复:打开接口在
intent=homepage且带sourceSessionId时,把该会话里最新一张offered提议卡改为opened并写入caseId/rectificationSessionId(调用者自己的客户端,messages为本人可更新列;best effort)。新GET /api/rectification/cases/[caseId]/source在本人最近 20 条咨询会话里找绑定该 case 的提议卡,刷新后仍能找回来源。不改表。 - 验证:同上测试文件(绑定一次、幂等、无提议不绑定、来源查找)。真实库读写未在本机跑(无 Docker):只用了已有查询写法(eq / order / limit / maybeSingle / update),见进度记录。
- 修复版本:staging
11333460(含6ad0bea3;2026-10-07 部署核对:health gitCommit=11333460、各项 ok,/api/account401,/login200;门禁 run 1762 一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1250 | 校正做完让用户「新建对话」,原问题和上下文丢失
- 状态:fixed-pending-verify(分支
codex/rectification-in-chat-step1-20261006;部署与真机清单完成前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品决定生时校正并入对话(任务书
docs/tasks/TASK-rectification-in-chat-step1-20261006.md,P1–P3、C1–C8),产品要求直接执行。 - 修复:
POST /api/rectification/cases/[caseId]/source由服务端读 case 的accepted_time判断是否改了时间,在来源对话末尾追加一条分隔消息(rectificationReturn,每个 case 一次)并把提议卡标为returned;浏览器重读该会话并切过去。校正面「回到刚才的对话」按钮(仅有来源时);采用后核对结束(verified_idle)或 case 终态时,在本视图做过操作的校正 2.5 s 后自动回去(useRectificationAutoReturn),历史打开的已结束 case 不自动跳。分隔线下「按新时间重新看」走onFollowUp,点击才发送。分隔消息不显示操作行、不参与「重新生成」。 - 验证:同上测试文件(追加一次、改 / 未改两种文案、标记 returned、不点不发、自动返回只跟本视图操作)。浏览器级与真实库未验,见真机清单。
- 修复版本:staging
11333460(含6ad0bea3;2026-10-07 部署核对:health gitCommit=11333460、各项 ok,/api/account401,/login200;门禁 run 1762 一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1251 | 改了出生时间后,模型仍读得到旧时间下的回答
- 状态:fixed-pending-verify(分支
codex/rectification-in-chat-step1-20261006;部署与真机清单完成前不标 resolved) - 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品决定生时校正并入对话(任务书
docs/tasks/TASK-rectification-in-chat-step1-20261006.md,P1–P3、C1–C8),产品要求直接执行。 - 修复:
storedConsultationTurns跳过分隔消息;最近一次改时间的分隔线之前的助手回答,在模型历史里前缀「(以下回答按校正前的出生时间,不要沿用其中的盘面判断)」;会话摘要早于改时间时同样加这句。 - 验证:同上测试文件(改了 / 没改两种情况、摘要)。
- 修复版本:staging
11333460(含6ad0bea3;2026-10-07 部署核对:health gitCommit=11333460、各项 ok,/api/account401,/login200;门禁 run 1762 一次通过)。真机未走,状态保持 fixed-pending-verify
BUG-1259 | 新对话里还没发第一句就换模型,提示「云端同步失败」
- 状态:fixed-pending-verify(真机复核前不标 resolved)
- 首次发现 / 最近更新:2026-10-07 / 2026-10-07
- 来源:产品 iPhone 真机截图(staging 首页,新对话未发消息时切到 gpt-5.6-luna)。
- 影响面:
use-session-management.ts的selectSessionModel→patchSessionModel→PATCH /api/sessions/{id};首页 / 新建的空对话。 - 现象:选择模型后顶部报「已在当前页面选择 gpt-5.6-luna,但云端同步失败;再次选择当前模型即可重试。」再选一次仍失败。
- 触发条件:会话还没发过第一句(本地未落库的空咨询)时切换模型。
- 根因:BUG-989(2026-09-21)把「新建对话」改成只在本地创建、第一问时才
POST /api/sessions落库;selectSessionModel没跟着改,仍对这个还不存在于服务器的会话发PATCH,路由查不到行返回 404「聊天记录不存在或已被删除」,前端包装成「云端同步失败」。本地选择本身已生效(第一问落库时model_id取自本地会话),报错是多余的,但会让用户以为没切成功。 - 复发自:无;BUG-989 漏改的调用点。BUG-989 的源码合同只锁了「新建无 POST / send 时 create」,没覆盖换模型。
- 修复:
selectSessionModel在本地更新后,若会话仍是未落库的空咨询(与发送路径同一判据isUnsavedEmptyConsultation且不在cloudCreatedIds),清掉该会话的同步失败标记后直接返回,不发PATCH。第一问落库的 create 本来就写model_id: session.modelId,所选模型随落库生效。已落库会话照旧同步。启动 / 回访时的模型下线回退(applyWarmCatalog)早已跳过未落库会话,未改。 - 验证:
session-list-filter.test.ts新增 1 条源码合同(守卫在任何 PATCH 之前、清失败标记并 return、create 写入model_id);去掉修复后该测试失败、恢复后通过。全量 4988 项 fail 24,与11333460逐条一致;tsc 0、lint 0 error;build/Static,首屏 gzip 568,343 → 568,362 B。 - 防复发:上述测试;以后凡是对会话发 PATCH 的入口,先判断会话是否已落库(BUG-989 的「第一问才落库」)。
- 相关记录:BUG-989、BUG-928。
- 修复版本:待发布