# PROGRESS · 数据卡补相位、同宫、燃烧,瑜伽按领域分流(2026-10-01,三单之一) - 任务书:`docs/tasks/TASK-consult-card-affliction-data-20261001.md`(origin/staging `9b937c4a`) - 执行:Claude fork 子代理(直接执行模式,产品授权) - 分支 / worktree:`codex/consult-card-affliction-data-20261001` / `.worktrees/consult-card-affliction-data-20261001`,基线 `9b937c4a`(任务书写的 `2d620fb4` 之后只多了任务书与排查记录两个文档提交) - BUG 编号:开工核对最大 BUG-1153;本单用 1154~1159(1158 = T5 核对结论,1159 = T4 诊断中查出的瑜伽清单问题) - 代码提交:`19802a48`(文档另一提交) - 预检:`python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45` 全部 ok;已读 `docs/research/pre_work_error_ledger.md` ## 环境 - 无项目 `.venv`(仓内 `.venv` 是悬空软链),Python 一律用本机 `python3` 3.13.5(user site 带 pyswisseph)。 - Node 22.14(`/exec-daemon/node`)跑前端;`frontend/node_modules` 用 `cp -al` 硬链接副本(真目录,Turbopack 可构建,已 gitignore,未提交)。 - 快速门内部的 `npm test` 步骤走系统 Node 20,70 条失败(Node 22 才支持的模块 mock 等);前端全量另用 Node 22 跑,结论以 Node 22 为准。 - 无 Docker:24 条数据库 / 部署套件失败,与基线逐条同名。 - 无模型凭据:模型读卡后的真实回答不在本单验收范围(第三单名人回测)。 ## 交付门 | 项 | 基线 `9b937c4a` | 本分支 | | --- | --- | --- | | `tsc --noEmit` | 0 错 | 0 错 | | `npm run lint` | 0 error / 126 warning | 0 error / 126 warning | | `npm test`(Node 22,提交后终跑) | 4,868(pass 4,803 / fail 24 / skip 41 / cancelled 0) | 4,878(pass 4,813 / fail 24 / skip 41 / cancelled 0) | | 失败名单 | 24 条,全是数据库 / 部署类 | 与基线逐条同名(`comm` 无差) | | 测试名单 | — | 缺 0、新增 10(全部来自新文件) | | `next build` | `/` ○ Static | `/` ○ Static | | 首屏 gzip(`index.html` 引用 29 个脚本 gzip-9 求和) | 566,193 B | 566,193 B(0%) | | Python 定向 `tests/test_consultation_native_layers.py` | 7 | 12 passed | | 快速门 Python 段 | — | 1,041 passed / 1 skipped(含新增 5 条;快速门整体因内部 Node 20 的 `npm test` 退出 1,见环境) | | `tests/test_api_server_growth_contract.py` | — | 通过(`jyotish_api_server.py` 0 行改动) | 时序抖动:`consult-evidence-lookup-20260927.test.ts`「a lookup does not reset the answer clock」在中间一次全量跑里报 `empty_answer` ≠ `answer_truncated`(70 秒假时钟下的竞态),同分支其余两次全量跑通过、单文件连跑 3 次 15/15 通过;本单没有改答题钟或该测试的任何依赖。 ## 实现 | 任务 | 落点 | | --- | --- | | T1 引擎层(BUG-1154) | `scripts/consultation_native_layers.py` 新增 `build_graha_drishti`:七颗行星调 `aspects.calc_house_aspects`,输出 `planets[星].aspects_houses / aspected_by / combust`、`houses[N].aspected_by`;罗睺、计都只作被照对象。燃烧:把完整报告 Step 5 Shadbala 块里的内联规则提成 `jyotish_engine.COMBUSTION_ORBS` + `is_combust()`(容许度、比较式原样),完整报告与本层都调它。`_guard` 包裹 | | T1 附带(T3 年运字段) | `build_slow_transits` 增加 `saturn_from_moon_house`(行运土星星座距本命月亮星座的整宫宫距) | | T4 引擎侧(BUG-1157) | 新增 `build_yoga_domains` 与固定表 `YOGA_PACKET_DOMAINS`(按 Raman 包文件,用 `rule_id` 对照),每条带 `name / hit / domain` | | 投影 | `frontend/src/mastra/consultation-workflow.ts`:`graha_drishti`、`yoga_domains` 进本命投影各自白名单;行运白名单加 `saturn_from_moon_house` | | T2 卡片 base(BUG-1155) | `consultation-evidence-card.ts` `baseNatal`:行星行加 `aspected_by`、`conjunct_with`(同 `house` 选择)、`combust`(仅 true);相位层缺失记 `base.aspects`。领域段宫位行加 `aspected_by` | | T3(BUG-1156) | parents roles 加 `lord_rows` / `significator_rows`;D12 同号宫加 `lord`、`lord_sign`;health `[1, 6, 8, 12]`、education `[4, 5, 9]`;年运卡抄 `saturn_from_moon_house` | | T4 卡片 | 规格新增 `yogaTags`;parents、children 打开 yogas;yogas 段 `{names, hits, claim_boundaries}` | | golden | `JYOTISH_API_CHART_CACHE_TTL_SECONDS=0 PYTHONHASHSEED=0 python3 scripts/research/capture_consult_evidence_card_golden.py` 重生;去掉新键后与旧 golden 逐值比对 0 处差异 | | 测试 | 新文件 `frontend/tests/consult-card-affliction-data-20261001.test.ts`(10 条);`tests/test_consultation_native_layers.py` 新增 5 条 | ## 与任务书的偏差(请验收时逐条判断) 1. **相位层挂在 `chart.consultation_native_layers.graha_drishti`,不在 `chart.modules`。** 该模块的既有约定是不写 `modules`(主题报告按 `modules` 数量计 `full_reading_module_count`,不能动),所有本地附加层都在这里,投影也从这里读。 2. **燃烧不是「透传」。** 普通对话的排盘路径根本没有 `combust` 字段(golden 三张盘行星行都没有)。做法是把完整报告的内联规则原地提成函数,两处共用;没有写回 `chart.planets` 行(不改既有输出),值放在相位层里。 3. **宫位相位放在领域段的宫位行,没放 base。** 两种 base 方案都试过:base `house_aspects`(空宫省略)时乔布斯 timing 卡「契约+卡」12,005 > 12,000;放进 `base.natal.houses` 更大。领域宫位行只给本领域选中的宫,读宫时就在手边。代价:某领域没选的宫,卡上看不到它被谁照(落了星的宫可从落星的 `aspected_by` 读到)。 4. **为守住 12,000 字符,压缩了两处既有格式(任务书 D6「先压缩格式」):** `antardashas_in_mahadasha` 从 `{lord,start,end}` 改为 `[主星, 起, 止]`;D9 行的 `vargottama` 只在 true 时出现。新增的 `aspected_by`、`conjunct_with` 空时不出现,`combust` 只在 true 时出现。没有删任何字段的信息。 5. **yogas 段去掉 `count`、加 `hits`。** 原 `count` 是引擎全量条数,分流后与名单对不上;`hits` 是引擎 `hit` 原值,见 T4 诊断第一条。 6. **`functional` 标签** benefic / malefic / neutral 由卡写,进 `EVIDENCE_CARD_LABELS`(同「母亲 / 父亲」的先例)。D12 宫主用卡内 `SIGN_LORDS` 常量(与 `scripts/functional_benefics.py` 同一张表)。 7. **T4 验收「子女卡含 Puthra 类」**:golden 的 family 路由与 annual 路由都有子女包,测试用 annual 路由(20 条全包)对五个领域逐条核对。这些名字大多是**未命中**的规则候选,不是成立的瑜伽,见下。 ## 改动的既有断言(原值 / 新值 / 原因) | 文件 | 原值 | 新值 | 原因 | | --- | --- | --- | --- | | `consult-evidence-card-20260927.test.ts`「the card names what else…」 | career 卡 `supplementable_sections` 不含 `yogas`(false) | 含(true) | T4:卡上只放本领域标签的瑜伽,完整清单仍只能一次查阅拿到 | | 同文件「every card value is…」白名单 | `EVIDENCE_CARD_LABELS` 四条 | 同一来源多 benefic / malefic / neutral,断言本身不变 | T3 父母卡宫主与代表星行的功能吉凶 | | `consult-evidence-card-v2-20260927.test.ts`「every base section carries the engine's D9 summary」 | 每行 deepEqual `pick(row, [d9_sign, d9_dignity, vargottama])` | `vargottama` 为 false 时期望里去掉该键;另加一条断言卡上 `vargottama === true` 与引擎一致 | D6 字符预算 | 三个 12,000 字符断言(`consult-evidence-card-20260927`、`consult-evidence-card-v2-20260927`、`consult-condensed-checklist-20260927`)一条未改。 ## 卡片体量(字符,`JSON.stringify` 长度;golden 三张盘) 「契约+卡」= 工具返回去掉 `methodology`(`consult-evidence-card-v2` 的口径);「含清单」= 整个返回(`consult-condensed-checklist` 的口径)。annual / timing 用各自路由的 golden,其余领域用 family 路由。 | 盘 / 领域 | 契约+卡 基线 | 本分支 | 差 | 含清单 基线 | 本分支 | 差 | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | 乔布斯 / career | 7,358 | 7,233 | -125 | 9,989 | 9,864 | -125 | | 乔布斯 / marriage | 7,904 | 7,941 | +37 | 10,612 | 10,649 | +37 | | 乔布斯 / wealth | 7,378 | 7,385 | +7 | 9,950 | 9,957 | +7 | | 乔布斯 / health | 7,206 | 7,412 | +206 | 10,993 | 11,199 | +206 | | 乔布斯 / education | 6,329 | 6,477 | +148 | 8,368 | 8,516 | +148 | | 乔布斯 / migration | 6,270 | 6,330 | +60 | 8,309 | 8,369 | +60 | | 乔布斯 / family | 6,352 | 6,424 | +72 | 8,388 | 8,460 | +72 | | 乔布斯 / parents | 6,518 | 7,379 | +861 | 8,555 | 9,416 | +861 | | 乔布斯 / children | 6,152 | 6,321 | +169 | 8,190 | 8,359 | +169 | | 乔布斯 / annual | 9,514 | 9,751 | +237 | 11,550 | 11,787 | +237 | | 乔布斯 / timing | 8,552 | 8,555 | +3 | 11,471 | 11,474 | +3 | | 乔布斯 / general | 7,911 | 7,859 | -52 | 9,948 | 9,896 | -52 | | 奥巴马 / career | 7,299 | 7,120 | -179 | 9,930 | 9,751 | -179 | | 奥巴马 / marriage | 7,653 | 7,662 | +9 | 10,361 | 10,370 | +9 | | 奥巴马 / wealth | 7,462 | 7,398 | -64 | 10,034 | 9,970 | -64 | | 奥巴马 / health | 7,134 | 7,319 | +185 | 10,921 | 11,106 | +185 | | 奥巴马 / education | 6,380 | 6,456 | +76 | 8,419 | 8,495 | +76 | | 奥巴马 / migration | 6,318 | 6,320 | +2 | 8,357 | 8,359 | +2 | | 奥巴马 / family | 6,373 | 6,396 | +23 | 8,409 | 8,432 | +23 | | 奥巴马 / parents | 6,549 | 7,398 | +849 | 8,586 | 9,435 | +849 | | 奥巴马 / children | 6,188 | 6,311 | +123 | 8,226 | 8,349 | +123 | | 奥巴马 / annual | 9,355 | 9,501 | +146 | 11,391 | 11,537 | +146 | | 奥巴马 / timing | 8,629 | 8,577 | -52 | 11,548 | 11,496 | -52 | | 奥巴马 / general | 7,894 | 7,799 | -95 | 9,931 | 9,836 | -95 | | 泰勒 / career | 7,244 | 7,167 | -77 | 9,875 | 9,798 | -77 | | 泰勒 / marriage | 7,970 | 8,064 | +94 | 10,678 | 10,772 | +94 | | 泰勒 / wealth | 7,432 | 7,466 | +34 | 10,004 | 10,038 | +34 | | 泰勒 / health | 7,090 | 7,333 | +243 | 10,877 | 11,120 | +243 | | 泰勒 / education | 6,334 | 6,527 | +193 | 8,373 | 8,566 | +193 | | 泰勒 / migration | 6,291 | 6,369 | +78 | 8,330 | 8,408 | +78 | | 泰勒 / family | 6,356 | 6,469 | +113 | 8,392 | 8,505 | +113 | | 泰勒 / parents | 6,518 | 7,364 | +846 | 8,555 | 9,401 | +846 | | 泰勒 / children | 6,142 | 6,362 | +220 | 8,180 | 8,400 | +220 | | 泰勒 / annual | 9,623 | 9,882 | +259 | 11,659 | 11,918 | +259 | | 泰勒 / timing | 8,568 | 8,604 | +36 | 11,487 | 11,523 | +36 | | 泰勒 / general | 7,811 | 7,800 | -11 | 9,848 | 9,837 | -11 | 最大值:契约+卡 9,882(上限 12,000);含清单 11,918(elizabeth_taylor/annual,上限 12,000,余量 82)。 **提醒第二单**:年运「含清单」只剩 82 字符(泰勒),timing 约 480。第二单若给年运、时运加中文清单,必须先在卡或清单上再省,或由产品重新定 12,000 的口径。 ## T4 诊断:Sasa、落陷取消、Gajakesari 为什么不在卡上 对三张公开盘直接调用 `yoga_expansion.detect_all_yogas` + `yoga_engine.detect_yogas`(即 `_compute_yogas_api` 的两个检测器): | 盘 | 检测器能出的大格局(节选) | 卡上 yogas(family 路由) | 其中命中 | | --- | --- | --- | --- | | 乔布斯 | Gajakesari、Raja Yoga 多条、Matrunasa、Saraswati、Amala(49 条) | 8 条母亲 / 子女 / 兄弟规则 | 0 | | 奥巴马 | Sasa、Neechabhanga Raja、Budhaditya、Matrunasa(60 条) | 同 8 条 | Matrunasa | | 泰勒 | Gajakesari、Hamsa (Trikona)、Saraswati、Eka / Bahu Puthra(61 条) | 同 8 条 | 0 | 三层原因(代码已核实,BUG-1159): 1. **卡上的名字是「规则候选」,不是「成立的瑜伽」。** `modules.yogas.yogas` = Raman 支持包里本路由成员规则的 `hits + misses`(`raman_support_observations.sieve_yogas_for_routes`),卡原来只抄名字不抄 `hit`。乔布斯 family 路由 8 条全部未命中,模型却看到 8 个瑜伽名。本单在卡上加了 `hits`。 2. **大格局不在任何 Raman 包里。** Sasa / Neecha Bhanga / Gajakesari 即使被检测到,也只进 `raman_support.yogas.ungoverned_detected`,不进 `modules.yogas`。这是「只放 governed_support_only_candidate_ready 的规则」的治理筛选,按任务书与 AGENTS §8.4 不放宽。 3. **规则引擎只在 `chart.yogas` 为空时才跑。** `jyotish_api_server.py` 普通对话 yogas 块:`chart.yogas` 有内容就直接用它,`if not compact:` 才调 `_compute_yogas_api`。乔布斯、泰勒的排盘结果自带 1~2 条扩展瑜伽(Amala、Saraswati / Kemadruma),于是完整检测没跑,乔布斯本可命中的 Matrunasa 也成了未命中;奥巴马 `chart.yogas` 为空,检测才跑,所以只有他有命中。命中与否取决于一个不相关的条件。 不是 Raman 筛选的条数上限(筛选没有上限,family 路由正好 3 个包 8 条规则)。第 2、3 条要改都涉及治理范围或 `jyotish_api_server.py`,交产品决定(BLOCKED.md)。 ## T5 核对:功能吉凶分类的出处(只核不改,BUG-1158 investigating) - 出处:`scripts/functional_benefics.py` `derive_functional_benefic_malefic`,`SOURCE = "strict_functional_benefic_malefic_v1"`,2026-06-28 `09817b96`「Bridge functional benefic malefic layer」引入;模块里没有引用任何典籍、流派或参考文件。 - 规则:三方宫 {1,5,9}、角宫 {1,4,7,10}、难宫 {3,6,8,11,12};同时主三方与角宫(日月除外)→ yogakaraka 且吉;只主三方 → 吉;主难宫且不含 1 宫 → 凶;其余(含只主角宫)→ 中性;8 宫主是日月时免凶。 - 由此:1 宫同时算三方和角宫,**除日月外的命主一律是 yogakaraka**(泰勒火星 1+6、奥巴马土星 1+2、乔布斯水星 1+10);**角宫主一律中性**,没有「凶星主角宫转吉」,天蝎上升的太阳(10 宫主)因此是中性;火星同时主 6 宫,因含 1 宫不算凶。 - 与常见 BPHS 列表的差别:yogakaraka 通常指同时主一个角宫与一个三方宫、且不靠命宫的行星(天蝎上升没有);天蝎上升常列功能吉星为太阳、月亮、木星。是否算缺陷、按哪派改,交产品与占星顾问;本单未改分类,父母卡的 `functional` 原样取自这一层。 ## 未做 / 交给后两单 - 提示词(`frontend/src/mastra/index.ts` 的数据卡说明)还没讲新字段(`aspected_by`、`conjunct_with`、`combust`、`lord_rows`、`hits`、`saturn_from_moon_house`、子运短数组)——属第二单(读法与清单)。 - 模型读新卡后的真实回答、名人回测——第三单。