Files
Jyotisha/docs/tasks/TASK-report-density-20260922.md
T
Jesse_ChenandClaude Opus 5.5 fcca56fe22 docs(tasks): accept the report density round and remove birth data from its brief
Acceptance of f968cb21 against TASK-report-density-20260922. Tasks 1, 2, 4
and 5 pass; task 3 does not. Under Node 22, the gate's version, the frontend
suite goes from 3,698 tests / 26 failing to 3,730 / 26 with an identical
failure list; / stays static and first-load gzip moves by 0.007%.

Task 3 renders the ordinary report's fact tables as 948 key/value rows whose
labels are engine paths: 447 carry snake_case keys and 521 carry array
indices, down to a Julian day. The brief never specified columns, so the fix
brief does.

The quick gate's new failure is the privacy scan, and two thirds of it is
mine: the brief pushed in baeec66f carried the comparison chart's real birth
date, time and coordinates. They are removed here and recorded as BUG-1008.
The copy in baeec66f's history stays until the product owner decides whether
to rewrite staging. The remaining hit is a five-character collision inside an
engine timestamp of the fictional fixture, reproduced by regenerating from its
declared fictional input.

Privacy scan on this tree before push: one finding, the fixture collision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-09-23 14:07:20 +08:00

15 KiB
Raw Blame History

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 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 先。

开工前置命令

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 张。