fix(report): draw North Indian charts from fences after skipHtml dropped SVG

Longform report pages kept the D1/D9/Moon and varga headings but skipHtml
stripped the engine's inline SVG. Emit a jyotish-chart JSON fence beside
each SVG and render a diamond chart on the reader without relaxing HTML
sanitization. BUG-607.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-09 15:11:00 +08:00
co-authored by Cursor
parent d5972ef44a
commit 2fdcb14fd6
23 changed files with 2628 additions and 124 deletions
@@ -0,0 +1,92 @@
# PROGRESS · 长报告页渲染全部星盘(2026-09-09)
- 工作树:`.worktrees/report-chart-render-20260909`
- 分支:`codex/report-chart-render-20260909`track `origin/staging`
- 基线:`origin/staging` @ `d5972ef4`(任务书 `54adb0d1` 之后的纯文档提交,含本任务书本身)
- BUG**BUG-607**
- 未改 `.gitea/workflows/**`,不提升 `main`,未 push
## 做了什么
引擎继续输出原来的南印度 `<svg>`(导出 / `run_all.py` 仍靠它)。每张成功的 SVG 旁边追加一段 `jyotish-chart` 围栏 JSON。报告页继续 `skipHtml`,只认围栏;zod 校验后用北印菱形组件自绘。导出 `.md` 前剥掉围栏。
| 任务 | 状态 | 说明 |
| --- | --- | --- |
| 1 引擎围栏 | 完成 | 新模块 `scripts/report_chart_block.py``_render_south_chart` 成功路径拼接围栏,失败路径不加 |
| 2 前端围栏→组件 | 完成 | `report-chart-block.ts` + markdown `pre` 渲染器 + 下载剥块 |
| 3 北印几何 | 完成 | `north-indian-chart-geometry.ts``VedicChartSvg` 换成任务书顶点表;宫内最多 6 行,超出 `+N` |
| 4 SVG 中心标签 | 完成 | D1 仍是 `Rasi Chart / (D1)`;其它跟标题里的 `D\d+` / `Moon` |
| 5 记录 | 完成 | BUG-607、CHANGELOG、本文件、走查清单;状态板改「待验收」 |
没有砍让步项。两列网格做了:`## 摘要` 是 eager lead,图都在 `.personal-report-md-lead` 里,所以 720px 规则同时打在 lead 和 section 上。
## 口径(与任务书一致)
- 北印式、全部 22 张、前端自绘。
- 宫内 `日 12°` / `土逆 3°`;角上星座序号 112。
- 图下不加第二行标题;`aria-label` 只用校验过的 `id``D1 北印度盘` / `月亮参考盘`),不用 JSON 里的 `title`
- `skipHtml` / `disallowedElements` / `urlTransform` 未改;无 `rehype-raw`
- `scripts/jyotish_api_server.py` 未增行。
- `tests/test_report_chart_block.py` 已列入 `CORE_PYTEST_TARGETS`staging 自动门会跑这 22 块围栏。
## 偏离
1. **D1/D9 传给 SVG 的 `title` 括号从半角改成全角。** `####` 标题本来就是 `(本命盘)` / `(婚盘)`;旧代码只在 `_render_south_chart` 的 title 参数上用了半角 `(本命盘)`。改成全角是为了让围栏 JSON 的 `title``####` 一致。用户看见的标题文案没变。
2. **两列用 float 配对 heading+figure,没有包一层 `.personal-report-chart-grid`。** 大纲不能改,没法给 19 张图单独包 grid。退役的 document view 仍用原来的 `.personal-report-chart-grid`
3. **`package.json``npm test` 加上 `tests/*.test.tsx`。** 否则新的 markdown-view 测试进不了全量门。当前 `tests/` 里只有这一份 `.tsx` 测试。
## 测试数字
开工未跑全量 `npm test`(任务书 §7 的基线命令在独立 worktree 上因时间放到收工一次跑)。对照同日 BUG-601 收工记录:2945 tests / 2903 pass / 28 fail / 14 skipped(当时无 Docker)。本轮 Docker 可用,跳过数为 0。
| 项 | 本轮实测 | 结论 |
| --- | --- | --- |
| `tests/test_report_chart_block.py` | 7 passed | 22 围栏 == 22 `<svg>`;D1 上升=core;逆行=本命;地名不进块;D9 SVG 含 `(D9)` 不含 `(D1)`;本文件在 quick 门清单里 |
| `tests/test_full_report_quality_gate.py` + longform parity/gaps2 | 先前本 worktree 已绿 | 标题 / SVG 数量门未破 |
| `run_quality_gate.py --profile quick` pytest | 649 passed, 1 skipped | Python 段通过 |
| `run_quality_gate.py``npm test` | 失败(与独立全量撞车) | 权威数字以下面独立 `npm test` 为准 |
| `tsc --noEmit` | 0 错 | 通过 |
| `npm run lint` | 0 error / 108 warning | warning 数与 BUG-601 相同;新文件无 error |
| `npm test` tests | 2975 | 高于 2945 |
| `npm test` pass | 2971 | 高于 2903 |
| `npm test` fail | 4 | 全是 Docker Postgres fixture2 条抢槽超时、2 条 migration failed。与图盘无关 |
| `npm test` skipped | 0 | Docker 可用后不再跳过 DB 套件 |
| `globals.css` gzip | HEAD `35423` B → 工作树 `35760` B | +337 B / +0.95%,在 ±2% 内 |
| `next build` `/` | `○ Static` | webpack 构建(worktree 的 `node_modules` 是指向主仓的符号链接,Turbopack 拒绝跨根;CI 是实体目录,不受影响) |
| 首页 CSS gzip(构建产物) | `static/css/*.css` 最大块 35,779 B | 与源码 gzip 同量级 |
| 报告页 chunk gzip | `app/reports/[reportId]/page-*.js` = 10,772 B | 含 `jyotish-chart`;首页 `app/page-*.js` 不含 |
新增前端测试 9 条,全绿:
- `frontend/tests/report-chart-block.test.ts` 8 条(golden 22 块、整宫、逆行标签、毒化 title、坏 JSON、剥围栏、多边形面积/不重叠)
- `frontend/tests/personal-report-markdown-view.test.tsx` 1 条(组件 SVG + `role="img"`,无引擎 `viewBox="0 0 420 480"`,坏块「图盘数据无效」且无 `<script`
`personal-report-view.test.ts` 密宫断言三栏:
| 原值 | 新值 | 原因 |
| --- | --- | --- |
| `transform="translate(0 100)"` | `points="200,0 300,100 200,200 100,100"` | 1 宫改为上中菱形 |
| `transform="translate(300 0)"` | `points="400,400 300,300 400,200"` | 9 宫改为右下三角 |
| `+5 项` | `+7` | 最多 6 行(5 条 + 折叠行),12 个占星体剩 +7 |
golden 来源命令(虚构出生 `1990-06-15 12:00`,地名 `FictionalHarborTownship`,不进块):
```text
.venv/bin/python -m pytest tests/test_report_chart_block.py -q
# 路径:_compute_chart_from_args → cmd_varga_full → moon_chart
# → build_professional_report_reference_packet → render_pl9_markdown
```
4 条独立 `npm test` 失败清单(均非本轮图盘代码):
1. `database-backup.test.ts` — 等 Postgres fixture 槽超时(并行 compose 网络上限 2)
2. `database-billing-product-admin.test.ts` — 同上
3. `database-personal-report-sections.test.ts``database migration failed`
4. `database-self-hosted-identity.test.ts``database migration failed`
## 没做的
- **没有在浏览器里打开过一份真实 `/reports/<id>`。** 无登录态。走查清单在 `docs/testing/report-chart-render-20260909.md`
- 未跑 `npm run test:db` 单独套件(已含在全量 `npm test` 的 database-* 里)。
- 未在本 worktree 用 Turbopack 跑 `npm run build`:符号链接 `node_modules` 会 panic。改用 `next build --webpack``/` 仍是 Static。
- 未 push。代码进 staging 会走 `Staging Backend Quality Gate`(本轮不是纯文档)。
+1 -1
View File
@@ -118,7 +118,7 @@
| `TASK-report-md-page-20260906.md` | `PROGRESS-report-md-page-20260906.md` | 长报告 Markdown 直接作为报告页 | 已合入 | `cfcd369d` / `809bdf13`BUG-563 lint 随后修) |
| `TASK-report-list-500-20260907.md`(仓库根) | `PROGRESS-report-list-500-20260907.md` | 列表 PostgREST JSON 路径 500 | 待验收 | `b466a6fc`BUG-574 |
| — | `PROGRESS-report-progress-20260909.md` | 生成等待屏只有 spinner 与秒表:后端 `progressPercent` / `progressPhase` 与分章行已产出,前端解析后一字未渲染,且分章行在 `generating` 时根本不出服务端。改为按章分格进度条 + 章节清单,停滞 90 秒改「用时较长,仍在写」;不画百分比条、不做插值动画、不报预计剩余 | 已验收 | `848e39e6``5565b632`BUG-601 |
| `TASK-report-chart-render-20260909.md` | | 报告页所有分盘标题下空白:引擎 22 张 `<svg>``skipHtml` 丢弃(09-06 Markdown 直渲回归)。引擎旁加 ```jyotish-chart 围栏 JSON,前端 zod 校验后用自绘北印式组件全部渲染;不放开原始 HTML | 待领取 | BUG-607 |
| `TASK-report-chart-render-20260909.md` | `PROGRESS-report-chart-render-20260909.md` | 报告页所有分盘标题下空白:引擎 22 张 `<svg>``skipHtml` 丢弃(09-06 Markdown 直渲回归)。引擎旁加 ```jyotish-chart 围栏 JSON,前端 zod 校验后用自绘北印式组件全部渲染;不放开原始 HTML | 待验收 | `codex/report-chart-render-20260909`BUG-607 |
### 前端基础与工程