Files
Jyotisha/docs/tasks/TASK-chart-ephemeris-fixes-20260924.md
T

8.3 KiB
Raw Blame History

TASK · Chara 0 年大运崩溃 + 星历切日期旧数据 + 星盘无资料空状态(2026-09-24)

基线

  • origin/staging = 9c9b66d6。分支 codex/chart-ephemeris-fixes-20260924。
  • 串行:T1、T2 可立即做;T3 碰 page.tsx / settings-url.ts,排在 TASK-home-slow-network-20260924.md 合入之后(或 T3 单独后合)。大运页、西洋盘 UI 重做不在本单(等原型定稿另出单),本单只修 bug 不改版式。

事故实证

T1 · 大运右栏「这张盘算不出来,我们已经记录下来了。」

  • frontend/src/lib/chart-view-mapper.ts charaTrack() → addWeightedSpan(start, end, index, weights),权重 finite(duration_years) ?? 1;?? 1 不替换 0。某段 Chara 大运为 0 年时 12 个子运全为 0 → total = 0 → 0/0 = NaN → new Date(NaN).toISOString() 抛 RangeError。
  • chart-view-load.ts assembleChartView catch 非 ZodError → engine_bad_payload → use-chart-page.ts requestLayer fail(...) → chartLayerFailureCopy → unavailable 句。
  • 0 来自 scripts/jaimini.py _chara_dasha_duration_profile(kn_rao):years = count-1,<=0 改 12,之后再按旺 +1 / 弱 -1,于是"一宫之隔且落陷"= 0。排查实测随机 2000 个生日(1960–2005)有 7.8% 至少一段 0 年;虚构 1985-08-03 03:59 北京可复现(Sagittarius 0 年)。
  • 引擎响应形状与 mapper 读取路径一致,不是请求或超时问题。

T2 · 星历切换日期,下方仍是前一天数据

  • frontend/src/components/ephemeris/ephemeris-page.tsx changeDate:setDate(next); const cached = …; if (cached) setState(ready…),缓存未命中无 else,state.payload 保留旧日;顶部日期已是新日。
  • 回归来源:9ccb65b7(BUG-966,09-18)删掉原 else setState({ phase: "open" }),因为同时加的 if (!payload) return <SecondaryPageShell waiting…> 会把整页(含日期条)换成等待句。
  • 请求失败(429 / 非 JSON / 坏包)时 load 直接 return,旧数据永久留着,且不重试(effect 只随 date 变)。
  • 挂载时 refreshEphemerisPage()(今天、无 date)不可中止,其回写守卫只比 payload.date,会在"切到未缓存日期"期间把今天的数据写回。
  • app/api/ephemeris/route.ts 每切一天都并行重算本命 /api/chart(与日期无关),且不把 request.signal 传给引擎调用。
  • 违反 frontend/DESIGN.md §16:「切日期立刻换五要素和行运……数据未到时写静态句「这一天的五要素还没拿到。」「这一天的行运还没拿到。」」。
  • 无 BUG 记录、无测试覆盖 EphemerisPage 状态机(ephemeris-page.test.tsx 只静态渲染 EphemerisView)。

T3 · 星盘页无出生资料时只有一句话

  • chart-page-view.tsx:任何非 ok 状态只渲染 <section className="chart-page-message"><p>{view.message}</p></section>;birth_profile_incomplete 文案「还没有可用来排盘的出生资料。把日期、时间和地点填进星盘资料后,打开就能看见盘。」但没有按钮;lib/settings-url.ts parseSettingsQuery 只支持 ?settings=billing,没有跳到星盘资料的深链。

根因

  • T1:mapper 按权重分配时间,未处理总权重为 0;引擎 kn_rao 口径可产出 0 年(合法值,不是引擎错误)。
  • T2:BUG-966 为避免整页换等待句,删掉了"未命中就清空",导致日期与数据分叉;失败无出口。
  • T3:失败页是纯文案设计,缺行动入口与深链。

