fix(chart): keep zero-year chara and ephemeris dates apart

A zero-year Chara period is shown as 0 years instead of failing the whole chart. Switching ephemeris dates drops the previous day immediately and can be retried. Missing birth data links to chart settings.
This commit is contained in:
jesse-ux
2026-09-25 02:10:48 +08:00
parent 82c8aeb06a
commit 19ea0f06c1
25 changed files with 744 additions and 60 deletions
@@ -0,0 +1,65 @@
# PROGRESS · Chara 0 年大运 + 星历切日期 + 星盘无资料入口(2026-09-25)
## 当前结论
T1、T2、T3、T4 都已完成,没有让步,也没有写进 BLOCKED。引擎 kn_rao 口径、`HEAVY_COMPUTE_PATHS`、数据库和 workflow 都没动。浏览器登录验收没做。没有推 `staging`,也没有推 `main`。
- 工作树:`G:\Ferti\Jyotisha\.worktrees\chart-ephemeris-fixes-20260924`
- 分支:`codex/chart-ephemeris-fixes-20260924`
- 基线:`origin/staging` = `514951d7`(代码提交 `824647f2`)
- BUG:1023 / 1024 / 1025 开工时仍空着,沿用任务书预留号。最大已占用号是 1022。没有占用 1026 及以后。
## 逐项
| 项 | 结果 | 说明 |
| --- | --- | --- |
| T1 | 完成 | 0 年大运显示「0 年」,不展开子运,前后日期首尾相接。`assembleChartView({layers:["chara"]})` 对真实引擎响应返回 ok。 |
| T2 | 完成 | 未缓存日期保留日期条,三段换成静态句。429 后点右箭头重试成功。挂载时的今天刷新和迟到的旧日期都不会盖住新日期。本命 `/api/chart` 按账户和出生资料短缓存,并把 `request.signal` 传进引擎调用。 |
| T3 | 完成 | 四种资料类失败都有「去填写出生资料」,链到 `/?settings=chart`,首页打开星盘资料。`?settings=billing` 的解析结果没改。 |
| T4 | 完成 | BUG 历史、DESIGN §15 / §16、VOICE 三句、CHANGELOG、本记录、真人清单和状态板同一提交。 |
## 真实 golden
本机没有仓库 `.venv`,Anaconda Python 3.11 能 `import swisseph`。fixture `frontend/tests/fixtures/chara-zero-year-1985-08-03-beijing.golden.json` 由下面的调用生成,没有手造引擎形状:
- `C:\Users\74082\anaconda3\python.exe`
- `JyotishAPIHandler.__new__()._compute_chart`,再 `_compute_chara_dasha`
- 虚构 1985-08-03 03:59,纬度 39.9042,经度 116.4074,时区 8,`ayanamsa=raman`,`node_mode=mean`,`skip_vedastro_main_entry_overview`,`antardasha=true`
生成结果 12 段,Sagittarius `duration_years` 为 0(debilitated)。`scripts/jaimini.py` 未改。
## 历史检索
开工读过 BUG-966、BUG-715、BUG-716、BUG-717、BUG-996、BUG-1016、BUG-1017,以及 chart-view / ephemeris / `birth_profile_incomplete` 的相关段。
- BUG-966 的「星历不得再上骨架 / 正在加载」还在。本单没有加骨架。它删掉未命中时清空状态的副作用,就是 BUG-1024。
- BUG-715 的失败分档还在。0 年以前会在 mapper 里抛 `RangeError`,被收成 `engine_bad_payload`。现在 0 年是合法段,不再走这条失败。
- BUG-1016 的骨架例外仍只在星盘盘位。星历没有用。
- BUG-1017 的按需层失败出口没改。
- BUG-996:查询失败仍不是 `birth_profile_incomplete`。四种资料失败各自留原文,只是多了同一颗按钮。
## 行为说明
失败当天,左右箭头重试这一天,不先改日期。日历里另选一天仍会换日期。成功之后箭头恢复成前一天 / 后一天。
本命缓存键用的是账户 id 加上出生日期、时间、经纬、时区、岁差和交点,与 `cachedPostEngine` 同一组字段、同一 5 分钟 TTL。键的 path 是 `/api/ephemeris/natal-chart`,避免和星盘页略有不同的 `/api/chart` 请求体串结果。行运盘仍按所选日期现算。
## 测试
改代码前,在 `frontend` 跑下面这组,93 条,失败 0:
`tests/ephemeris-page.test.tsx`、`tests/ephemeris-route.test.ts`、`tests/chart-page-view.test.tsx`、`tests/chart-page-hook.test.ts`、`tests/settings-url.test.ts`、`tests/home-shell-growth-contract.test.ts`、`tests/secondary-page-entry.test.ts`、`tests/billing-panel.test.ts`
改完后同一组再加上 `tests/ephemeris-page-lifecycle.test.tsx` 和 `tests/chart-view-zero-year.test.ts`:104 条,失败 0。改前那 93 条里没有新增失败。
- `.\node_modules\.bin\tsc --noEmit`:通过。
- `npm run lint`:0 error,121 条既有 warning。本次新文件没有新增 warning,也没有为了清 warning 改业务代码。
- 没有改既有断言的期望值,所以没有「原值 / 新值 / 原因」三栏。
- 没有跑 Windows 全量 `npm test`,也不把别人机器上的 3809 / 56 写成这次的结果。
- 没有跑 `next build`,没有声称 `/`、`/chart`、`/ephemeris` 的 Static 标记在本机构建里复核过。源码没有把这三条改成动态路由。
## 没做的验证
- 没有浏览器登录。真人步骤在 `docs/testing/chart-ephemeris-fixes-20260924.md`,结果都是待验。
- 没有部署,没有对过 staging health。
- 没有推 `origin/staging`。功能分支可以另推 `origin`,以回报里的远端核对为准。
+1 -1
View File
@@ -203,7 +203,7 @@
| 任务书 | 进度 | 主题 | 状态 | 落点 |
| --- | --- | --- | --- | --- |
| `TASK-chart-dasha-western-redesign-20260924.md` | — | **大运 / 西洋盘改版**(原型 https://claude.ai/artifact/DYovEPMqD1xwNgna5xVTre ,产品 09-24 全部同意,相位线常显):大运「现在」卡 + 时间条 + 默认折叠(原 81/144 行平铺);西洋盘 520 三圈、行星避让 + 引线、宫号居宫中、ASC/MC 盘内、行星位置表、点选过滤相位。排在 chart-ephemeris-fixes T1 之后 | 待领取 | — |
| `TASK-chart-ephemeris-fixes-20260924.md` | — | **Chara 0 年大运崩溃 + 星历切日期旧数据 + 星盘无资料空状态(BUG-1023/1024/1025)**:mapper 0/0 → `RangeError`(约 7.8% 星盘,产品定照实显示 0 年、不改引擎);BUG-966 删掉重置致日期与数据分叉(回归);无资料只有一句话,加「去填写出生资料」+ `?settings=chart` 深链(T3 排在 home-slow-network 之后)。大运 / 西洋版式重做不在本单 | 待领取 | — |
| `TASK-chart-ephemeris-fixes-20260924.md` | `PROGRESS-chart-ephemeris-fixes-20260924.md` | **Chara 0 年大运崩溃 + 星历切日期旧数据 + 星盘无资料空状态(BUG-1023/1024/1025)**:mapper 0/0 → `RangeError`(约 7.8% 星盘,产品定照实显示 0 年、不改引擎);BUG-966 删掉重置致日期与数据分叉(回归);无资料只有一句话,加「去填写出生资料」+ `?settings=chart` 深链(T3 排在 home-slow-network 之后)。大运 / 西洋版式重做不在本单 | 待验收 | `PENDING_SHA` |
| `TASK-chart-page-skeleton-wait-20260924.md` | `PROGRESS-chart-page-skeleton-wait-20260924.md` | **星盘页骨架盘等待态 + 三个「永远还没拿到」死角**:「这一张盘还没拿到。」只代表 `view === null`,是唯一等待态,失败各有句子;但客户端无超时(A)、401 跳转前(B)、分盘层失败 / 429 后 `ChartVedicTab` 只看 `!selected` 不看 pending(C)三条路都让这句永远挂着。**产品 09-24 拍板推翻 09-18 方案一(BUG-966)**:星盘页盘位用骨架盘 + 呼吸动效(复用 `VedicChartSvg` 空模型,不复制几何;`prefers-reduced-motion` 静止),范围只限星盘页会出盘轮的位置,首页 / 侧栏 / 星历 / 报告的「不放骨架」合同不动;`AGENTS.md` §6 与 `DESIGN.md` §9 / §15 追加例外。三死角出口:客户端 25s 超时 → `timedOut` 句、401 先写失败体、按层失败态 `layerFailures`(429 独立档,BUG-715/723)。BUG-1016 / 1017 | 已验收(Claude 09-24:红线全绿、+46 测试、失败名单同基线、四路由标记与 gzip 同基线;BUG-1016/1017 resolved) | `1420471a`(staging 已部署);真机清单欠 |
| `TASK-smalltalk-test-stdout-mock-20260921.md` | `PROGRESS-smalltalk-test-stdout-mock-20260921.md` | **测试基础设施(BUG-995)**:`consultation-smalltalk.test.ts` 为验证「SDK 不得把私密文本写进日志」,用 `t.mock.method(process.stdout/stderr, "write", …)` 全局接管标准输出——而 `node:test` 的 TAP 报告也走 `process.stdout`,于是运行器自己的 `# Subtest:` / `ok` 行被吞进测试的 `logs` 数组。实测四次全量:`default Mastra adapter` 四条用例只上报 4 / 2 / 1 / 1 条(`Subtest` 声明本身就少,不是 grep 锚点问题),`# tests` 因此 ±3 抖;单跑该文件 33 条静态展开只报 30 条,仅循环最后一个 `provider_error` 稳定可见。**更要紧**:注入必失败探针后,`invalid_schema` / `bad_json` 只剩一行 `not ok 1 - <文件绝对路径>`,**没有用例名、没有断言消息**(退出码仍是 1,门禁不会漏掉失败,不夸大)。定位 BUG-987 时靠的就是从门禁日志 grep `not ok` 抓用例名,红在这三条上那条路会断。**决策**:mock 改为记录后**透传给原始 `write`**(保留隐私断言,不换报告通道,不改 `npm test` 脚本);F3 把「比用例名列表 diff、不比 `# tests` 总数」写进 `frontend/AGENTS.md`。全仓只此一个文件用了这个手法(已 grep 确认)| 待验收 | `2503c019` |
| `TASK-frontend-optimization-20260828.md` | `PROGRESS-frontend-optimization-20260828.md` | 前端优化九条 | 待核对 | 分支 `codex/frontend-optimization-20260828` |