docs(tasks): brief the read-only chart and ephemeris pages
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
This commit is contained in:
co-authored by
Claude Opus 5
parent
b3ccef4cbb
commit
6f74aa6704
@@ -221,7 +221,9 @@
|
||||
|
||||
| `TASK-rectification-house-lord-gochara-research-20260913.md` | `PROGRESS-rectification-house-lord-gochara-research-20260913.md` | 研究单:宫主触发与木星/土星过运(合冲本命宫主、罗睺紧密合、年精度、用于 block 选上升)四种放宽,20 例公开 AA 离线量 block 层与 minute 层两组指标;引擎里已有宫主/功能吉凶/受控过运,只量缺的四条 | 待验收(无收益,关闭;不立实现单) | `codex/rectification-house-lord-gochara-research-20260913` |
|
||||
|
||||
| `TASK-qizheng-native-chart-20260915.md` | `PROGRESS-qizheng-native-chart-20260915.md` | 接入原生七政四余排盘:vendored `stem-branch` 0.8.0(Apache-2.0)归档 + API 镜像 Node runtime + 适配器(计都派别与宿度坐标系参数化、boundary 按实测重写)+ `/api/qizheng` 薄注册。实证三条:四柱时柱按 UTC 算(BUG-700,本轮不修不调用)、`ketuMode` 写死未暴露(BUG-701)、boundary 把空神煞与未闭合庙旺说成已生成(BUG-702)。前端第五个 Tab 另出单,串行在本单之后 | 待领取 | — |
|
||||
| `TASK-qizheng-native-chart-20260915.md` | `PROGRESS-qizheng-native-chart-20260915.md` | **后端单(独占 `scripts/jyotish_api_server.py`)**:vendored `stem-branch` 0.8.0(Apache-2.0)归档 + API 镜像 Node runtime + 七政适配器(计都派别与宿度坐标系参数化、boundary 按实测重写)+ 三个只读端点 `/api/qizheng`、`/api/western`、`/api/ephemeris_events`。实证三条:四柱时柱按 UTC 算(BUG-700,本轮不修不调用)、`ketuMode` 写死未暴露(BUG-701)、boundary 把空神煞与未闭合庙旺说成已生成(BUG-702)。BUG 段 700–703 | 待领取 | — |
|
||||
| `TASK-chart-page-20260915.md` | `PROGRESS-chart-page-20260915.md` | **前端单(独占 `app-sidebar.tsx`,同时加星盘与星历两个入口)**:P0 只读星盘页,五个 Tab(星盘 / 基础信息 / 大运 / 西洋盘 / 七政四余),中宫排盘参数卡,三套坐标系各自标注且禁止互相换算。不扣点不调模型不出 spinner;不碰 `page.tsx`(1951/2000);**不搬 `vedic-chart-svg.tsx`**(rectification-board 也在用)。BUG 段 704–706 | 待领取 | — |
|
||||
| `TASK-ephemeris-page-20260915.md` | `PROGRESS-ephemeris-page-20260915.md` | **前端单**:P1 星历页,今日五要素 + 当日行运(相对本命宫位)+ 未来九十天换座与停滞,底部「带这天去提问」出口。页面不得出现任何运势判断。含实证缺陷:panchanga 写死 Lahiri 与账户 Raman 分裂(BUG-707,只标注不修)。侧边栏入口由 chart-page 单交付。BUG 段 707–709 | 待领取 | — |
|
||||
|
||||
## 命名与归档
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
# TASK-chart-page-20260915 · P0 星盘事实页(五个 Tab)
|
||||
|
||||
## 基线
|
||||
|
||||
- 代码基线:`origin/staging` = **`2d7698ea`**(文档树基线 `b3ccef4c`,纯文档提交,代码未变)。
|
||||
- 并行关系:本单与 `TASK-qizheng-native-chart-20260915`(后端)、`TASK-ephemeris-page-20260915`(前端星历页)**同期并行**。三份单没有共享代码文件,见「文件归属」。
|
||||
- 原型:会话内已交付可点原型(五个 Tab 的完整形态、中宫排盘参数卡、三套坐标系边界文案、桌面 1440 与手机 390 两套)。**原型里的数据全部是实跑引擎输出**,不是编的;实现时以本单文字为准,原型作为视觉与信息层级参照。
|
||||
- Skill 版本 `6.9.16`,本单**不 bump**(不新增解读口径,只把已算出的事实露出来)。
|
||||
|
||||
## 为什么要有这一页
|
||||
|
||||
当前产品里,用户填完出生资料**看不见自己的盘**:
|
||||
|
||||
- `VedicChartSvg` 在 `frontend/src/components/personal-report/vedic-chart-svg.tsx`,只被三处引用——`personal-report-document-view.tsx`、`personal-report-markdown-view.tsx`(用其中的 `NorthIndianChartSvg`)、`rectification-board.tsx`。也就是说,盘面只在**已生成报告**或**校正进行中**才出现。
|
||||
- 侧边栏「星盘资料」是 `ChartLibraryPanel`(`frontend/src/components/chart-library-panel.tsx`),只管出生资料的增删改查,**不画盘**。
|
||||
- 结果:第一次看见自己的盘必须先花点数、等模型。
|
||||
|
||||
而引擎侧 `/api/chart`、`/api/dasha/chara`、`/api/nakshatra_full` 早就在;星宿与 Pada 在 `scripts/dasha_analyzer.py` 已经算出来。**这一页不新增任何计算,只是把已有结果摆出来。**
|
||||
|
||||
## 决策记录
|
||||
|
||||
产品负责人在 2026-09-15 的对话中授权:
|
||||
|
||||
1. 新建一个**不消耗点数、不调模型、打开即有**的只读星盘页。
|
||||
2. Tab 顺序定为 **星盘 / 基础信息 / 大运 / 西洋盘 / 七政四余**。
|
||||
3. **体系分在 Tab 这一层**;分盘 chip(D1–D30)只在印度体系内部使用。不得把西洋盘或七政四余做成第 13 个 chip。
|
||||
4. **盘的中宫放「排盘参数卡」**:钟表时间与校正状态、出生地与经纬、岁差与交点模式、上升与月亮星宿、当前大运分运、引擎名。手机上放不下,只留岁差两行,完整参数改成盘下方一张卡。
|
||||
5. **三套坐标系必须各自在中宫写明自己是什么,并显式点名不能互相换算**,也不得把两边结论叠加成「双重印证」。
|
||||
6. 「传统象征」词条层可以做,但必须带边界句「是词条式释义,不是对你个人的判断」,且**不得出现任何运势判断**。
|
||||
|
||||
### 本单推翻的一处早期说法
|
||||
|
||||
立单核对时发现:`VedicChartSvg` 并非「只被 personal-report 引用」,`rectification-board.tsx` 也在用。**因此本单不搬这个文件。** 新页面直接从现有路径 `@/components/personal-report/vedic-chart-svg` 引入。搬家会同时碰校正热点文件,与并行的校正轮次冲突;若将来要搬,单独一轮只做搬家。
|
||||
|
||||
## 硬红线
|
||||
|
||||
1. `frontend/src/app/page.tsx` 在 `origin/staging` 上是 **1951 行**,AGENTS.md §6 定了 2000 行上限且「不得再增长」。**本页必须是独立 route,一行都不许加进 `page.tsx`。**
|
||||
2. **不得搬动 `vedic-chart-svg.tsx`**,见上。也不得改动它的导出签名——三个既有调用点必须零改动。
|
||||
3. **不得扣点、不得调模型。** 新增的 BFF 路由**不许 import** `@/lib/consultation-billing` 或任何 mastra agent。这一页出现 spinner / 骨架 / 「正在加载」即为不通过(AGENTS.md §6:揭幕后不得出现加载动画,流式生成中除外)。
|
||||
4. `./node_modules/.bin/tsc --noEmit` 通过;`npm run lint` **0 error**。
|
||||
5. `next build` 后 `/` 仍是 `○ Static`;首屏 gzip 变化在 ±2% 内,超出要在进度记录里给出原因。**新 route 自身不要求 Static**(它读账户数据)。
|
||||
6. 测试总数不得低于开工时 `origin/staging` 的实测。改任何既有断言必须写「原值 / 新值 / 原因」三栏。
|
||||
7. 不改数据库结构;本单不得顺带动迁移。
|
||||
8. 改 UI 的同一提交内更新 `frontend/DESIGN.md`;新文案先对照 `frontend/docs/VOICE.md`。
|
||||
9. **一行后端代码都不许改**,特别是 `scripts/jyotish_api_server.py`——它归 `TASK-qizheng-native-chart-20260915` 独占。
|
||||
|
||||
## 文件归属(并行前提)
|
||||
|
||||
**本单拥有**:
|
||||
|
||||
- `frontend/src/app/chart/**`(新 route)
|
||||
- `frontend/src/app/api/chart-view/**`(新 BFF)
|
||||
- `frontend/src/components/chart-page/**`(新组件目录)
|
||||
- **`frontend/src/components/app-sidebar.tsx`** —— 本单独占。**本单同时加「星盘」与「星历」两个入口**,星历页那一单不碰这个文件。
|
||||
- `frontend/src/lib/chart-view-*.ts`
|
||||
|
||||
**本单不许碰**:`scripts/**`、`deploy/**`、`vendor/**`、`frontend/src/app/ephemeris/**`、`frontend/src/app/api/ephemeris/**`、`frontend/src/lib/ephemeris-*.ts`、`frontend/src/components/personal-report/**`、`frontend/src/components/rectification-*`。
|
||||
|
||||
**三方共享、只许追加各自小节**:`docs/BUG_HISTORY.md`、`CHANGELOG.md`、`frontend/DESIGN.md`。合入顺序 **qizheng → chart-page → ephemeris-page**;rebase 时按各自小节重放,不得覆盖对方。
|
||||
|
||||
## 任务分解
|
||||
|
||||
### 任务 1 · BFF 路由 `frontend/src/app/api/chart-view/route.ts`
|
||||
|
||||
按既有路由的写法(参照 `frontend/src/app/api/daily-starlanguage/route.ts` 的骨架:`export const runtime = "nodejs"`、`maxDuration`、`jyotishApiBase = process.env.JYOTISH_API_BASE ?? "http://127.0.0.1:5200"`、`createServerSupabaseClient`、`ACCOUNT_BIRTH_SELECT` + `globalBirthProfileFromAccountRow`、`consumeUserRequestRateLimit`),但:
|
||||
|
||||
- **不 import** `consultation-billing`、不 import `@/mastra`。
|
||||
- 出生资料只从**服务端自有资料**读,不接受前端传入的出生参数(沿用 `server-owned-birth-profile` 的既有边界)。
|
||||
- 未登录返回 `401`。
|
||||
- 上游调用:`/api/chart`(主)+ `/api/dasha/chara`(Chara 大运)。
|
||||
- **坑,必须照做**:`/api/dasha` 单独调会忽略月亮黄经、回默认 Ketu balance;大运要从 `/api/chart` 响应的 `dasha` 段取。`/api/dasha/chara` 与 `/api/varga_full` 必须把 `planets`、`ascendant`、`houses` 一起传进去,否则返回 `planets must include longitude data`。
|
||||
- 西洋盘与七政四余走 `/api/western`、`/api/qizheng`(由 qizheng 单交付)。**这两个端点未上线时,本路由对应字段返回 `unavailable`,不得抛错、不得让整页失败。**
|
||||
- 响应是一个窄合同(新增 `frontend/src/lib/chart-view-contract.ts`,zod 校验),只含页面要用的字段,不透传引擎全量响应。
|
||||
|
||||
**验收标准**
|
||||
|
||||
- 新增 `frontend/tests/chart-view-route.test.ts`:未登录 401;资料不全返回结构化提示而非 500;`/api/western` 或 `/api/qizheng` 不可用时该字段为 `unavailable` 且其余 Tab 正常;响应通过 zod 校验。
|
||||
- fixture 必须来自**真实引擎响应**(golden),不得手造形状(AGENTS.md §7.4)。
|
||||
- 全程零扣点:测试里断言未调用任何计费路径。
|
||||
|
||||
### 任务 2 · 页面与五个 Tab `frontend/src/app/chart/`
|
||||
|
||||
页面结构按原型:页眉(眉标「直接计算 · 打开即有 · 不消耗点数」+ 标题 + 出生资料一行)→ 五个 Tab → 内容区。
|
||||
|
||||
- **星盘**:D1–D30 分盘 chip(一行可换行)+ 盘面 + 右侧行星位置表(星体 / 星座与度数 / 宫位 / 状态)。盘面复用 `VedicChartSvg`(**从现有路径引入,不搬文件**)。
|
||||
- **基础信息**:每个星体一张卡——D1 落座与宫位、本命星宿 + 第几足 + 星宿主、D9 落座、传统象征一行(灰字 + 顶部边界卡)。
|
||||
- **大运**:Vimshottari 与 Chara 两条轨**并列**,当前段高亮。底部固定一句:只有两条同时指向同一段时间才算证据,单轨命中降一级置信度。
|
||||
- Chara 的正式名称是 **Chara Dasha(kn_rao 变体)**,引擎 `method` 字段就这么写的,**不要写成 Narayana**。
|
||||
- 分运(antardasha)若从引擎拿不到,可以按标准比例从大运边界推,但**必须在界面上注明「按标准比例推出,不是引擎单独返回的字段」**。
|
||||
- **西洋盘**:圆盘(外圈黄道十二宫、Placidus 不等宽宫位、上升在左、度数逆时针)+ 元素模式分布 + 主要相位表。右栏第一张必须是边界卡。
|
||||
- **七政四余**:十二地支宫方格(巳午未申在上,寅丑子亥在下)+ 人事宫 + 所辖宿 + 十一曜宿度表。必须出现三条口径:宿度自角宿起算、计都派别、庙旺未闭合。
|
||||
|
||||
**中宫排盘参数卡**:桌面放六行;**手机上盘宽只有约 318px,中宫塞不下**,只留岁差两行,完整参数改成盘下方一张卡。原型里这两种形态都画了。
|
||||
|
||||
**验收标准**
|
||||
|
||||
- `tsc --noEmit` 0 错;`npm run lint` 0 error。
|
||||
- 新增组件测试:五个 Tab 都能切换;`unavailable` 的 Tab 渲染静态说明而不是报错;手机宽度下中宫只有两行且完整参数卡出现在盘下方。
|
||||
- 视觉:`frontend/DESIGN.md` 追加「只读星盘页」小节,写清 Tab 体系边界、中宫参数卡、三套坐标系的各自标注。
|
||||
- 文案对照 `frontend/docs/VOICE.md`。
|
||||
- **触摸目标 ≥ 44px**;正文 ≥ 14px,12–13px 只给短标签与元信息(DESIGN.md §3)。盘内 SVG 文字按缩放后实际像素计,不得低于 12px。
|
||||
|
||||
### 任务 3 · 侧边栏入口(本单独占)
|
||||
|
||||
- `frontend/src/components/app-sidebar.tsx`(367 行)在「新建对话」之后、「我的报告」之前新增两个入口:**星盘**、**星历**。
|
||||
- 两个入口都要有折叠态图标(DESIGN.md「Sidebar shell · Collapsed content」要求折叠时仍可达)。
|
||||
- 当前项使用既有的白玻璃面 + 2px 深棕标记,不得新造一套选中态。
|
||||
- **星历入口现在会指向一个尚未存在的 route**(由 `TASK-ephemeris-page-20260915` 交付)。本单先建一个最小占位页 `frontend/src/app/ephemeris/page.tsx`?**不。** 本单**不创建** ephemeris 目录(归属对方)。做法:星历入口在对方 route 落地前指向 `/ephemeris`,本地开发会 404,这是预期的;在进度记录里写明,并在 PR 描述里注明合入顺序。
|
||||
|
||||
**验收标准**
|
||||
|
||||
- 侧边栏三态(桌面展开 288px / 平板折叠 64px / 手机抽屉)下两个入口都可达。
|
||||
- `frontend/tests/touch-target-contract.test.ts` 通过。
|
||||
- 不得改动既有入口的顺序与文案。
|
||||
|
||||
## 让步顺序
|
||||
|
||||
1. 先保证 任务 1 + 任务 2 的前三个 Tab(星盘 / 基础信息 / 大运)。这三个不依赖任何新端点,**可以完全独立交付**。
|
||||
2. 西洋盘、七政四余两个 Tab 可以只落 `unavailable` 形态,等对方端点上线再点亮。
|
||||
3. 「传统象征」词条层可以延后;延后时基础信息卡只保留事实三行。
|
||||
4. **不可让步**:不碰 `page.tsx`、不搬 `vedic-chart-svg.tsx`、不扣点不调模型、无 spinner、三套坐标系的边界标注、`/` 仍 Static。
|
||||
|
||||
## 开工前置命令
|
||||
|
||||
```bash
|
||||
cd /workspace/Jyotisha
|
||||
git status -sb
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/chart-page-20260915 .worktrees/chart-page-20260915 origin/staging
|
||||
cd .worktrees/chart-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
|
||||
```
|
||||
|
||||
无 Docker 时 `npm run test:db` 的失败清单要与基线逐条比对,写进 `BLOCKED.md`,不得写成「通过」。
|
||||
|
||||
## BUG 编号起点
|
||||
|
||||
`origin/staging` 上 `docs/BUG_HISTORY.md` 当前最大号 **BUG-699**。三份并行单预分配:qizheng 单 700–703、**本单 704–706**、ephemeris-page 单 707–709。本单是新功能,正常情况下**不需要开 BUG 号**;只有实现中发现既有缺陷才用,用满要通知另外两单整体后移。
|
||||
|
||||
## 进度与记录
|
||||
|
||||
- 进度记录:`docs/tasks/PROGRESS-chart-page-20260915.md`,本单状态板一行。
|
||||
- 环境缺口写 `BLOCKED.md`(预期:无 Docker → `test:db` 阻塞;无登录态与 Chrome → 浏览器级走查留给 `docs/testing/`)。
|
||||
- 真人验收清单写进 `docs/testing/`:五个 Tab 切换、手机宽度中宫两行、折叠侧边栏两个入口可达、未登录跳转。
|
||||
- `CHANGELOG.md` 写用户可感知的变化,注明 Skill 版本**未 bump**。
|
||||
- 索引:`docs/tasks/README.md` 追加本单。
|
||||
@@ -0,0 +1,157 @@
|
||||
# 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` 追加本单。
|
||||
@@ -1,4 +1,6 @@
|
||||
# TASK-qizheng-native-chart-20260915 · 接入原生七政四余排盘
|
||||
# TASK-qizheng-native-chart-20260915 · 接入原生七政四余排盘,并开出三个只读计算端点
|
||||
|
||||
> **2026-09-15 修订(立单当天)**:本单扩了范围。原版只做 `/api/qizheng`;核对后发现 P0 星盘页与 P1 星历页还各缺一个后端端点,而三者都要改 `scripts/jyotish_api_server.py`。为了让后续两份前端单能真并行,**把三个端点的注册全部收进本单**,由本单独占该文件的写权。两份前端单一行后端代码都不碰。
|
||||
|
||||
## 基线
|
||||
|
||||
@@ -116,9 +118,23 @@ node CLI --date "1990-04-09T13:24:00" --lat 31.19 --lng 121.44 --pillars -
|
||||
|
||||
**本单做**:vendored 归档、镜像 Node runtime、适配器、只读接口、测试、Bug 记录。
|
||||
|
||||
**本单不做**:前端界面。原因是前端的「七政四余」是**星盘页的第五个 Tab**,而那个星盘页(P0 事实层:星盘 / 基础信息 / 大运 / 西洋盘 / 七政四余)本身还没有立单。两件事必须串行:先有星盘页,再往上挂 Tab。前端单由 Claude 另出,依赖本单的 `/api/qizheng` 响应合同。
|
||||
**本单不做**:任何前端文件。
|
||||
|
||||
原型(含五个 Tab 的完整形态、中宫排盘参数卡、三套坐标系的边界文案)已经画好,前端单会引用它。
|
||||
**并行与文件归属**。同期还有两份前端单,三份单之间**没有任何共享代码文件**,可以并行开工:
|
||||
|
||||
| 文件 / 目录 | 归属 |
|
||||
| --- | --- |
|
||||
| `vendor/**`、`NOTICE`、`deploy/**` | 本单 |
|
||||
| `scripts/qizheng_chart_engine.py`、`scripts/ephemeris_events.py` | 本单 |
|
||||
| **`scripts/jyotish_api_server.py`** | **本单独占**。两份前端单一行都不许改 |
|
||||
| `tests/**`(本单新增的三个文件) | 本单 |
|
||||
| `frontend/src/app/chart/**`、`frontend/src/app/api/chart-view/**`、`frontend/src/components/app-sidebar.tsx` | `TASK-chart-page-20260915` |
|
||||
| `frontend/src/app/ephemeris/**`、`frontend/src/app/api/ephemeris/**`、`frontend/src/lib/ephemeris-*.ts` | `TASK-ephemeris-page-20260915` |
|
||||
| `docs/BUG_HISTORY.md`、`CHANGELOG.md`、`frontend/DESIGN.md` | 三方都写,**只许追加各自小节**,合入顺序 本单 → chart-page → ephemeris-page |
|
||||
|
||||
前端对本单的依赖是**运行时依赖,不是代码依赖**:端点没上线时,前端对应区块渲染静态说明,端点合入后自然点亮。
|
||||
|
||||
原型(五个 Tab 的完整形态、中宫排盘参数卡、三套坐标系的边界文案)已经画好,两份前端单都会引用它。
|
||||
|
||||
## 任务分解
|
||||
|
||||
@@ -160,17 +176,38 @@ node CLI --date "1990-04-09T13:24:00" --lat 31.19 --lng 121.44 --pillars -
|
||||
- `.venv/bin/python -m pytest tests/test_qizheng_chart_engine.py` fail=0。
|
||||
- `.venv/bin/python scripts/run_quality_gate.py --profile quick` 通过。
|
||||
|
||||
### 任务 3 · `/api/qizheng` 薄注册
|
||||
### 任务 3 · 三个只读端点薄注册(本单独占 `scripts/jyotish_api_server.py` 写权)
|
||||
|
||||
- 在 `scripts/jyotish_api_server.py` 的路由分支处新增一条 `elif path == '/api/qizheng':`。参照点:`origin/staging` 上 `elif path == '/api/chart':` 在 **3497 行**。
|
||||
- handler 用既有的 `_load_local_module('qizheng_chart_engine')` 模式(该函数已存在,见 137 / 413 / 1009 行的用法),函数体 **≤ 8 行**,只做加载、调用、把领域异常翻成 `BadRequest`。
|
||||
- **行数硬上限**:`origin/staging` 上该文件是 **11313 行**,契约上限是 **11363**(`tests/test_api_server_growth_contract.py`,baseline 11063 + 300 余量)。**本轮对该文件的净增不得超过 12 行**,只剩 50 行总余额。
|
||||
- **行数硬上限**:`origin/staging` 上该文件是 **11313 行**,契约上限是 **11363**(`tests/test_api_server_growth_contract.py`,baseline 11063 + 300 余量),**总余额只有 50 行**。三个端点加起来,本轮对该文件的净增 **不得超过 32 行**。超了就把 handler 再压薄,不要动契约。
|
||||
- 端点是只读计算,按既有重计算端点的口径纳入 `JYOTISH_HEAVY_COMPUTE_CONCURRENCY` 限流(默认 2,饱和 429)。CLI 子进程 20 秒超时要与限流口径对得上。
|
||||
|
||||
#### 3b · `/api/western`(回归黄道本命盘)
|
||||
|
||||
前端「西洋盘」Tab 需要,但**当前没有任何路由暴露它**:`scripts/western_chart_engine.py` 的 `build_tropical_natal_chart` 只在 `/api/consult` 内部被 `_western_evidence_packet_from_body` 间接调用(见 `jyotish_api_server.py:2205`)。
|
||||
|
||||
- 新增 `elif path == '/api/western':`,薄注册直接调 `build_tropical_natal_chart`(**不要**走 `build_tropical_western_evidence_packet`,那条带 `route_packet` 是咨询链概念)。
|
||||
- `house_system` 从请求读,默认 `P`(Placidus),回写进响应。
|
||||
- 响应必须显式带坐标系标注(`zodiac: "tropical"` 引擎已有,再加一条人类可读的边界句),并写明与恒星黄道的岁差差值。
|
||||
- handler 函数体 ≤ 8 行。
|
||||
|
||||
#### 3c · `/api/ephemeris_events`(区间换座与停滞)
|
||||
|
||||
前端「未来九十天」需要。`/api/transit` 返回的是**对本命的触发点**(`transit_trigger.search_all_transit_triggers`,见 `jyotish_api_server.py:9947` 一带),**不是换座与顺逆停滞**,不能复用。
|
||||
|
||||
- 新增模块 `scripts/ephemeris_events.py`(**计算主体放这里,不进 api_server**):给定起止日期、岁差、交点模式,逐日扫描并返回 `ingress`(进入星座)与 `station`(停滞转顺 / 转逆)两类事件。
|
||||
- 区间上限与既有过境端点对齐(`transit search range must be <= 730 days`,见 `jyotish_api_server.py:9929`),超限返回 400。
|
||||
- 岁差必须按请求参数走,**不得写死**(附录里那条 panchanga 写死 Lahiri 的教训就在这)。
|
||||
- 新增 `elif path == '/api/ephemeris_events':`,handler 函数体 ≤ 8 行。
|
||||
- 参考实现口径(立单时用 pyswisseph 实跑过,`1990` 那组资料 2026-09-15 起 90 天、Raman、六星体得到 **14 条**事件):逐日取黄经与速度,星座号变化记 ingress,速度符号翻转记 station。
|
||||
|
||||
**验收标准**
|
||||
|
||||
- `.venv/bin/python -m pytest tests/test_api_server_growth_contract.py` 通过。
|
||||
- 新增 `tests/test_qizheng_api_productization.py`(上游同名文件可参考,但断言按本仓合同重写):正常请求返回 11 曜 12 宫;缺 `lat` / `lon` 返回 400 而非 500;node 不可用时返回结构化错误。
|
||||
- 新增 `tests/test_readonly_chart_endpoints.py`:`/api/western` 返回十一项行星 + 十二宫 cusp + 相位 + 元素模式分布,`house_system` 可覆盖并回写;`/api/ephemeris_events` 在 90 天窗口返回按日期升序的事件列表,两类事件都有,超过 730 天返回 400,岁差参数生效(同一区间 Raman 与 Lahiri 的结果不相同)。
|
||||
- 新增 `tests/test_ephemeris_events.py`:模块级单测,golden 来自真实引擎输出。
|
||||
- `curl -s -X POST http://127.0.0.1:5200/api/qizheng -d '{"year":1990,"month":4,"day":9,"hour":13,"minute":24,"lat":31.19,"lon":121.44,"tz":8}'` 的输出贴进进度记录(脱敏不需要,示例资料是虚构的)。
|
||||
|
||||
### 任务 4 · Bug 记录
|
||||
@@ -181,16 +218,19 @@ node CLI --date "1990-04-09T13:24:00" --lat 31.19 --lng 121.44 --pillars -
|
||||
- **BUG-701**|计都派别(`ketuMode: apogee`)未暴露为参数。状态 `resolved`(任务 2 参数化后)。
|
||||
- **BUG-702**|适配器 boundary 文案把空的神煞与未闭合的庙旺说成已生成。状态 `resolved`(任务 2 改写后)。
|
||||
|
||||
- **BUG-703**|`/api/panchanga_range` 岁差写死 Lahiri,与账户默认 Raman 不一致。状态 `investigating`(本轮只记录,不修,见附录)。
|
||||
|
||||
不得把姓名、出生资料、邮箱、用户/案例 ID、Cookie、JWT、密钥、完整请求体或模型原文写进去(AGENTS.md §5.6)。
|
||||
|
||||
## 让步顺序
|
||||
|
||||
资源不够时按这个顺序砍,**不得自行调整**:
|
||||
|
||||
1. 先保证 任务 1 + 任务 2 + 任务 4。没有镜像 Node 与适配器,这一单等于没做。
|
||||
1. 先保证 任务 1 + 任务 2 + 任务 3a + 任务 4。没有镜像 Node 与适配器,这一单等于没做。
|
||||
2. 任务 3 的限流接入可以延后,但路由与行数上限不能延后。
|
||||
3. `raw_engine_output` 的审计字段可以先不落地,改为进度记录里说明。
|
||||
4. **不可让步**:许可证归档(Apache-2.0 §4)、`vendor/**` 进 gated-paths、庙旺不得进结论、`--pillars` 不得调用、api_server 行数上限。
|
||||
3. 3b 与 3c 若来不及,可以拆到本单的第二次推送,但**必须仍由本单交付**——不得让前端单去改 `scripts/jyotish_api_server.py`。延后时要在进度记录里写明,并通知两份前端单:对应区块先渲染「该数据源尚未上线」。
|
||||
4. `raw_engine_output` 的审计字段可以先不落地,改为进度记录里说明。
|
||||
5. **不可让步**:许可证归档(Apache-2.0 §4)、`vendor/**` 进 gated-paths、庙旺不得进结论、`--pillars` 不得调用、api_server 行数上限。
|
||||
|
||||
## 开工前置命令
|
||||
|
||||
@@ -221,11 +261,19 @@ done
|
||||
|
||||
## BUG 编号起点
|
||||
|
||||
`origin/staging` 上 `docs/BUG_HISTORY.md` 的当前最大号是 **BUG-699**。本单从 **BUG-700** 起,开工时必须重新核对一次(可能已被别的会话占用)。
|
||||
`origin/staging` 上 `docs/BUG_HISTORY.md` 的当前最大号是 **BUG-699**。
|
||||
|
||||
三份并行单**预分配编号段**,各自不得越界;开工时仍要重新核对当前最大号(可能已被别的会话占用,占用了就整体后移并通知另外两单):
|
||||
|
||||
| 单 | 编号段 |
|
||||
| --- | --- |
|
||||
| 本单(七政 + 只读端点) | **BUG-700 ~ 703** |
|
||||
| `TASK-chart-page-20260915` | BUG-704 ~ 706 |
|
||||
| `TASK-ephemeris-page-20260915` | BUG-707 ~ 709 |
|
||||
|
||||
## 附:本轮发现但不在本单范围
|
||||
|
||||
立单期间实跑我们自己的引擎时发现一条与七政无关的口径不一致,**不要在本单修**,但请在本单的 Bug 记录里补一条 `investigating` 占位,避免丢失:
|
||||
立单期间实跑我们自己的引擎时发现一条与七政无关的口径不一致,**不要在本单修**,但请在本单的 Bug 记录里记成 **BUG-703 `investigating`**,避免丢失:
|
||||
|
||||
- **panchanga 岁差与账户设置不一致**。`/api/panchanga_range` 的响应里 `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`。
|
||||
|
||||
Reference in New Issue
Block a user