Same-chart measurement against the upstream PL9 export: 847,632 chars and 8,044 table rows there, 309,357 and 1,478 here. The engine is not the gap -- twenty divisional charts, four KP tables, Ashtakavarga, the six Shadbala components, Avastha, Sahams and the annual pack all compute today. The report contract has nowhere to put them: REPORT_SECTION_KINDS has no table kind and CHART_IDS lists nine charts. The outgoing raw appendix also leaks 925 distinct engineering identifiers over 3,797 occurrences, including an internal function name, a third-party engine name and a third-party product's page numbers. The comparison report scores zero on every one of those. The decision record states plainly that two BUG-999 guards are overridden for the appendix channel only; the ordinary prose projection is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
15 KiB
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 页报告。
事故实证
一、对照物与我方同一组出生资料实测
用对照报告的同一组出生资料(真实个人资料,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 D30reportDocumentV2Schema.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:报告契约是"结论 + 证据 id"的叙述式文档,
charts之外没有事实表字段。引擎算出的表无处可放,不是算不出来。 - 层 2:
render_pl9_markdown同时服务内部审计与对外附录两种用途,状态词、内部 producer 名与第三方产品页码是审计需要的字面量,直接外流。 - 层 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 项执行(8 组表 + 分盘扩容)。
- 层 2 原始附录允许直接给 C 端用户,先全部显示,不分研究/付费档。
- B 类(引擎算得出但被自身标 blocked 的大运族)要显示出来。
本决策推翻 BUG-999 的两条红线,执行方按本节执行,不得以 BUG-999 为由拒改:
- 推翻"专业参考不得再被普通下载当作 fallback":原始附录成为 C 端可下载产物,但必须是独立入口,不得替换 BUG-999 建立的普通正文投影。普通报告正文仍然只走
projectOrdinaryReportDocument。 - 推翻"
parameter_sensitive/ 参数敏感 命中即整行剔除"仅限原始附录这一条通道。普通正文的剔除规则不变。原始附录改为把状态词翻译成用户语言后保留,而不是删除整行——删除会让整张表少掉状态列,比留着更难读。
未授权、不得顺手做的:不调整任何 assertionLevel / claimStatus 的升级规则;不把附录里的替代大运族日期写进正文结论;不改 BUG-999 的 XSS / 危险 URL / jyotish-chart 围栏合同。
硬红线
- 表格层是服务端确定性装配,不是 Skill writer 生成。
skills/jyotish-personal-report/SKILL.md的"不得计算星盘"约束不变:writer 不得撰写、改写、补全任何表格单元格。 - 对照物的数字不得照搬。源头自己的台账
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 分量在源头仍未闭环,我方展示时必须保持既有状态标注,不得因为"对照物这么写"而升级。 - 不得学对照物的双向解释模板("When supported… When afflicted…"两边都写)。
- 不得动
scripts/jyotish_api_server.py的类方法数与JyotishAPIHandler.__new__伪造点(AGENTS.md §6,tests/test_api_server_growth_contract.py执行)。附录相关逻辑进scripts/独立模块。 - 原始附录里不得出现内部函数名、第三方引擎名、第三方产品页码。
- 出生资料不进仓:本轮所有实测用
/tmpscratchpad,任何真实出生数据不得写进测试、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 中存在,来源键见括号):
- Vimshottari 主大运全表 + 当前主运下的 AD / PD(
timing_and_predictive_systems.dasha.timeline[].antardasha_timeline[].pratyantar_dasha_timeline) - SAV / BAV(
strengths_and_scores.ashtakavarga) - Shadbala 六分量(
strengths_and_scores.shadbala) - 功能吉凶(
strengths_and_scores.functional_benefic_malefic) - Avastha(
advanced_systems.avasthas) - Upagraha + 特殊上升点 + 敏感派生点(
divisional_and_special_charts.upagrahas/special_lagnas/sensitive_points) - Sade Sati 三轮(
advanced_systems.yogas_doshas.sade_sati) - 当年 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 --noEmit0 错、npm run lint0 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 + 任务 2(原始附录清洗与入口)。这是用户今天就能拿到的东西,且修的是正在外流的工程词。
- 再交任务 3 的前 4 组表(Vimshottari 全表、SAV/BAV、Shadbala 六分量、功能吉凶)。
- 再交任务 4 的分盘扩容。
- 任务 3 的后 4 组表(Avastha、Upagraha、Sade Sati、年度)与任务 5 最后。
任务 3 与任务 4 都改 personal-report-contract.ts,必须串行,顺序为 3 → 4。任务 2 与任务 3 都改报告页组件,任务 2 先。
开工前置命令
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 推翻,而是补上它没覆盖的第二条通道;决策记录里写明的两处推翻只限附录通道。
本任务书据以成立的实测记录与三处更正
本任务书之前的口头分析有三处数字错误,均已更正,执行方以本文件为准:
- "我方导出 163,320 字符 / 530 表格行 / 0 个日期行" —— 该次是在主检出的旧分支
codex/unified-loading-20260902(9-02)上跑的。origin/staging实测为 309,357 字符 / 1,478 表格行 / 227 日期行,Vimshottari 主大运表存在。 - "全仓没有任何 API 或前端调用 PL9 导出" —— 错。
/api/professional_report_reference存在,frontend/src/lib/personal-report-longform-generate.ts第 141 行在调。 - "产品页报告 charts 上限 6 张" —— 错。
origin/staging上是.max(CHART_IDS.length)= 9 张。