feat(report): render longform Markdown as the report and close gaps2 holes

New reports skip the writer, persist pl9 Markdown as the body, and settle zero-token usage on the catalog model. Planned longform sections now emit blocked rows instead of vanishing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-06 21:46:16 +08:00
parent e428e9d056
commit cfcd369d4f
36 changed files with 2290 additions and 402 deletions
+32
View File
@@ -8629,4 +8629,36 @@
- 复发自:BUG-553`a1956deb` 的测试没断言守卫会命中)
- 修复版本:`0102a973`
## BUG-561 | 长报告 KP Lord/Sub 与专题 KP 表在失败信封下整段消失
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:`scripts/jyotish_engine.py` pl9 长报告、专业参考 Markdown
- 用户现象:三年 KP 月表还在,但 KP Lord/Sub 原始表和事业/财运/关系章的 KP 结构小表整段不见,页面上也没有 blocked 说明。
- 触发条件:专业参考路径把 `cmd_kp` 收成 `{status: blocked, reason}` 且没有 planets/houses;渲染层遇到该信封直接返回空列表。鉴别条件是候选窗/blocked KP 信封,不是 Raman 本身。
- 根因:`_kp_lord_sub_section` 等装配函数在 KP 模块失败时 `return []`,计划段被静默跳过。
- 修复:失败路径改为输出显式 blocked 标题和原因码;最终 Markdown 再扫一遍计划标题,缺席则补 `planned_section_unavailable`
- 验证:`tests/test_report_longform_gaps2.py`
- 防复发:计划内段必须 present 或 blocked,不允许缺席。禁止再对失败 KP 信封直接 `return []`
- 相关记录:无
- 复发自:无
- 修复版本:待本单合入 SHA
## BUG-562 | 专业参考路径剥掉 raw_full_reading 后模块审计表只剩表头
- 状态:resolved
- 首次发现:2026-09-06
- 最近更新:2026-09-06
- 影响面:`scripts/jyotish_engine.py` 长报告审计表、力量章 Yogini/Narayana/Kala/Bhava Bala
- 用户现象:逐模块执行审计只有表头、零数据行。力量章缺 Bhava Bala 与三套辅助大运明细。
- 触发条件:professional 路径 sanitize 掉 `raw_full_reading`worksheet 只抄了 dasha 家族状态、没有 periods。
- 根因:审计行只从将被剥离的 raw 模块读;辅助大运渲染拿不到规范化 periods。
- 修复:sanitize 前抽出 `_iter_audit_modules`;规范化 dasha family periods,并从本地 yogini/narayana/kala/bhava_bala 模块补装,缺数据走 blocked 行。
- 验证:`tests/test_report_longform_gaps2.py`
- 防复发:full pack 下审计表数据行必须达到模块执行数下限;力量章六子块缺数据必须有 blocked 标题。
- 相关记录:BUG-561
- 复发自:无
- 修复版本:待本单合入 SHA
@@ -0,0 +1,33 @@
# PROGRESS · 长报告对齐二轮 gaps22026-09-06
工作树:`.worktrees/report-md-page-20260906`(与 MD 详情页串行)
分支:`codex/report-md-page-20260906`
任务书:`TASK-report-longform-gaps2-20260906.md`
## 任务 0 根因
专业参考路径可以把 `cmd_kp` 收成 `{status: blocked, reason: ...}` 且没有 `planets` / `houses`。渲染层 `_kp_lord_sub_section` 与专题 KP 表遇到该信封曾直接 `return []`,计划段无声消失。三年 KP 月表不走同一信封,所以仍在。
触发变量是 **API 候选窗 / blocked KP 信封**,不是 Raman 本身。同构虚构盘(候选窗 + raman + full pack)在单元层用 blocked KP 信封复现;本轮未把真实用户盘入库。
## 任务状态
| 任务 | 状态 | 说明 |
| --- | --- | --- |
| 0 根因 | 完成 | 见上;BUG-561 |
| 1 KP + 静默跳过禁令 | 完成 | blocked 标题;最终 heading-ensure |
| 2 审计表零行 | 完成 | `_iter_audit_modules` 在 raw 剥离前抽出;BUG-562 |
| 3 力量章六子块 | 完成 | Bhava Bala 规范化;Yogini/Narayana/Kala periods 从本地模块补装 |
| 4 年度回放按年展开 | 完成 | `_annual_series_surface` 保留 replay 键;缺年 blocked |
| 4b 时间精度合同 | 完成 | 四粒度:day blockedweek/month parameter_sensitiveyear 有年度系列则 partial_verified |
| 4c 出生 provenance | 完成 | `birthplace_label` / `coordinate_source=user_reported` / `time_source` / `uncertainty_minutes`;坐标来源保持 user_reported |
| 5 C 组小段 | 完成(blocked-or-present | 原始模块索引、Technique Audit、Vargas I 等走显式 heading;分钟排行等与全量 Agent 行数未做真实盘逐行校准 |
| 6 质量门 | 完成 | `tests/test_report_longform_gaps2.py` 与 timing/parity 相关切片 |
## 质量门
```bash
.venv/bin/python -m pytest tests/test_report_longform_gaps2.py tests/test_timing_precision_contract.py tests/test_report_longform_parity.py -q
```
未改 `.gitea/workflows/**`,不提升 main。
@@ -0,0 +1,50 @@
# PROGRESS · 报告详情页改渲染长报告 MD2026-09-06
工作树:`.worktrees/report-md-page-20260906`
分支:`codex/report-md-page-20260906`
基线:`origin/staging` `e428e9d0`
任务书:`TASK-report-md-page-20260906.md`(硬依赖 gaps2,已在同一 worktree 完成)
未改 `.gitea/workflows/**`,不提升 main。
## 开工回执
- 目标:新报告 worker 只生成 pl9 长报告 MD;详情页渲染该 MD;writer 停用保留;零模型用量必须结算。
- 顺序:gaps2 装配缺口 → MD-only 生成/计费 → 详情页 TOC/懒渲染 → 导出与报告中心 → 质量门。
- 最大风险:`complete_personal_report_job` 仍要合法 v2 封面文档;计费 `completeUsage` 必须接受 0 token,且不得把 `actualModelId` 写成 `unknown`
## 我替领导拍的板
- 计费:沿用 `report.full` 预留,结算 `inputTokens=0 outputTokens=0 costMicrousd=0``actualModelId` 用创建时的目录默认模型 id。缺少合法模型 id 时 **在报告标 ready 之前** 重试,避免预留悬挂。
- 封面文档:writer 不再跑;写入一份最小合法 v2 文档(各主题走 blocked disclosure),页面不渲染它。卡片摘要来自生成时对 MD「摘要」的截取,仍落在封面 `executiveSummary.summary`,列表 JSON 路径不变。
- 旧报告:无附录 MD 即显示「旧版本报告,请重新生成」,不维护五章视图。professional-reference POST 改为只读缓存,缺失返回 410。
- 导出:详情页用内存中的同一份 MD 下载;文件名 `个人报告-YYYY-MM-DD`。打印为次按钮。
## 任务状态
| 任务 | 状态 | 说明 |
| --- | --- | --- |
| gaps2 | 完成 | 见 `PROGRESS-report-longform-gaps2-20260906.md`BUG-561 / BUG-562 |
| MD-only 生成 + 计费 | 完成 | `PERSONAL_REPORT_WRITER_ENABLED=false`0-token settle |
| 详情页 TOC/懒渲染 | 完成 | `react-markdown` + `remark-gfm` + `skipHtml`;首屏导航+摘要;H2 懒渲染 |
| 导出 / 报告中心 | 完成 | 「导出报告(.md)」;一步式等待文案 |
| 端到端 staging | 待部署后 | 本机质量门先跑;staging 真实创建仍需部署 |
## 计费适配
`complete_usage` 已允许 `input_tokens >= 0`。生产 worker 在长报告路径上:
1. 先加载模型目录,没有默认模型则 `model_unavailable`(可重试),不跑引擎。
2. 生成成功后带 `actualModelId=catalog.defaultModelId`、0 token。
3. worker-core 若看到空/`unknown` 模型 id,在 `completeReportReady` 之前抛错,避免报告已 ready、预留永不结算。
## 首屏渲染
本地对含导航/摘要/宽表/诚实标签的样例 Markdown 做 `renderToStaticMarkup``frontend/tests/personal-report-longform-md.test.ts` 锁首屏 < 1500ms(本机实测约 30ms)。完整 ~250KB / 2700 行报告的浏览器首屏仍需 staging 部署后补测;H2 分段懒渲染后首屏只挂载导航+摘要。
## 质量门命令
```bash
.venv/bin/python -m pytest tests/test_report_longform_gaps2.py tests/test_timing_precision_contract.py tests/test_report_longform_parity.py -q
cd frontend && ./node_modules/.bin/tsc --noEmit
cd frontend && ./node_modules/.bin/tsx --test tests/personal-report-longform-md.test.ts tests/personal-report-view.test.ts tests/personal-report-export.test.ts tests/personal-report-entry.test.ts tests/report-polling-contract.test.ts tests/professional-report-reference-route.test.ts tests/personal-report-worker.test.ts tests/personal-report-api.test.ts
```