决策记录(产品 2026-09-24)

  • D1 0 年大运照实显示:该段标注「0 年」,不占时间轴、不展开子运(子运区写「本段时长为 0,不展开」),前后段日期连续;不改引擎口径(kn_rao 规则不动)。
  • D2 星历切到未缓存日期:日期条与布局保留,五要素 / 行运区立即换成现有静态句(「这一天的五要素还没拿到。」「这一天的行运还没拿到。」),事件区写新句「换座和停滞还在取。」(不再误用「还没接上」)。不加 spinner / 骨架(星盘骨架例外不外溢到星历)。请求失败 → 各区显示「这一天的星历没取到,点日期再试一次。」,再点同一日期或左右箭头即重试。
  • D3 星盘无资料:chart-page-message 加标题「还没有星盘资料」+ 现有文案 + 按钮「去填写出生资料」→ 打开设置弹窗的星盘资料面板(新增深链 ?settings=chart,与 ?settings=billing 同一 helper)。timezone_resolver_failure / adopted_calculation_incomplete / profile_query_error 也给同一按钮(文案各自不变)。样式:居中、标题 --type-title-md、按钮为主按钮,其余沿用现有 token。
  • D4 /api/ephemeris 本命盘按账户 + 资料指纹做服务端短缓存(复用 chart-view-engine 的 cachedPostEngine 或同等 5 分钟 TTL),并把 request.signal 传入引擎调用。

硬红线

  1. 不改 scripts/jaimini.py 口径;不改 HEAVY_COMPUTE_PATHS 与并发(BUG-717)。
  2. 星历不得出现骨架 / spinner / 「正在加载」(DESIGN §9 例外只限星盘)。
  3. 旧日期响应不得覆盖新日期(含挂载时的今天刷新)。
  4. 深链解析只在 settings-url.ts 一处;/、/chart、/ephemeris 保持 ○ Static。
  5. 既有断言三栏;测试总数不低于基线;不改 Python。

任务分解

T1 · Chara 0 年(chart-view-mapper.ts、chart-dasha-tab.tsx)

  • addWeightedSpan / charaTrack 处理 0 权重与 0 年大运;0 年段在 Track 里显示「0 年」且无子运列表。

验收:chart-view-mapper 测试用真实引擎响应 golden(虚构 1985-08-03 03:59 北京,按 AGENTS §7.4 由真实 _compute_chara_dasha 生成并入库 fixture)→ 12 段 periods、Sagittarius 段 0 年、无 RangeError、前后日期连续;assembleChartView({layers:["chara"]}) 返回 ok;UI 测试 0 年段不渲染子运。

T2 · 星历(ephemeris-page.tsx、ephemeris-view.ts、app/api/ephemeris/route.ts)

  • state 只在 payload.date === date 时视为 ready,否则 EphemerisView payload={null}(保留日期条);事件区 pending 文案;失败态与重试;挂载刷新守卫比对 date;路由本命缓存 + 传 signal。

验收:新增 EphemerisPage 生命周期测试——切到未缓存日期立即显示静态句且日期条仍在;旧响应迟到不覆盖;429 → 失败句,再点重试成功;挂载时今天刷新不覆盖正在看的其它日期。路由测试:同账户连续两天只调一次本命 /api/chart。

T3 · 星盘空状态 + 深链(chart-page-view.tsx、settings-url.ts、设置弹窗打开入口;排在 home-slow-network 之后)

验收:四种资料类失败都渲染按钮;点按钮 → /?settings=chart → 设置弹窗打开到星盘资料;?settings=billing 行为不变;/chart 仍 ○。

T4 · 记录

  • docs/BUG_HISTORY.md:BUG-1023 Chara 0 年大运前端崩溃(关联 BUG-715、1017);BUG-1024 星历切日期旧数据(复发自 BUG-966 的修复副作用,写明旧测试为何没拦住);BUG-1025 星盘无资料无入口。
  • DESIGN §15 / §16、VOICE(新增三句)、CHANGELOG、PROGRESS、docs/testing/chart-ephemeris-fixes-20260924.md(Slow 3G 下星历连点三天、断网切日期、0 年大运样例、无资料账号点按钮)。

让步顺序

  1. T1 若 golden 生成需要 swisseph 而执行环境无,允许用排查时的虚构生日在有 swisseph 的机器生成后入库,注明生成命令。
  2. D4 缓存若与路由结构冲突,可只做传 signal,缓存写入 BLOCKED。
  3. 不得让步:D1 不改引擎、红线 2、红线 3。

开工前置命令

git fetch origin --prune
git worktree add -b codex/chart-ephemeris-fixes-20260924 .worktrees/chart-ephemeris-fixes-20260924 origin/staging
cd .worktrees/chart-ephemeris-fixes-20260924/frontend
./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -5
npx tsx --test tests/ephemeris-page.test.tsx tests/ephemeris-route.test.ts tests/chart-page-view.test.tsx tests/chart-page-hook.test.ts

BUG 编号

本单 BUG-1023 / 1024 / 1025(1021/1022 由慢网单预留),开工时核对。