Files
Jyotisha/docs/tasks/TASK-chart-profile-update-consistency-20260922.md
T
2026-09-22 11:19:35 +08:00

9.5 KiB
Raw Blame History

任务书 · 更新出生资料后星盘真值一致性与可诊断错误(2026-09-22)

0. 基线与串行关系

  • 基线 commit1bc6a954c597e2817fe72bfedad93119ab19a527(当前 origin/staging)。执行方开工前必须重新 git fetch origin --prune,再以最新 origin/staging 为基线。
  • 执行分支:codex/chart-profile-update-consistency-20260922;工作树:.worktrees/chart-profile-update-consistency-20260922
  • 本单是星盘现场测试暴露的功能修复,不是整页重设计。与既有 TASK-chart-page-blocking-open-20260915.mdTASK-chart-vedastro-decouple-20260915.mdTASK-readonly-pages-fix-20260916.md 有交集时必须串行:先核对这些分支是否已合入 staging;未合入时不得同时改同一符号,按“既有星盘打开链路 → 本单资料真值一致性”顺序执行。
  • 本单不接入 chart_profiles 作为 /chart 的主资料来源;/chart 继续使用账户权威 profiles。人物资料库属于普通聊天的另一条 subject binding 任务。
  • 与既有星盘打开链路任务严格串行:先完成并核对 TASK-chart-page-blocking-open-20260915.mdTASK-chart-vedastro-decouple-20260915.mdTASK-readonly-pages-fix-20260916.md 的 staging 状态,再处理本单真值/缓存;不得并行修改相同 chart-view loader、engine client 或错误映射符号。

1. 事故实证

会议现场反馈:用户已经更新出生时间,但打开星盘仍提示没有可显示内容,或点击后无法打开。按产品设计,填写出生资料后应能生成并查看星盘。

调查已确认的代码事实(按符号定位,不依赖脆弱行号):

  • frontend/src/app/(app)/chart/page.tsx 只挂载客户端 chart 壳;frontend/src/hooks/use-chart-page.ts 通过 refreshChartPage() 请求 /api/chart-view
  • frontend/src/lib/chart-view-client.ts 对响应做 JSON/Zod 合同解析;解析失败或请求失败会归并为通用 chart_unavailable
  • frontend/src/lib/secondary-page-data.tschartCache 是模块级单值缓存,没有账户/profile 版本键;资料 PATCH 成功后没有明确的 chart snapshot 失效边界。
  • frontend/src/lib/chart-view-service.tsprofiles 读取 ACCOUNT_BIRTH_SELECT,再经 server-owned-birth-profile.ts 与 timezone resolver 组装排盘资料。
  • accepted/confirmed 资料存在 active_birth_date 但缺 active_birth_timezone_offset 时,server-owned-birth-profile / birth-profile-timezone 会抛出 adopted_birth_calculation_incomplete 或 timezone error;当前错误路径可能最终只显示通用不可用状态。
  • frontend/src/lib/account-profile-patch.ts 的普通账户 PATCH 主要处理 active time/status,不会在所有声明变化后原子维护 adopted date/offset/provenance;迁移 frontend/supabase/migrations/20260920020000_adopted_birth_date.sql 的 trigger 会在特定字段变化时清理 adopted tuple。这需要实证核对,不能在任务书外擅改出生资料语义。
  • loadChartView 对 profile 查询错误的区分不足,数据库读取失败可能被误认为资料不完整。

相关历史:BUG-005、BUG-009、BUG-018、BUG-073、BUG-076、BUG-715717、BUG-936。BUG-936 是首页 JS 死屏,不替代本单的 /chart 数据真值问题。

2. 根因

当前已确认的是“错误分类、资料更新一致性和客户端旧快照存在断点”;具体现场账号命中的单一数据库状态尚未证明。因此本单不得把某个假设写成已确认根因。优先验证并修复三层:

  1. /api/account PATCH、数据库 trigger/adoption 与 profiles 中 date/time/offset/provenance 的原子一致性;
  2. GET /api/chart-view 对 profile/query/timezone/engine/schema 各类失败的结构化分类;
  3. profile 更新、account refresh、chart request 和 chartCache 之间的失效与竞态。

3. 决策记录(产品授权)

  • 产品授权:填写出生资料后必须能够生成并查看星盘;不能用“资料还在,过一会儿再打开”掩盖永久错误。
  • 保持现有安全语义:reported、accepted、confirmed、candidate、active provenance 不得混为一谈;confirmed 的普通资料编辑不得覆盖受保护的 active time。
  • 失败要可理解、可诊断;不得把数据库错误、资料不完整、时区 adopted tuple 错误、引擎忙/超时和响应格式错误静默归成一个空状态。
  • 本单不顺手改星盘算法、Dasha、校正算法、Node/Ayanamsa 业务规则,也不把 chart_profiles 作为 /chart 真值。
  • 若现有迁移/trigger 与上述边界冲突,执行方必须停在任务书与 BUG 记录中,不能自行放宽或删除保护。

