Splits the remaining work into three briefs that own disjoint files so agents can run them in parallel. The qizheng brief is widened to own scripts/jyotish_api_server.py outright and register all three read-only endpoints (/api/qizheng, /api/western, /api/ephemeris_events), because the chart page needs a tropical natal route that no path exposes today and the ephemeris page needs ingress/station scanning that /api/transit does not provide. The chart-page brief owns app-sidebar.tsx and adds both nav entries; the ephemeris-page brief owns only its own route. Checking the tree changed one plan: vedic-chart-svg.tsx is also imported by rectification-board, so the component stays where it is instead of moving to a shared layer. BUG ranges are pre-allocated per brief to keep parallel work from colliding, and the panchanga Lahiri/Raman split found while prototyping is filed as investigating rather than fixed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
158 lines
10 KiB
Markdown
158 lines
10 KiB
Markdown
# TASK-ephemeris-page-20260915 · P1 星历页
|
||
|
||
## 基线
|
||
|
||
- 代码基线:`origin/staging` = **`2d7698ea`**(文档树基线 `b3ccef4c`,纯文档提交,代码未变)。
|
||
- 并行关系:本单与 `TASK-qizheng-native-chart-20260915`(后端)、`TASK-chart-page-20260915`(前端星盘页)**同期并行**。三份单没有共享代码文件,见「文件归属」。
|
||
- 原型:会话内已交付可点原型(桌面 1440 与手机 390 两套,日期箭头可点,三天数据全部实跑)。实现以本单文字为准,原型作为视觉与信息层级参照。
|
||
- Skill 版本 `6.9.16`,本单**不 bump**。
|
||
|
||
## 为什么要有这一页
|
||
|
||
引擎侧已经算得出、但产品层一条都没露的东西:
|
||
|
||
| 层 | 引擎有没有 | 现在露没露 |
|
||
| --- | --- | --- |
|
||
| Panchanga 五要素 | 有,`/api/panchanga_range` | **没露**,只作为模型的输入 |
|
||
| 行运位置 | 有,`/api/chart` 传任意日期即可 | **没露** |
|
||
| 换座与顺逆停滞 | 需要新端点(由 qizheng 单交付 `/api/ephemeris_events`) | 从来没做 |
|
||
| 「今日」 | 有,`/api/daily-starlanguage` | 露了,但**是模型生成的三句话**,45 秒超时、烧点数 |
|
||
|
||
也就是说,连「今天的五要素是什么」这种纯查表的东西,现在都要让模型去说一遍。这一页把它们直接摆出来:**秒开、零点数、不调模型**。
|
||
|
||
## 决策记录
|
||
|
||
产品负责人在 2026-09-15 的对话中授权:
|
||
|
||
1. 新建一个**不消耗点数、不调模型**的只读星历页。
|
||
2. 内容三段:**今日五要素**(Panchanga)、**当日行运**(含相对本命上升的宫位)、**未来九十天换座与停滞**。
|
||
3. 日期可前后切换。
|
||
4. **这一页是天象本身,不是对用户的判断**。页面不得出现任何运势结论。
|
||
5. 页面底部固定一个出口:**「带这天去提问」**,把当前日期带进对话。免费层的职责是把人送进对话,不是死胡同。
|
||
6. 不做竞品那种「每月取一个采样点 + 套模板」的年度报告——我们已经有真报告,加一个糊出来的月历只会和它打架。
|
||
|
||
## 硬红线
|
||
|
||
1. `frontend/src/app/page.tsx` 在 `origin/staging` 上 **1951 行**,上限 2000 且不得再增长。**本页必须是独立 route。**
|
||
2. **不得扣点、不得调模型。** 新增 BFF **不许 import** `@/lib/consultation-billing`、不许 import `@/mastra`。这一页出现 spinner / 骨架 / 「正在加载」即为不通过。
|
||
3. **不得做任何运势解释。** 事件列表只写「某星 进入 某座」「某星 停滞转顺 · 度数」,不得附加一个字的含义。Yoga / Karana 的吉凶字段来自引擎,可以照搬标签,但**不得展开成建议**。
|
||
4. **不得做三套坐标系之间的换算或叠加。**
|
||
5. `tsc --noEmit` 通过;`npm run lint` **0 error**;`next build` 后 `/` 仍 `○ Static`;首屏 gzip ±2%。
|
||
6. 测试总数不得低于开工时实测;改既有断言要写「原值 / 新值 / 原因」三栏。
|
||
7. 不改数据库结构、不动迁移。
|
||
8. 改 UI 的同一提交内更新 `frontend/DESIGN.md`;文案对照 `frontend/docs/VOICE.md`。
|
||
9. **一行后端代码都不许改**(`scripts/jyotish_api_server.py` 归 qizheng 单独占);**不许改 `frontend/src/components/app-sidebar.tsx`**(归 chart-page 单独占)。
|
||
|
||
## 文件归属(并行前提)
|
||
|
||
**本单拥有**:`frontend/src/app/ephemeris/**`、`frontend/src/app/api/ephemeris/**`、`frontend/src/components/ephemeris/**`、`frontend/src/lib/ephemeris-*.ts`。
|
||
|
||
**本单不许碰**:`scripts/**`、`deploy/**`、`vendor/**`、`frontend/src/components/app-sidebar.tsx`、`frontend/src/app/chart/**`、`frontend/src/app/api/chart-view/**`、`frontend/src/components/personal-report/**`、`frontend/src/components/rectification-*`。
|
||
|
||
**三方共享、只许追加各自小节**:`docs/BUG_HISTORY.md`、`CHANGELOG.md`、`frontend/DESIGN.md`。合入顺序 **qizheng → chart-page → ephemeris-page**;本单最后合,rebase 时按各自小节重放,不得覆盖对方。
|
||
|
||
**侧边栏入口由 `TASK-chart-page-20260915` 交付**(它会同时加「星盘」「星历」两个入口)。本单只交付 `/ephemeris` 这个 route 本身;在对方合入前,页面通过直接访问 URL 验收。
|
||
|
||
## 任务分解
|
||
|
||
### 任务 1 · BFF 路由 `frontend/src/app/api/ephemeris/route.ts`
|
||
|
||
骨架参照 `frontend/src/app/api/daily-starlanguage/route.ts`(`runtime = "nodejs"`、`maxDuration`、`jyotishApiBase`、`createServerSupabaseClient`、`ACCOUNT_BIRTH_SELECT` + `globalBirthProfileFromAccountRow`、`consumeUserRequestRateLimit`),但**不 import** 计费与 mastra。
|
||
|
||
- 入参:目标日期(默认今天,按用户时区)。出生资料只从服务端自有资料读。
|
||
- 上游调用:
|
||
- `/api/panchanga_range`(五要素)
|
||
- `/api/chart`(目标日期的行运位置;宫位由「行运星座号 − 本命上升星座号」推出)
|
||
- `/api/ephemeris_events`(未来九十天换座与停滞,**由 qizheng 单交付**)
|
||
- **端点未上线的降级**:`/api/ephemeris_events` 不可用时该字段返回 `unavailable`,页面渲染静态说明,**不得抛错、不得让整页失败**。
|
||
- 响应窄合同 + zod 校验,新增 `frontend/src/lib/ephemeris-contract.ts`。
|
||
- 未登录返回 `401`。未设置出生资料时,五要素仍可返回(它与本命盘无关),行运的「相对本命」字段置空并注明。
|
||
|
||
**验收标准**
|
||
|
||
- 新增 `frontend/tests/ephemeris-route.test.ts`:未登录 401;无出生资料时五要素仍返回、相对本命为空;`ephemeris_events` 不可用时降级;响应通过 zod 校验。
|
||
- fixture 来自**真实引擎响应**(golden),不得手造形状。
|
||
- 断言未触发任何计费路径。
|
||
|
||
### 任务 2 · 页面 `frontend/src/app/ephemeris/`
|
||
|
||
三段,顺序固定:
|
||
|
||
1. **日期条**:前一天 / 后一天 / 「今天」。三个都是 ≥44px 的真按钮。
|
||
2. **今日五要素**:Vara、Tithi、Nakshatra(含第几足)、Yoga、Karana。桌面五张卡一行,手机改成一张卡里的五行(标签左、值右)。
|
||
3. **当日行运**:九个星体的星座与度数、相对本命宫位、顺逆。桌面表格,手机堆叠行。
|
||
4. **未来九十天**:按日期升序的事件列表,只写事实。
|
||
|
||
底部一张卡:一句「这一页是天象本身,不是对你的判断」+ 主按钮「带这天去提问」(把当前日期带进新对话)。
|
||
|
||
**验收标准**
|
||
|
||
- `tsc --noEmit` 0 错;`npm run lint` 0 error。
|
||
- 组件测试:日期前后切换会换掉五要素与行运;`unavailable` 时事件段渲染静态说明;手机宽度下五要素为纵向一张卡。
|
||
- 正文 ≥ 14px,12–13px 只给短标签与元信息;触摸目标 ≥ 44px。
|
||
- `frontend/DESIGN.md` 追加「星历页」小节。
|
||
- 文案对照 `frontend/docs/VOICE.md`;**逐条检查不含运势判断**。
|
||
|
||
### 任务 3 · 岁差口径标注(BUG-707)
|
||
|
||
**立单时实跑发现的真实缺陷,本单必须在界面上诚实标出来:**
|
||
|
||
`/api/panchanga_range` 的响应里 `report.calculation_policy.panchanga` 写的是
|
||
`"SwissEph Lahiri at sunrise-relative reference time"`,而账户默认岁差自 `80102459`(2026-08-20)起已经是 **Raman**,`/api/chart` 也确实按 Raman 算。
|
||
|
||
后果:**同一个用户,在星盘页看到的是 Raman 盘,在星历页看到的五要素却是 Lahiri 口径。**
|
||
|
||
复现:
|
||
```
|
||
POST /api/panchanga_range {"start_date":"2026-09-14","end_date":"2026-09-16","lat":31.19,"lon":121.44,"tz":8,"ayanamsa":"raman"}
|
||
读 report.calculation_policy.panchanga
|
||
```
|
||
|
||
**本单的处置(不要越界)**:
|
||
|
||
- **不修**。统一到 Raman 还是明确声明 panchanga 永远用 Lahiri,是产品决策,另出单。
|
||
- 五要素卡片下方**必须**出现一行说明:这一段按 Lahiri 计算,与星盘页的岁差设置不同。措辞对照 `frontend/docs/VOICE.md`,不得写成故障或道歉。
|
||
- 行运段用的是 `/api/chart`,走账户岁差,**这一段不要加这句**——否则会误导。
|
||
- 在 `docs/BUG_HISTORY.md` 记 **BUG-707**,状态 `investigating`:写清现象、复现、影响面(星盘页 vs 星历页口径分裂)。**不要猜根因、不要顺手改默认值。**
|
||
- qizheng 单也记了同一现象(BUG-703 `investigating`)。开工时若发现对方已经记了,**关联它、不要重复开号**,把本单的编号让出来。
|
||
|
||
**验收标准**
|
||
|
||
- 五要素段出现该说明,行运段不出现。
|
||
- `docs/BUG_HISTORY.md` 有对应记录,或明确关联 BUG-703。
|
||
|
||
## 让步顺序
|
||
|
||
1. 先保证 任务 1 + 任务 2 的前三段(日期条 / 五要素 / 当日行运)。这三段不依赖新端点,**可以完全独立交付**。
|
||
2. 「未来九十天」可以只落 `unavailable` 形态,等 `/api/ephemeris_events` 上线再点亮。
|
||
3. 「带这天去提问」若接对话的链路复杂,可以先做成跳到新对话并预填日期文本。
|
||
4. **不可让步**:不碰 `page.tsx`、不碰 sidebar、不扣点不调模型、无 spinner、页面无运势判断、任务 3 的岁差标注。
|
||
|
||
## 开工前置命令
|
||
|
||
```bash
|
||
cd /workspace/Jyotisha
|
||
git status -sb
|
||
git fetch origin --prune
|
||
git worktree add -b codex/ephemeris-page-20260915 .worktrees/ephemeris-page-20260915 origin/staging
|
||
cd .worktrees/ephemeris-page-20260915/frontend
|
||
npm ci
|
||
./node_modules/.bin/tsc --noEmit
|
||
npm run lint
|
||
npm test 2>&1 | tail -20 # 记下测试总数
|
||
npx next build 2>&1 | grep -E "^[│├└]| / " | head # 记下 `/` 的 Static 标记与首屏 gzip
|
||
wc -l src/app/page.tsx # 应为 1951
|
||
```
|
||
|
||
## BUG 编号起点
|
||
|
||
`origin/staging` 上当前最大号 **BUG-699**。三份并行单预分配:qizheng 单 700–703、chart-page 单 704–706、**本单 707–709**。任务 3 用 **BUG-707**(若 qizheng 单已记 BUG-703,改为关联,本单不另开号)。
|
||
|
||
## 进度与记录
|
||
|
||
- 进度记录:`docs/tasks/PROGRESS-ephemeris-page-20260915.md`,本单状态板一行。
|
||
- 环境缺口写 `BLOCKED.md`。
|
||
- 真人验收清单写进 `docs/testing/`:日期前后切换、五要素随日期变化、事件列表降级形态、「带这天去提问」真的把日期带进了对话。
|
||
- `CHANGELOG.md` 写用户可感知的变化,注明 Skill 版本**未 bump**。
|
||
- 索引:`docs/tasks/README.md` 追加本单。
|