Unify reader cleanup rules, lock writer table guards, and register the exact fictional timestamp collision. Preserve existing ordinary-report safety contracts and source-data gaps. Validation: report Node 165/165, final safety 29/29, Python 101/101, Chrome 28/28; both PDFs retain all 130 rows. Full Node 3704 tests with the same 91 baseline failures. Privacy test: 62 passed, 1 failed due to 17 protected-file READ_ERRORs; not a green gate. Build, DB, manual checklist and controlled-login gaps remain documented. User explicitly authorized staging push with these gaps disclosed. Co-Authored-By: Claude Code <noreply@anthropic.com>
199 lines
16 KiB
Markdown
199 lines
16 KiB
Markdown
# TASK-report-density-20260922 · 个人报告信息密度与原始附录
|
||
|
||
## 基线 commit
|
||
|
||
- 基线:`origin/staging` = `f77a1b5a`(2026-09-22 拉取)
|
||
- 开发分支:`codex/report-density-20260922`,worktree `.worktrees/report-density-20260922`
|
||
- 对照物:用户在本机提供的一份对照报告 Markdown(不入仓,路径与文件名含个人信息,不在此记录),由**源头 skill 仓** `/workspace/yinduzhanxing` 的 `scripts/pl9_bilingual_batch_export.py` → `jyotish_engine.py` 生成。它对标商业软件 Parashara's Light 9 的 204 页报告。
|
||
|
||
## 执行状态(2026-09-23)
|
||
|
||
原实现 `bbd96d3b` 已提交并进入 staging,但原验收未通过;当前从 `0d37bec15` 在独立工作树执行 [验收修复单](TASK-report-density-fix-20260923.md)。实施与验证见 [进度记录](PROGRESS-report-density-20260922.md)。F3 新建真人清单被工具拒绝、文件仍缺,不能记为文档齐备;无受控浏览器或本轮部署通过结论。
|
||
|
||
范围更正:任务 3 的 Sade Sati 源数据只有阶段框架和当前状态,没有三轮起止日期;原“已逐项确认存在”的日期部分有误,新增日期计算按修复单明确列为范围外。下文保留原事故与规格历史,不作为该能力已完成的证据。
|
||
|
||
## 事故实证
|
||
|
||
### 一、对照物与我方同一组出生资料实测
|
||
|
||
用对照报告的同一组出生资料(真实个人资料,AGENTS.md §8 不得入仓;Raman / mean / target-year 2026)分别跑对照物与我方 `render_pl9_markdown`:
|
||
|
||
| 指标 | 对照报告 | 我方 staging 实测 |
|
||
|---|---:|---:|
|
||
| 字符 | 847,632 | 309,357 |
|
||
| 行数 | 13,287 | 3,559 |
|
||
| 表格行 | 8,044 | 1,478 |
|
||
| 含日期的表格行 | ~6,000 | 227 |
|
||
| 分盘数值表 | 22 张(D1–D60) | 20 张(带中文盘名) |
|
||
|
||
我方**已有**且质量不低于对照物的:20 张分盘全表、KP 四表(Lord/Sub、显著星 ABCD、Ruling Planets、Cuspal Promise)、Ashtakavarga 四类(BAV / SAV / Sodhita / Prastara / Pinda)、Shadbala 六分量 + Declination 中间量、Bhava Bala、Avastha、Upagraha + 特殊上升点 + 敏感派生点、Sahams、Yoga/Dosha、Vimshottari 主大运表、Yogini 周期表、Kala Chakra、Narayana、Chara、年度 Mudda / Patyayini / Saham / Monthly Windows / 三年流月星历。
|
||
|
||
我方**缺**的:Vimshottari AD / PD 子行(对照物约 4,500 行)、10 个替代大运族周期表(Sthira / Drig / Shoola / Tribhagi / Tribhagi-40 / Satabdika / Shattrimshatsama / Niryaana / Navamsha / Lagna-Kendradi)、Sade Sati 三轮表、Bhavesh 十二宫主逐宫段、逐 MD/AD/PD 解释长文。
|
||
|
||
### 二、产品页报告的契约上限(层 1 的真正瓶颈)
|
||
|
||
`frontend/src/lib/personal-report-contract.ts`:
|
||
|
||
- `CHART_IDS`(符号定位第 48 行)只有 9 种:`D1 D2 D6 D8 D9 D10 D11 D24 D30`
|
||
- `reportDocumentV2Schema.charts`(符号定位第 286 行)`.max(CHART_IDS.length)`,即上限 9 张
|
||
- `evidenceAppendixSchema.techniqueAudit` 第 170 行 `.max(100)`;`calculationEvidence` 第 172 行 `.max(100)`
|
||
- `REPORT_SECTION_KINDS`(`personal-report-plan.ts` 第 7 行)十种,**没有任何表格类型**
|
||
|
||
结论:上面第一节列出的"我方已有"的几十张表,在产品页报告里**没有可以落脚的字段**。它们只存在于引擎导出,不在用户打开的那份报告里。
|
||
|
||
### 三、原始附录的工程词泄漏
|
||
|
||
`scripts/professional_report_reference.py` → `build_professional_report_reference_packet` → `render_pl9_markdown` 的输出(312,050 字符)实测:
|
||
|
||
| 泄漏项 | 出现次数 |
|
||
|---|---:|
|
||
| 不同的 snake_case 标识符 | 925 种 |
|
||
| snake_case 总出现 | 3,797 处(占 3,559 行中的 1,408 行) |
|
||
| `parameter_sensitive` | 697 |
|
||
| `pyjhora_behavior_only` | 321 |
|
||
| `not_multiengine_parity` | 318 |
|
||
| `unresolved_external_tuple_boundary` | 192 |
|
||
| `cmd_full_reading`(内部函数名) | 68 |
|
||
| `PyJHora` / `JHora` | 35 |
|
||
| `PL9`(含"第 83-88 页"式页码) | 59 |
|
||
|
||
对照物同项扫描为 0。源头仓 `docs/research/pl9_reader_main_information_coverage_audit_2026_09_09.md` 记录他们已完成同类清理,用户版不出现 `blocked`、`parameter_sensitive`、`PyJHora`、`producer`、`schema`、`source_path`。
|
||
|
||
## 根因
|
||
|
||
1. **层 1**:报告契约是"结论 + 证据 id"的叙述式文档,`charts` 之外没有事实表字段。引擎算出的表无处可放,不是算不出来。
|
||
2. **层 2**:`render_pl9_markdown` 同时服务内部审计与对外附录两种用途,状态词、内部 producer 名与第三方产品页码是审计需要的字面量,直接外流。
|
||
3. **层 3(新发现的冲突)**:BUG-999(2026-09-22,同日)已建立 `frontend/src/lib/report-public-projection.ts`,按 allowlist 把普通报告投影成纯正文。其 `INTERNAL_KEY_TOKEN`(第 124 行)显式包含 `parameter_sensitive` **与中文「参数敏感」**,命中即整行剔除;`personal-report-longform-download.ts` 第 16 行已改为读 `GET /api/reports/:id` 并投影。该记录的防复发条款写明"专业参考不得再被普通下载当作 fallback"。
|
||
|
||
## 决策记录
|
||
|
||
产品负责人 2026-09-22 就三个问题拍板:
|
||
|
||
1. **层 1 表格清单按本文件第 1 项执行**(8 组表 + 分盘扩容)。
|
||
2. **层 2 原始附录允许直接给 C 端用户**,先全部显示,不分研究/付费档。
|
||
3. **B 类(引擎算得出但被自身标 blocked 的大运族)要显示出来**。
|
||
|
||
**本决策推翻 BUG-999 的两条红线,执行方按本节执行,不得以 BUG-999 为由拒改:**
|
||
|
||
- 推翻"专业参考不得再被普通下载当作 fallback":原始附录成为 C 端可下载产物,但必须是**独立入口**,不得替换 BUG-999 建立的普通正文投影。普通报告正文仍然只走 `projectOrdinaryReportDocument`。
|
||
- 推翻"`parameter_sensitive` / 参数敏感 命中即整行剔除"**仅限原始附录这一条通道**。普通正文的剔除规则不变。原始附录改为把状态词翻译成用户语言后保留,而不是删除整行——删除会让整张表少掉状态列,比留着更难读。
|
||
|
||
未授权、不得顺手做的:不调整任何 `assertionLevel` / `claimStatus` 的升级规则;不把附录里的替代大运族日期写进正文结论;不改 BUG-999 的 XSS / 危险 URL / `jyotish-chart` 围栏合同。
|
||
|
||
## 硬红线
|
||
|
||
1. 表格层是**服务端确定性装配**,不是 Skill writer 生成。`skills/jyotish-personal-report/SKILL.md` 的"不得计算星盘"约束不变:writer 不得撰写、改写、补全任何表格单元格。
|
||
2. 对照物的数字不得照搬。源头自己的台账 `docs/research/pl9_complete_replication_gap_matrix_2026_09_15.md` 列 5 项 open accuracy gap(3 项 P0:p33 declination/Kranti/KP cusp、p43-44 Shadbala 六分量、p61-120 Dasha 日期),`pl9_residual_loop_status_2026_09_18.md` 记 p109–p120 尚有 506 行 date mismatch、`production_tuning_allowed=false`、`formal_dasha_case_count=0`。替代大运族与 Shadbala 分量在源头仍未闭环,我方展示时必须保持既有状态标注,不得因为"对照物这么写"而升级。
|
||
3. 不得学对照物的双向解释模板("When supported… When afflicted…"两边都写)。
|
||
4. 不得动 `scripts/jyotish_api_server.py` 的类方法数与 `JyotishAPIHandler.__new__` 伪造点(AGENTS.md §6,`tests/test_api_server_growth_contract.py` 执行)。附录相关逻辑进 `scripts/` 独立模块。
|
||
5. 原始附录里不得出现内部函数名、第三方引擎名、第三方产品页码。
|
||
6. 出生资料不进仓:本轮所有实测用 `/tmp` scratchpad,任何真实出生数据不得写进测试、fixture、进度记录或 Bug 历史(AGENTS.md §8)。
|
||
|
||
## 任务分解
|
||
|
||
### 任务 1 · 原始附录工程词清洗(BUG-1003)
|
||
|
||
在 `scripts/` 新增独立模块,把 `render_pl9_markdown` 的对外副本清洗后再返回;内部审计路径与 CLI 保持原样。
|
||
|
||
替换范围:判断状态词(`parameter_sensitive` → 参数敏感、`pyjhora_behavior_only / not_multiengine_parity` → 仅单一外部参照,未做多引擎核对、`unresolved_external_tuple_boundary` → 外部边界未对齐、`partial_verified` → 部分核验、`raw_appendix_only` → 仅原始附录可见、`missing_in_local` → 本地暂无等)、内部标识符(`cmd_full_reading`)、第三方引擎名(`PyJHora` / `JHora` → 外部参照引擎)、第三方产品页码(`PL9 第 N 页` / `PL9 pN` / `PL9` → 外部参照资料)。
|
||
|
||
**保留**:原始字段名(`sign_cn`、`degree_in_sign`、`nakshatra_lord` 等)。附录的价值就是可以拿数字去和引擎对,改列名会让它更难核。
|
||
|
||
`blocked` / `executed` / `available` / `computed` 这类同时出现在英文散文里的词,只在"整个 markdown 单元格就是这个词"时替换,不得全局替换。
|
||
|
||
**验收标准**:
|
||
- 清洗后 `PyJHora`、`JHora`、`PL9`、`cmd_full_reading`、本文件列出的七个判断状态词,在对外副本中出现次数为 0;
|
||
- 行数不变(不得整行删除);
|
||
- 原始字段名一行不少;
|
||
- 英文散文句不被破坏(如 `Raw output is visible; this status alone does not promote it.` 原样);
|
||
- 定向测试覆盖上述每一条。
|
||
|
||
**参考实现已在本 worktree 落地**:`scripts/reader_appendix_language.py` + `tests/test_reader_appendix_language.py`,已接入 `scripts/professional_report_reference.py` 的 markdown 分支,`python3 -m pytest tests/test_reader_appendix_language.py tests/test_professional_report_reference_api.py -q` 为 29 passed。执行方可直接采用或重写,但验收标准不变。
|
||
|
||
### 任务 2 · 原始附录的 C 端独立入口(BUG-1004)
|
||
|
||
按决策记录第 2 条,给报告页加**独立**的原始附录下载入口,与 BUG-999 的普通正文导出并列,不替换它。
|
||
|
||
**验收标准**:
|
||
- 普通正文导出行为与 staging 基线逐字一致(`projectOrdinaryReportMarkdown` 的输出不变);
|
||
- 原始附录入口产出任务 1 清洗后的全文,表格行数与引擎导出一致(当前实测 1,478 行,不得因投影而减少);
|
||
- 入口文案对照 `frontend/docs/VOICE.md`,不写接口路径、状态码、"正在加载";
|
||
- `frontend/tests/report-public-projection.test.ts` 既有断言不得弱化;新增断言覆盖"附录通道不经过普通正文投影"与"普通通道仍然剔除内部词"。
|
||
|
||
### 任务 3 · 报告契约增加事实表层(BUG-1005)
|
||
|
||
`personal-report-contract.ts` 增加 `factTables`(服务端装配,与 `charts` 同级,不进 Skill writer 的可写范围);`personal-report-plan.ts` 增加对应 section kind 只用于排序与位置;`report-evidence-bundle-v2.ts` 携带表数据;`personal-report-generation.ts` 从 workflow packet 装配。
|
||
|
||
首批 8 组表(已逐项确认 packet 中存在,来源键见括号):
|
||
|
||
1. Vimshottari 主大运全表 + 当前主运下的 AD / PD(`timing_and_predictive_systems.dasha.timeline[].antardasha_timeline[].pratyantar_dasha_timeline`)
|
||
2. SAV / BAV(`strengths_and_scores.ashtakavarga`)
|
||
3. Shadbala 六分量(`strengths_and_scores.shadbala`)
|
||
4. 功能吉凶(`strengths_and_scores.functional_benefic_malefic`)
|
||
5. Avastha(`advanced_systems.avasthas`)
|
||
6. Upagraha + 特殊上升点 + 敏感派生点(`divisional_and_special_charts.upagrahas` / `special_lagnas` / `sensitive_points`)
|
||
7. Sade Sati 三轮(`advanced_systems.yogas_doshas.sade_sati`)
|
||
8. 当年 Varshaphala + Saham(`timing_and_predictive_systems.annual_tajika_pack`、`advanced_systems.sahams`)
|
||
|
||
**验收标准**:
|
||
- 每张表的每一行都能追溯到 packet 中的真实键,不得手造 fixture(AGENTS.md §7.4,合同测试 fixture 必须来自真实引擎响应);
|
||
- Skill writer 的 schema 不包含 `factTables`,writer 输出里出现表格视为 fail closed;
|
||
- 每张表带 `claimStatus`,取值沿用既有枚举,不新增、不升级;
|
||
- `tsc --noEmit` 0 错、`npm run lint` 0 error;
|
||
- 前端测试总数不低于开工时 `origin/staging` 实测。
|
||
|
||
### 任务 4 · 分盘扩容到 16 张(BUG-1006)
|
||
|
||
`CHART_IDS` 从 9 种扩到 16 种:`D1 D2 D3 D4 D7 D9 D10 D11 D12 D16 D20 D24 D27 D30 D40 D60`。引擎已全部排得出且带中文盘名(`Vargas I` 段实测 20 张)。`charts` 上限随 `CHART_IDS.length` 自动到 16。
|
||
|
||
**验收标准**:
|
||
- 16 张盘在报告页都能渲染,`VedicChartSvg` 不重叠(参照 BUG-616/617 的教训:`float` + 负 margin 曾让 22 张盘全部叠在一起);
|
||
- 滚动不触发整篇重建(BUG-617);
|
||
- `next build` 后 `/` 仍为 `○ Static`;首屏 gzip 变化在 ±2% 内,超出要在进度记录里给出原因;
|
||
- 既有 `REPORT_DOCUMENT_V1_CHART_IDS`(D1/D9/D10)的存量 v1 报告仍可读。
|
||
|
||
### 任务 5 · B 类大运族对用户可见(BUG-1007)
|
||
|
||
Yogini / Kala Chakra 的周期表在附录中已经出得来(实测 Yogini 完整周期表、Kala Chakra 可用);Ashtottari 因适用性规则不满足而 blocked,**这是正确行为,不得为了"显示出来"而绕过适用性判定**。Mudda 已返回年度阶段,Patyayini 为 `missing_in_local`。
|
||
|
||
**验收标准**:
|
||
- 附录中每个大运族都出现一行"是否适用 / 为什么不适用"的用户语言说明,不再只给一个状态词;
|
||
- 适用性判定本身不得放宽;
|
||
- 全部标注"第二时间轴,仅供交叉核对,不单独生成应期"。
|
||
|
||
## 让步顺序
|
||
|
||
资源不够时按此顺序砍,不得自行改变:
|
||
|
||
1. 先交任务 1 + 任务 2(原始附录清洗与入口)。这是用户今天就能拿到的东西,且修的是正在外流的工程词。
|
||
2. 再交任务 3 的前 4 组表(Vimshottari 全表、SAV/BAV、Shadbala 六分量、功能吉凶)。
|
||
3. 再交任务 4 的分盘扩容。
|
||
4. 任务 3 的后 4 组表(Avastha、Upagraha、Sade Sati、年度)与任务 5 最后。
|
||
|
||
任务 3 与任务 4 都改 `personal-report-contract.ts`,**必须串行**,顺序为 3 → 4。任务 2 与任务 3 都改报告页组件,任务 2 先。
|
||
|
||
## 开工前置命令
|
||
|
||
```bash
|
||
git fetch origin --prune
|
||
git status -sb # 确认分支,主检出常被别的会话切走
|
||
git worktree add -b codex/report-density-20260922 .worktrees/report-density-20260922 origin/staging
|
||
python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45
|
||
```
|
||
|
||
预检已知基线失败(与本轮无关,不得算作本轮引入):`tests/test_preflight_fragment_scan.py::test_preflight_fragment_scan_preserves_audit_capability_and_oracle_boundaries`,断言 `4 >= (0 + 8)`,由主检出的未跟踪残留文件导致。
|
||
|
||
## BUG 编号起点
|
||
|
||
`docs/BUG_HISTORY.md` 当前最大号 **BUG-1002**(共 832 条)。本任务书编号从 **BUG-1003** 起,开工时需重新核对最大号。
|
||
|
||
关联记录:**BUG-999**(普通报告导出仍带出内部技法与工作流字段)。本轮任务 1 与 BUG-999 属同一现象族——内部词外流。BUG-999 的解法是普通正文按 allowlist 投影,未覆盖 `/api/professional_report_reference` 的原始附录通道,因此该通道的 3,797 处标识符没有被拦住。任务 1 不是把 BUG-999 推翻,而是补上它没覆盖的第二条通道;决策记录里写明的两处推翻只限附录通道。
|
||
|
||
## 本任务书据以成立的实测记录与三处更正
|
||
|
||
本任务书之前的口头分析有三处数字错误,均已更正,执行方以本文件为准:
|
||
|
||
1. "我方导出 163,320 字符 / 530 表格行 / 0 个日期行" —— 该次是在主检出的旧分支 `codex/unified-loading-20260902`(9-02)上跑的。`origin/staging` 实测为 309,357 字符 / 1,478 表格行 / 227 日期行,Vimshottari 主大运表存在。
|
||
2. "全仓没有任何 API 或前端调用 PL9 导出" —— 错。`/api/professional_report_reference` 存在,`frontend/src/lib/personal-report-longform-generate.ts` 第 141 行在调。
|
||
3. "产品页报告 charts 上限 6 张" —— 错。`origin/staging` 上是 `.max(CHART_IDS.length)` = 9 张。
|