4. 硬红线

  1. 不能信任客户端出生日期、时间、地点或 offset 覆盖服务端 profile 真值。
  2. 不得通过清空 active_birth_*、放宽 accepted/confirmed 校验或 fallback 到不一致声明值来“修复”页面。
  3. 不得把数据库 query error 当作 birth_profile_incomplete;日志不得记录姓名、出生日期/时间、坐标、完整请求体、Cookie、JWT、密钥或模型原文。
  4. 不新增 spinner、骨架屏或第二套滚动/加载机制;继续遵守 frontend/DESIGN.mdfrontend/docs/VOICE.md
  5. 不修改 scripts/jyotish_api_server.py 增长红线,不改 .gitea/workflows/**,不提升 main
  6. 若动表,必须通过向后兼容迁移并运行 npm run test:db --prefix frontend;删除/重命名/收紧约束必须另轮。
  7. 任何既有断言变更都要写“原值 / 新值 / 原因”;测试名不得减少。

5. 任务分解与验收标准

T1 · 端到端重放并锁定服务端真值

  • 用 synthetic/golden fixture 覆盖 reported、accepted、confirmed、跨午夜 adopted date;不要写真实用户资料。
  • 逐段核对 PATCH /api/account → GET /api/account → GET /api/chart-view,确认 status、reported time、active time、active date、timezone id/offset、provenance 的语义一致。
  • 若发现 PATCH/trigger 清掉 adopted tuple 后仍留下可排盘的 active 状态,修复为原子、可解释的状态转换;confirmed 保护保持不变。

验收:同一 fixture 的 account 与 chart-view 返回相同的 server-owned date/time/offsetaccepted/confirmed 不出现可排盘但 adopted tuple 不完整的状态;reported 仍按声明字段排盘;跨午夜使用 adopted dateconfirmed 普通编辑仍不能覆盖 active time。

T2 · 锁定 chart-view 错误分类

  • 为 profile query error、profile incomplete、adopted calculation incomplete、timezone resolver failure、engine 429、engine timeout、bad payload、response schema failure 建立稳定内部分类和用户可读映射。
  • 保留 HTTP/API 兼容性,除非测试证明现有状态码无法表达安全边界;不要把 200 的结构化业务状态随意改成异常 500。
  • 日志只记稳定类别、route、耗时和必要的 HTTP status,不记出生资料。

验收:每类错误有独立测试;数据库读取失败不再伪装成 profile incomplete;用户页面不再只显示无上下文的“没有可显示内容”;VOICE.md 规定的“失败不写过会儿再打开”口径保持一致。

T3 · 修复更新后的缓存与竞态

  • account PATCH 成功后明确失效/重新验证 chart snapshot,至少按 account + profile version/真值指纹隔离;不得让旧 snapshot 在新资料已成功保存后长期冒充新结果。
  • 检查 refreshAccount 与 chart request 的顺序,避免并发请求读到半更新 profileengine cache key 继续包含 date/time/offset、ayanamsa、node mode 等已有必要维度。

验收:资料更新后重新进入 /chart 取得新 date/time/offset;失败时旧 snapshot 不覆盖已确认失效的 profile;同账户不同资料不会互相命中;现有 secondary-page cache 合同不被削弱。

T4 · 补完整回归链路

至少覆盖:PATCH 后 chart-view 新资料、reported/accepted/confirmed、跨午夜、缺 active offset、query error、引擎 busy/timeout/bad payload、schema parse、旧 snapshot 失效、D1 planets/houses 合同。保留现有 chart-view-routechart-view-engineserver-owned-birth-profileadopted-birth-dateaccount-apisecondary-page-entry 测试名。

6. 让步顺序

  1. T1 服务端真值一致性;2. T2 错误分类;3. T3 缓存/竞态;4. T4 全链路回归。若时间不足,不能以只改文案代替 T1;缓存优化可延期,但必须把旧快照风险记入进度和 BLOCKED.md

7. 开工前置命令

git status -sb
git fetch origin --prune
git worktree add -b codex/chart-profile-update-consistency-20260922 .worktrees/chart-profile-update-consistency-20260922 origin/staging
cd .worktrees/chart-profile-update-consistency-20260922/frontend
./node_modules/.bin/tsc --noEmit
npm run lint
npm test
npm run build

开工时再次执行 grep -n '^## BUG-' docs/BUG_HISTORY.md | tail -1,若最大号变化,以当时最大号为准,不复用本文件预估号。

8. BUG 编号起点

本轮写任务书时 docs/BUG_HISTORY.md 最大号为 BUG-995。实现方若确认本现象属于新 Bug,开工前再次核对最大号,按最大号 + 1 连续编号;若只是 BUG-073/076 或 BUG-715717 的复发,必须关联原记录,不得伪装成无关新 Bug。

9. 交付记录

代码实现同轮必须更新 docs/BUG_HISTORY.mddocs/tasks/PROGRESS-chart-profile-update-consistency-20260922.md;若有用户可见状态或文案变化,更新 CHANGELOG.md。验收未闭环前不得写 resolved,不得声称 staging 已部署。