Files
Jyotisha/docs/tasks/TASK-readonly-pages-fix-20260916.md
T
Jesse_ChenandClaude Opus 5 fadb64fbcf docs(tasks): accept the three read-only page briefs and file the fixes
Re-ran the checks independently rather than taking the progress notes at
their word. Most red lines hold: the api server sits at 11334 of 11363,
vendor/** is gated, the image installs nodejs and copies vendor, NOTICE
carries the Apache-2.0 attribution, the forbidden CLI subcommands are
blocked, neither new BFF imports billing or mastra, vedic-chart-svg did
not move, tsc is clean and lint has no errors.

Three findings. The qizheng adapter validates ketu_mode and
sidereal_mode, echoes them into calculation.*, and never passes them to
the engine: asking for descending-node returns 200 with a chart still
computed on the apogee school and no warning. The ephemeris brief
asserted that app-sidebar must not mention /ephemeris, which contradicts
the chart-page brief that was told to add both entries, so staging is
red. The ephemeris_events golden compares full-precision floats and
regenerates itself when missing.

Staging also still reports deployment.gitCommit 2d7698ea, six commits
behind, with gated paths changed in between.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
2026-09-15 09:35:49 +00:00

12 KiB
Raw Blame History

TASK-readonly-pages-fix-20260916 · 三份只读页单的验收修复

基线

  • 验收对象:origin/staging = 4161222b,含 25851dd3 + e776cf9d + f7386e07qizheng 单)、830799fachart-page 单)、d3a2c48bephemeris-page 单)。
  • 验收人:Claude(独立复跑,未采信进度记录的自述)。
  • 三份原单的绝大多数红线已通过,详见「已通过项」。本单只处理未通过项。

验收结论摘要

结论
后端红线(行数 / gated-paths / Dockerfile / NOTICE / 禁用子命令) 通过
前端红线(不碰 page.tsx / 不搬 SVG / 不扣点不调模型 / 无 spinner / tsc 0 / lint 0 error 通过
派别参数化 未通过 · P1BUG-710
staging 测试是否全绿 未通过 · P1BUG-711)、P2BUG-712
staging 是否已部署 未达标,见「部署缺口」

未通过项

P1 · BUG-710ketu_mode / sidereal_mode 收了请求但从不传给引擎

scripts/qizheng_chart_engine.py_run_cli():172-181)把命令行写死成 --date / --lat / --lng / --seven-governors / --json请求里的派别参数一个都没传下去。 而 _normalize():245-248)把请求值写进 calculation.ketu_mode,把实际值另放在 calculation.engine_ketu_mode,两者不一致时既不警告也不拒绝。

验收实测(虚构资料 1990-04-09T13:24:00+08:00 / 31.19N 121.44E,请求 ketu_mode: "descending-node"):

请求 ketu_mode      : descending-node
引擎实际 ketu_mode  : apogee
计都实际位置        : 亢 15.46      ← 仍是月孛派
月孛实际位置        : 亢 14.23
罗睺实际位置        : 女 5.09       ← 对点应在此处附近,并非计都所在
有无 warning 字段   : 无

调用方读 calculation.ketu_mode——这是最自然的那个字段——拿到的是自己要的值,不是算出来的值。这比没有这个参数更糟:它让派别看起来被采纳了。

原任务书决策记录 4 写的是「不接受引擎默认静默生效」,实现成了「静默不生效但假装生效」。

原单的测试没兜住这条:tests/test_qizheng_chart_engine.py:50 的 live 用例只断言了默认值 result["calculation"]["ketu_mode"] == "apogee"),没有任何用例请求非默认派别。

要做什么(二选一,由执行方按引擎实际能力定,并在进度记录里说明选了哪条):

  • A(首选):确认 vendored 引擎能接受派别入参(库函数 getSevenGovernorsChart 的 options,而非 CLI 旗标),把 ketu_mode / sidereal_mode 真正传下去,并断言回读的 engine_ketu_mode 等于请求值。
  • B(引擎确实不支持时):非默认派别一律 400 拒绝,错误码 ERR_QIZHENG_KETU_MODE_UNSUPPORTED,并在 boundary 里写明本引擎固定 apogee 派。不得继续接受并回写。

无论 A 还是 Bcalculation.ketu_mode 必须只表示实际生效的派别

验收标准

  • 新增用例:请求 ketu_mode: "descending-node" 时,要么计都位置随之改变且 engine_ketu_mode 跟随(走 A),要么返回 400(走 B)。
  • 新增用例:calculation.ketu_modecalculation.engine_ketu_mode 永不出现不一致的 200 响应。
  • sidereal_mode 同样处理,同样加用例。
  • 前端 frontend/src/lib/chart-view-mapper.ts:602ketuMode 映射跟着改到实际生效字段。

P1 · BUG-711|星历单的守卫与星盘单的侧栏入口互相矛盾,staging 现在是红的

frontend/tests/ephemeris-page.test.tsx:70

assert.doesNotMatch(readFileSync(".../app-sidebar.tsx", "utf8"), /ephemeris/);

TASK-chart-page-20260915 任务 3 明确要求 chart-page 单同时加「星盘」「星历」两个入口,它也照做了(app-sidebar.tsx:236/241)。两单合入后这条断言必然失败:

The input was expected to not match the regular expression /ephemeris/. Input:
  '    router.push("/ephemeris");\n' +
  '              isActive={pathname === "/ephemeris" || pathname.startsWith("/ephemeris/")}\n'

tests/ephemeris-page.test.tsx 单跑:5 pass / 1 fail

根因是星历单把「本单不改 sidebar」写成了「sidebar 里不得出现 ephemeris」。前者是文件归属,后者是产品事实——两者不是一回事。

要做什么:把第 70 行的断言改成只约束本单的文件归属,例如断言 git diff 里本单没改过 app-sidebar.tsx,或直接删掉这一行(归属已由前两行 + 任务书保证)。不得反过来删掉 sidebar 的星历入口。

验收标准

  • npx tsx --test frontend/tests/ephemeris-page.test.tsx 全绿。
  • 侧栏「星历」入口仍在,三态(桌面 288 / 平板 64 / 手机抽屉)可达。
  • 改断言要按 AGENTS.md §7.3 写「原值 / 新值 / 原因」三栏。

P2 · BUG-712ephemeris_events 的 golden 存了全精度浮点,跨机器不稳

tests/golden/ephemeris_events_raman_20260915_90d.jsonspeed_longitude 存成全精度浮点, tests/test_ephemeris_events.py:27 直接 assert result["events"] == golden 做整体相等比较。

验收机(Linux / Python 3.13 / 仓库 .venv)实跑:

At index 2 diff: ... 'speed_longitude': 1.42928209  !=  ... 'speed_longitude': 1.4292821

golden 生成在执行方本机(Windows / Anaconda 3.11.7)。同一天、同一区间、同一岁差,事件的种类、日期、星体、星座全部一致,只有浮点尾数差一位。

这不是计算错,是 golden 的比较口径太严——任何与生成机 pyswisseph / 星历数据不完全一致的机器都会红,包括 CI。

tests/test_ephemeris_events.py 还有一处更该修的:第 24-25 行在 golden 不存在时自动写出 golden。这会让「golden 丢失」静默变成「golden 重新生成并通过」,等于没有 golden。

要做什么

  • 比较时对浮点做量化:speed_longitudelongitude 各保留固定小数位(建议 6 位,与仓库既有坐标精度口径一致)后再比。事件的 kind / date / body / from_sign / to_sign 保持严格相等。
  • 删掉自动写 golden 的分支;golden 缺失应当失败并提示用专门的生成脚本重建。

验收标准

  • .venv/bin/python -m pytest tests/test_ephemeris_events.py 在验收机与执行方机器上都全绿。
  • golden 文件被删除时该测试失败,不是自动重建。

部署缺口(不在本单实现范围,但必须一起解决)

https://staging.jyotisha.chat/api/healthdeployment.gitCommit = 2d7698ea 而 staging HEAD 是 4161222b,中间有 6 个提交且含门禁路径改动scripts/**frontend/**deploy/**vendor/**)。

按 AGENTS.md §2.4,这就是未部署。BUG-711 / BUG-712 两条红测试很可能就是门禁没过的原因;此外 frontend/tests/chart-view-route.test.ts:150assert.ok(newlines <= 1951) 也是红的——page.tsx 现在 1964 行。

那条 page.tsx 超限不是本批三单造成的git log -- frontend/src/app/page.tsx 显示唯一改动者是 f51e494c fix(chat): keep rectification sessions findable...+14/1),另一个会话已在 4161222b 立了修复单。本单不处理它,但修完 711 / 712 后若门禁仍红,请先确认那一单是否已落地。

验收标准:本单推送后,deployment.gitCommit 等于最近一次含门禁路径改动的 staging 提交。

已通过项(复跑确认,不必重做)

qizheng 单

  • scripts/jyotish_api_server.py 11334 行,上限 11363(净增 21,任务书允许 ≤32)。tests/test_api_server_growth_contract.py 通过。
  • deploy/gated-paths.txt:29vendor/**deploy/railway-api.DockerfilenodejsCOPY vendor ./vendor
  • NOTICE 齐备:包名、0.8.0、Apache-2.0、Copyright、上游地址。
  • FORBIDDEN_CLI_FLAGS 覆盖 --pillars / --luck / --polaris / --qimen / --liuren / --chuanren,调用处主动校验。
  • coordinate_system = qizheng_mansion_degrees_from_jiaoboundary 无「神煞」;dignities.status = unclosedmay_enter_conclusions = falseruntime_promotable_count = 0
  • 定向测试:test_api_server_growth_contract / test_qizheng_chart_engine10/ test_qizheng_api_productization4/ test_readonly_chart_endpoints525 passed
  • BUG-700703 已落 docs/BUG_HISTORY.md

chart-page 单

  • 独立 route frontend/src/app/chart/page.tsxpage.tsx 未被本单改动(唯一改动者是 f51e494c)。
  • vedic-chart-svg.tsx 未搬家,导出签名零改动;引用方从 3 个变 4 个(新增 chart-page/chart-vedic-tab.tsx)。
  • frontend/src/app/api/chart-view/route.ts 不 import consultation-billing、不 import @/mastra
  • 侧栏「星盘」「星历」两个入口齐备(app-sidebar.tsx:224-241)。
  • chart-page-view.test.tsx 7/7 通过
  • frontend/DESIGN.md 新增「§15 只读星盘页」。

ephemeris-page 单

  • 独立 route /ephemeris;不碰 page.tsx、不碰 app-sidebar.tsx
  • BFF 不 import 计费与 mastra。
  • 五要素段有 Lahiri 标注(ephemeris-page.tsx:154),行运段没有——与任务书一致。
  • 全页无 spinner / 骨架 / 「正在加载」。
  • 逐词检查无运势判断宜 / 忌 / 吉日 / 运势 / 建议 / 有利于 / 不利于 / 会带来 均无命中;不利时段 是引擎原标签,属照搬)。
  • BUG-707 已落 docs/BUG_HISTORY.md
  • frontend/DESIGN.md 新增「§16 星历页」。

全局

  • ./node_modules/.bin/tsc --noEmit 0 错
  • npm run lint 0 error119 warning 全部是既有的)。

环境缺口(验收机做不了,不算未通过)

说明
Node 版本 验收机 node v20.19.2,仓库标准是 Node 22deploy/railway-web.Dockerfile:1.gitea/workflows/* 均为 node:22)。frontend/tests/ephemeris-route.test.tsmock.module(Node 22+),在验收机上 3 条子进程用例退出码 1,无法判定通过与否。请执行方在 Node 22 上复跑并把结果贴进进度记录。
Docker 验收机无 Docker:API 镜像构建、容器内 node --versionCOPY vendor 是否真的进镜像,全部未验证。镜像体积变化未知。
登录态与 Chrome 两个新页面的浏览器级走查未做,留给 docs/testing/

让步顺序

  1. BUG-711 优先:它是 staging 变红的直接原因之一,改动最小(一行断言)。
  2. 其次 BUG-712(测试口径)。
  3. 最后 BUG-710(派别参数)。它目前没有用户可见影响——/api/chart-view 不传派别参数,所以请求值恒等于默认值——但接口一旦被别处调用就会误报,不能留。

不可让步:不得靠删掉 sidebar 星历入口来让 711 变绿;不得靠放宽 dignities 或 boundary 口径来省事;不得调高 jyotish_api_server.py 的行数契约。

开工前置命令

cd /workspace/Jyotisha
git status -sb
git fetch origin --prune
git worktree add -b codex/readonly-pages-fix-20260916 \
    .worktrees/readonly-pages-fix-20260916 origin/staging
cd .worktrees/readonly-pages-fix-20260916
node --version                                   # 必须是 22.x,否则 mock.module 用例跑不了
python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45
.venv/bin/python -m pytest tests/test_ephemeris_events.py -q
cd frontend && npx tsx --test tests/ephemeris-page.test.tsx tests/chart-view-route.test.ts

BUG 编号起点

origin/staging 当前最大号 BUG-709700703 qizheng、704706 与 708709 被另一会话的校正单占用、707 星历)。本单从 BUG-710 起,开工时重新核对。

进度与记录

  • 进度记录:docs/tasks/PROGRESS-readonly-pages-fix-20260916.md
  • 三条 BUG 必须在同一变更内写进 docs/BUG_HISTORY.md,并关联原单。
  • 环境缺口写 BLOCKED.md
  • 索引:docs/tasks/README.md 追加本单,并把三份原单的状态从「待验收」改成「已验收(带修复单)」。