- dasha-unify: fix Ashtottari birth balance (BUG-1344) - rect-scoring-perf: server-side candidate cap 241 (BUG-1345) - node-runtime-resources: enable user-side session cookie cache, 300 s (BUG-1346) - consult-prep-parallel: classifier/reserve concurrency declined - answer-effort-ab: model key supplied by product at acceptance - drop stricter-than-test line-count red lines (AGENTS.md section 6) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Ad9rqAB8pXXvyzh7f4X8v
28 KiB
TASK · 大运实现收口:/api/dasha 改走引擎同一套实现,删占位注册表(issue #待定)
- 日期:2026-10-13
- 来源:2026-10-13 全仓屎山与性能排查 ·「大运多套实现」条目(
scripts/*dasha*.py28 个、两套条件大运、_birth_lord_and_balance多份、Vimshottari 年数表多处硬编码) - 执行方:Grok(Windows + Docker),分支
codex/dasha-unify-20261013,只推这个分支 - 验收:Claude(Linux)
- BUG 编号:BUG-1341、BUG-1344(开工时核对
docs/BUG_HISTORY.md最大号;写作时最大号 BUG-1317;号段由主会话统一分配)(另:TASK-upstream-sync7-20261014占 BUG-1318~1322,本批号段整体从 BUG-1323 起) - 开工前置:
api-chart-subprocess-gate已合入origin/staging(它改jyotish_api_server.py与排盘路径)。与python-hygiene(BUG-1340)同为第二批,两单都改scripts/jyotish_api_server.py与scripts/jyotish_engine.py:本单在python-hygiene合入后开工,若主会话决定并行,则本单只碰第 6 节列出的函数,合入时由后合的一方 rebase。 - 主会话补充的串行链(2026-10-14):引擎/API 文件链按此顺序逐单合入后开工(
upstream-sync7也改jyotish_engine.py/jyotish_api_server.py,若在途,后合一方 rebase):api-disk-cache-retention+report-reference-perf→api-chart-subprocess-gate→pl9-render-split→python-hygiene→dasha-unify→api-server-service-extract。本单的前置以这条链为准(与上一行不一致时以本行为准)。 - 后继:
api-server-service-extract依赖本单合入。
1. 基线
- 基线 commit:
origin/staging77057a819(写作时);开工以前置单合入后的origin/staging为准。 - 实测脚本:
compare.py(写作时放在起草方 scratch,下文给出可照抄的要点)。虚构盘:1990-01-01 12:00,新德里 28.6139 / 77.2090,时区 +5.5,Lahiri,平均交点。月亮 306.4653°(第 23 宿 Dhanishta 第 4 足),上升 343.9226°(双鱼),太阳 256.8599°。 - 本机参照:PyJHora 4.8.7(
jhora.horoscope.dhasa.graha.*/raasi.narayana),drik.set_ayanamsa_mode("LAHIRI")。PyJHora 只在研究 / 测试中作对照,不进运行时(许可证边界见 AGENTS §9 与tests/test_runtime_jhora_import_scan.py)。
2. 事故实证(77057a819)
2.1 同一张盘,两条入口给出两套大运
引擎全量解读(scripts/jyotish_engine.py::cmd_full_reading 约第 16860 行起,按字符串表 __import__ 独立模块)与接口 POST /api/dasha(scripts/jyotish_api_server.py::JyotishAPIHandler._compute_dasha_system → scripts/extended_dashas.py::calc_any_dasha)对比,前 3 段主运(主星 / 起始日):
| 体系 | /api/dasha(extended_dashas) |
引擎独立模块 | PyJHora 4.8.7 |
|---|---|---|---|
| Panchottari | Ketu 1990-01-01、Venus 2002-01-01、Sun 2014-01-01(Vimshottari 的九星,各 12 年) | Jupiter 至 1990-04-10、Sun 1990-04-10、Mercury 2002-04-10、Saturn 2015-04-11 | Jupiter(1972-04-10 起)、Sun 1990-04-10、Mercury 2002-04-10、Saturn 2015-04-11 |
| Satabdika | Ketu 1990-01-01、Venus 2001-01-01、Sun 2012-01-02(各 11 年) | Venus 余额、Mercury 1990-02-25、Jupiter 2000-02-26、Mars 2020-02-26 | Venus(1980 起)、Mercury 1990-02-25、Jupiter 2000-02-26、Mars 2020-02-26 |
Chaturshitisama(注册键 chaturaaseeti) |
Ketu 1990-01-01、Venus 1999-01-01、Sun 2008-01-02(各 9 年) | Moon 余额、Mars 1990-03-08、Mercury 2002-03-08、Jupiter 2014-03-08 | Moon(1978 起)、Mars 1990-03-08、Mercury 2002-03-08、Jupiter 2014-03-08 |
| Shastihayani | Moon 1990-01-01、Mars 1998-01-01、Mercury 2008-01-02 | Saturn 余额、Rahu 1990-01-12、Jupiter 1996-01-13、Sun 2006-01-12 | Saturn(1984 起)、Rahu 1990-02-03、Jupiter 1996-02-04、Sun 2006-02-03 |
| Narayana | Pisces 3 年、Capricorn 6、Aquarius 9((idx%3+1)*3 编出来的年数) |
narayana_dasha.py(旧算法,已标注):Pisces 3、Aries 7、Taurus 8 |
Pisces 9、Cancer 5、Scorpio 8 |
结论:
- Panchottari / Satabdika / Chaturshitisama:引擎独立模块与 PyJHora 逐日一致(主运起始日完全相同);
extended_dashas完全对不上。 - Shastihayani:引擎模块与 PyJHora 主星顺序一致,起点差约 22 天,原因是模块按文献用 28 宿(含 Abhijit)切分(
scripts/shastihayani_dasha.py模块说明与source_locator写明出处)。这是有出处的口径选择,本单不改。 - Narayana:三套各不相同。引擎用的
narayana_dasha.py是已知旧算法(BUG-1215 已加「旧算法」标注;BUG-1218 的 Rath 版只并列展示);extended_dashas是第三套、比旧算法还假。选哪套为准属于占星裁定,已有 BUG-1215 / 1218 在推进,本单不裁定,只要求/api/dasha与引擎、咨询给出同一份(含旧算法标注)。
2.2 extended_dashas 是占位实现,不是另一种口径
scripts/extended_dashas.py(408 行)登记 35 个键。逐键用两组完全不同的输入(月宿 3 vs 17、上升 0 vs 7、太阳星座 2 vs 9 …)各算一次:
| 现象 | 键数 |
|---|---|
| 两组输入输出完全相同(不看盘) | 28 / 35(含无实现返回空表的 vimshottari、ashtottari、chara) |
输出就是「Vimshottari 九星 × 每段 4 年」同一张表(calc_generic_dasha 或 calc_any_dasha 的兜底) |
18 |
| 总年数与登记年数不符 | 例:panchottari 登记 105 实出 108;kalachakra 94 实出 144;shasti_hayani 60 实出 84;dwadashottari 112 实出 36 |
- 文件自己写了:
scripts/panchottari_dasha.py模块说明「The legacyextended_dashashelper is deliberately not used: it is a generic placeholder」。 _compute_dasha_system对这些结果回precision: 'calculator'(只要注册函数名不是calc_generic_dasha),把占位结果标成真算。- 已有记录:BUG-1262 防复发写「
extended_dashas.py的另一套 Yogini 仍按月宿对 8 取余且不裁余额,全盘报告不走它」;BUG-1284(mitigated)写「'shoola'注册项……接口层仍能按这个键调用……本单不得改jyotish_api_server.py的方法体,所以注册项还在」。两条都把问题留给了今天这一单。
谁在用:
POST /api/dasha非 Vimshottari 键:只有引擎内网接口与/api/technique_example(技法浏览器示例,默认 payload 是 Vimshottari)。前端frontend/src只调/api/dasha/chara;/api/daily-starlanguage已改走/api/daily_guidance(BUG-265 记录里「并行取/api/dasha」已过时)。没有用户可见入口,所以是 P2 级缺陷,但它是对外公开的接口合同。/api/chart(_compute_chart_sync)返回体里的available_dashas(35 条元数据)与dasha_count来自extended_dashas.DASHA_REGISTRY;frontend/tests/fixtures/chara-zero-year-1985-08-03-beijing.golden.json与tests/golden/consultation_contract_keypaths_v1.json含此字段。tests/run_all.py(release 门第 84 行会跑)有extended_dashas/conditional_dashas导入与「至少 30 / 35 个」断言。
2.3 conditional_dashas.py 只被测试用
scripts/conditional_dashas.py(193 行,calc_dwisaptati_dasha / calc_shattrimsa_dasha / calc_dwadashottari_dasha 等 7 个函数)全仓只有 tests/run_all.py 4 处导入;生产用的是 dwisaptatisama_dasha.py / shattrimshatsama_dasha.py / dwadashottari_dasha.py。
2.4 _birth_lord_and_balance 8 份:6 份同一算法换参数
| 文件 | 起算宿 | 结构 |
|---|---|---|
panchottari_dasha.py |
Anuradha(16) | 同一算法:offset=(宿-起算宿)%27,lord=SEQ[offset%len],余额=年数×(1-宿内比例) |
satabdika_dasha.py |
Revati(26) | 同上 |
chaturshitisama_dasha.py |
Swati(14) | 同上 |
dwisaptatisama_dasha.py |
Mula(18) | 同上 |
shattrimshatsama_dasha.py |
Sravana(21) | 同上 |
shodashottari_dasha.py |
Pushya(7) | 同上 |
shastihayani_dasha.py |
28 宿分组 | 不同算法(关键字参数,组内多宿) |
dwadashottari_dasha.py |
— | 不同算法(返回 4 元组) |
排查报告「8 份互不相同」修正为:6 份是同一算法的参数化副本,2 份是真不同。
2.5 Vimshottari 年数表硬编码 15 处(排查报告写 7 处)
{'Ketu': 7, 'Venus': 20, …, 'Mercury': 17} 字面量:jyotish_engine.py、dasha_analyzer.py、dasha_calculator.py、dasha_calculator_enhanced.py、vimshottari_subperiod_timeline.py、conditional_dashas.py、event_prediction_model.py、tajika.py(2)、prashna.py、validate.py、nakshatra_dasha.py、kp_system.py、divisional_charts_extended.py、research/offline_research_20260926_lib.py。复现:grep -rnE "Rahu['\"]?\s*:\s*18" --include=*.py scripts mcp_server.py jyotish_vedic。
其中 dasha_analyzer.py、divisional_charts_extended.py、narayana_dasha.py 在校正冻结身份里(references/real_case_calibration/minute_rectification_holdout_v3.json frozen_scoring.files;tests/test_sealed_holdout_contract_freshness.py 与 tests/test_rectification_validation_integrity_gate.py 按字节哈希核对,ERR-110 / ERR-114)。改一个字节就要按研究协议重新冻结。
2.6 附带发现:全量解读的 Ashtottari 没有出生余额(产品 10-14 决定:修,见 T6,BUG-1344)
同一虚构盘:
| 来源 | 前两段主运 |
|---|---|
ashtottari_dasha.calculate_ashtottari_dasha(全量解读 modules.ashtottari_dasha) |
Jupiter 1990-01-01 起满 19 年、Rahu 2009-01-01 |
PyJHora get_ashtottari_dhasa_bhukthi |
Jupiter 1983-10-07 起(出生时已走约 6.2 年)、Rahu 2002-10-07 |
nakshatra_dasha.calc_ashtottari_dasha(全量解读 modules.nakshatra_dasha 内) |
起运星 Mars(与前两者都不同) |
原因(代码可见):ashtottari_dasha.py 只有在拿到 moon_longitude 且适用规则族是「Rahu 从命主」时才算出生余额;cmd_full_reading 传的是 birth_info_for_alt_dasha(没有 moon_longitude),所以第一段从出生满额起算,后续每段都差约 6 年。改它会改报告里 Ashtottari 表的全部日期。主会话补充核实(77057a819):即便拿到 moon_longitude,_build_major_periods(start_lord_idx, birth_dt) 仍从出生时刻按满额排第一段,_calc_candidate_balance_and_birth_chain 的结果只作附加字段、不平移时间线;默认规则族(月宿 + 月相)路径根本不算余额;「当前大运」也按「第一段从出生开始」推算。产品 10-14 决定修复(T6)。
3. 根因
extended_dashas.py(「冲刺全球第一 · Dasha 总数 10 → 18 → 35」)是早期为凑覆盖数写的占位;之后逐个体系补了有出处的独立模块(2026-09 起),引擎全量解读改走独立模块,但 HTTP 接口方法体受「不得改jyotish_api_server.py」限制一直没换(BUG-1284 原文)。不是回归,是迁移只做了一半。- 条件大运每加一个就复制一份
_birth_lord_and_balance;年数表各文件自带。
4. 决策记录
- 2026-10-13 产品负责人同意按排查结论全部立单交 Grok 执行。
- 权威实现 = 引擎全量解读当前调用的独立模块(第 6 节 T2 映射表)。依据是代码与对照数据,不是占星判断:Panchottari / Satabdika / Chaturshitisama 与 PyJHora 逐日一致,
extended_dashas不读盘。 - Narayana 权威 =
narayana_dasha.narayana_dasha_full_report+narayana_legacy_label.label_legacy_narayana(与咨询_attach_local_consultation_layers同一调用)。不在本单裁定旧算法 vs Rath 版(BUG-1215 / 1218 红线:Rath 版只并列展示,替换由产品在 T5 试算后决定)。 - Shastihayani 28 宿口径维持。
- 没有独立实现的注册键(
tara、sudasa、mandooka、lagnamsaka、yogardha、kendradi、tithi_ashtottari、tithi_yogini、patyayini、paryaaya、rashmi、naisargika、aayu、karaka、panchasvara、sudarsana等,开工重核):/api/dasha改为返回 400dasha system not implemented,不再编周期。依据:用户偏好「多余入口宁可删除也不修」;这些键没有任何用户可见调用方。 /api/chart的available_dashas/dasha_count本单保持逐字节不变(元数据搬到新注册表,内容不改),避免牵动排盘 golden 与排盘缓存;与/api/dasha能力不一致的问题列入「后续阶段」,需拍板是否裁剪。conditional_dashas.py、extended_dashas.py删除。tests/run_all.py(release 门)里对应断言按三栏改。- 冻结身份内文件(
dasha_analyzer.py、divisional_charts_extended.py、narayana_dasha.py、domain_calculation_service.py等)本单一个字节不改;它们的年数表留到下次有理由重新冻结时一起收。 - 2.6 Ashtottari 出生余额:产品负责人 2026-10-14 决定修复(T6,BUG-1344)。这是本单唯一允许改变引擎全量解读输出的项:只许
modules.ashtottari_dasha(及 T6 列出的同源 Ashtottari 输出)变化,其余逐字节不变。适用性规则(哪些盘适用 Ashtottari、两种规则族的判定)不改,只修「已判定适用时的时间线」。 - 推翻的既有约束:BUG-1284「本单不得改
jyotish_api_server.py的方法体」只是那一单的范围限制,不是长期红线;本单授权改_compute_dasha_system/_fallback_dasha_periods/_compute_chart_sync里读注册表的三行;行数按增长合同粗护栏。
5. 硬红线
- 引擎全量解读输出逐字节不变(
modules.*_dasha全部;唯一例外是 T6 的 Ashtottari 时间线)。本单只改接口路径与去重;若 T3 / T4 去重让任何数值或字段顺序变化,即为搬错,改代码不改断言。 - 冻结身份两份清单内的文件一个字节不改(开工打印清单,第 9 节)。
- 不改
narayana_dasha.py、narayana_rath.py、narayana_legacy_label.py;不改 BUG-1215 / 1218 的展示与打分口径。 scripts/jyotish_api_server.py:行数只按tests/test_api_server_growth_contract.py的粗护栏(AGENTS.md §6:不另加比测试更严的行数红线,也不得压行凑数),类方法数与JyotishAPIHandler.__new__伪造点不得增长;类方法数(删_fallback_dasha_periods后应下降 1,合同测试基线同步下调并写三栏);JyotishAPIHandler.__new__计数不得增长。/api/chart响应逐字节不变(available_dashas内容与顺序不变)。- 新注册模块不得 import
jyotish_api_server;不得在运行时 importjhora(PyJHora 只在测试里作对照,且测试需在缺 PyJHora 时 skip,镜像不装它——见记忆「验收要屏蔽 PyJHora 模拟生产」)。 - 不改
.gitea/workflows/**、不动表结构与迁移、不升级依赖。 - 改既有断言写「原值 / 新值 / 原因」三栏;golden 只能来自真实引擎输出。
- 不碰
skills/*/versions/**。
6. 任务分解
T1 · 实测对照固化为测试(必做,先做)
- 新测试
tests/test_dasha_api_engine_parity.py:- 用第 1 节虚构盘,对 Panchottari、Satabdika、Chaturshitisama、Shastihayani、Dwisaptatisama、Shattrimshatsama、Shodashottari、Dwadashottari、Yogini、Ashtottari、Kalachakra、Narayana:
/api/dasha(handler 方法直调)返回的主运列表 == 引擎全量解读同一模块的主运列表(主星、起止日期、年数逐项相等)。本条在基线上应当红(证明 BUG-1341),修完转绿;把基线红的输出贴进进度记录。 - PyJHora 对照(
pytest.importorskip("jhora")):Panchottari / Satabdika / Chaturshitisama 主运起始日与 PyJHora 逐日相等——锁住「为什么以独立模块为准」。golden 由真实 PyJHora 与真实引擎输出生成,fixtureProvenance写命令。
- 用第 1 节虚构盘,对 Panchottari、Satabdika、Chaturshitisama、Shastihayani、Dwisaptatisama、Shattrimshatsama、Shodashottari、Dwadashottari、Yogini、Ashtottari、Kalachakra、Narayana:
- 验收:Grok 在基线上跑一次(应红)、修后跑一次(应绿),两次输出都进进度记录。
T2 · /api/dasha 改走权威实现,删 extended_dashas.py(必做)
改什么:
- 新建
scripts/dasha_registry.py(不 importjyotish_api_server):-
DASHA_REGISTRY_METADATA:从extended_dashas.DASHA_REGISTRY原样搬(35 个键、顺序、name/years/type不变),只供/api/chart的available_dashas。 -
IMPLEMENTED:键 → 权威函数映射(写作时,开工重核):注册键 权威实现 vimshottari不变: domain_calculation_service.compute_vimshottari_timeline(handler 现有分支)yoginiyogini_dasha.calculate_yogini_dashaashtottariashtottari_dasha.calculate_ashtottari_dasha(与全量解读同参,含 2.6 的问题,原样)kalachakrakalachakra_dasha.calculate_kalachakra_dashapanchottari/satabdika/chaturaaseeti/shasti_hayani/shodasottari/dwadashottari/dwisaptati/shattrimsa对应独立模块的 calculate_*,参数与cmd_full_reading的conditional_birth_info/extra相同narayananarayana_dasha_full_report+label_legacy_narayana(与咨询同参)shoola不接(BUG-1284:报告走 rashi_dashas_native的shoola_forward_md_direct_ad_v1;若开工确认rashi_dashas_native.calculate_rashi_dashas的shoola段可直接复用则接它,否则 400)chara400,消息指向 /api/dasha/chara其余 400 dasha system not implemented -
一个构造入参的函数:从
planets(含lon)与ascendant(含lon或sign_idx)算出conditional_birth_info所需字段(上升经度、月亮经度、太阳经度、D9 上升星座、十宫主所在星座、罗睺星座、命主所在星座、白天出生、月宿、月足、太阴半月等)。这部分逻辑优先从cmd_full_reading抽成共享函数、两边共用,保证「同盘同参」;若抽取会改jyotish_engine.py输出(硬红线 1),则在新模块里实现并用 T1 测试锁住等价。
-
_compute_dasha_system非 Vimshottari 分支改为调用dasha_registry.compute(key, ...);precision字段:权威实现回'engine_module',删掉'generic'/'calculator'两个旧值(对外合同变化,写进 CHANGELOG 与 BUG 记录)。- 删
JyotishAPIHandler._fallback_dasha_periods;_compute_chart_sync改读dasha_registry.DASHA_REGISTRY_METADATA。 - 删除
scripts/extended_dashas.py。tests/test_rashi_dashas_native.py里「不导入extended_dashas」断言保留(自然成立)。 - 不改:
/api/dasha/chara;/api/dashaVimshottari 分支;引擎全量解读任何调用。
验收:
- T1 测试绿。
/api/chart虚构盘响应基线 vs 本分支逐字节一致(同进程无法同时加载两版,用两个工作树各跑一次handler._compute_chart_sync落盘 JSON 后cmp;排盘缓存先清空scratch/local/api_chart_cache,VEDASTRO_ENABLE_NETWORK=0,PYTHONHASHSEED=0)。- 新测试:未实现键返回 400;
chara400 且消息含/api/dasha/chara;响应不再出现precision: 'generic'。 tests/test_api_server_security.py、tests/test_api_server_growth_contract.py(方法数基线 218 → 实测值,三栏)、tests/test_rashi_dashas_native.py、tests/test_narayana_legacy_label.py绿。
T3 · 删 conditional_dashas.py,改 tests/run_all.py(必做)
- 开工先
grep -rn "conditional_dashas" --include=*.py --include=*.ts --include=*.json .复核只剩tests/run_all.py;references/**/*.json若有路径记录(写作时references/oracle/full_technique_invocation_matrix_2026_07_22.json等提到extended_dashas),不改历史证据 JSON,在进度记录列出。 tests/run_all.py:t18、t19与第 129–130、219–233、402–409、624–630 行附近断言改为对应独立模块或注册表(三栏)。这个文件由 release 门执行(.gitea/workflows/release-quality-gate.yml第 84 行),必须本地跑通:python tests/run_all.py。
T4 · 去重:条件大运余额函数与 Vimshottari 年数表(必做,限非冻结文件)
- 新建
scripts/dasha_common.py:nakshatra_seeded_birth_balance(moon_longitude, *, anchor_index, sequence) -> (lord, balance_years, nakshatra_index),算式与 6 份副本逐运算相同(运算顺序不变,保证浮点逐位相等)。VIMSHOTTARI_YEARS(插入顺序 Ketu、Venus、Sun、Moon、Mars、Rahu、Jupiter、Saturn、Mercury)与VIMSHOTTARI_ORDER。
- 第 2.4 节 6 个模块的
_birth_lord_and_balance改为一行调用(保留函数名,测试可能按名调用);shastihayani、dwadashottari不动。 - 第 2.5 节非冻结文件里的字面量年数表改为引用常量;每处先断言
list(旧表.items()) == list(VIMSHOTTARI_YEARS.items()),顺序或数值不同的不换,在进度记录说明。冻结的dasha_analyzer.py、divisional_charts_extended.py与research/下的研究脚本不动。 - 验收:
- 新测试:6 个模块各用 2,000 个随机月亮经度(固定种子)比对新旧函数
==(旧函数在测试里以源码副本形式保留作对照,或用基线工作树导出的 golden)。 - 新测试(只许降):
scripts/根目录非冻结文件里 Vimshottari 年数字面量计数 == 本单收尾值(冻结文件列白名单)。 - 全量解读逐字节不变:基线工作树与本分支各跑一次虚构盘
python scripts/jyotish_engine.py full-reading …(参数见--help),PYTHONHASHSEED=0,去掉墙钟字段后cmp。
- 新测试:6 个模块各用 2,000 个随机月亮经度(固定种子)比对新旧函数
T6 · Ashtottari 出生余额(BUG-1344,必做,产品 10-14 决定)
- 改
scripts/ashtottari_dasha.py::calculate_ashtottari_dasha:已判定适用时,按月亮在起运星所辖星宿组内已走过的比例算出生余额,第一段主运起点 = 出生时刻 − 已走过部分,后续各段与次级周期、current推算都随之平移。两种规则族都要算余额(默认规则族现在完全不算)。 scripts/jyotish_engine.py::cmd_full_reading调用calculate_ashtottari_dasha时补传moon_longitude(conditional_birth_info已有该值,照它传;不得改birth_info_for_alt_dasha本身,那会牵动 Yogini / Kalachakra)。scripts/orchestrator_bridge.py等其余调用方开工 grep 全,同样补传。- 余额算法以 PyJHora
get_ashtottari_dhasa_bhukthi的语义为对照(只对照结果,不复制其代码,注意许可证边界,AGENTS.md §B1)。 - 全量解读里第二套
nakshatra_dasha.calc_ashtottari_dasha(起运星 Mars,与前两者都不同):开工查清它在报告 / 咨询 / 前端哪里被展示。若用户可见,改为直接复用ashtottari_dasha.py的结果;若无人读取,只在 PROGRESS 记录,留后续阶段。 - 验收:
- 新测试:至少 3 张盘(第 1 节虚构盘 + 2 张公开名人盘,覆盖两种规则族各至少 1 张;PyJHora 缺失时 skip 对照部分,但保留与固化 golden 的比对)主运起止日期与 PyJHora 相差 ≤ 1 天,起运星一致;出生时刻落在第一段内;
current与按日期查表一致。 - golden:受影响的报告 / 全量解读 golden 只能用真实引擎重新采集,PROGRESS 列出每个变化文件与变化只限 Ashtottari 段(其余段 diff 为空)。
- Claude 验收跑全量 pytest 与基线逐名比对(引擎数值改动,见验收分工)。
- 新测试:至少 3 张盘(第 1 节虚构盘 + 2 张公开名人盘,覆盖两种规则族各至少 1 张;PyJHora 缺失时 skip 对照部分,但保留与固化 golden 的比对)主运起止日期与 PyJHora 相差 ≤ 1 天,起运星一致;出生时刻落在第一段内;
T5 · 记录(必做)
docs/BUG_HISTORY.md新增 BUG-1341:/api/dasha非 Vimshottari 体系是占位实现(不读盘、年数自编、标成calculator);修复为走引擎同一套模块;关联 BUG-1262、BUG-1284(把 1284 状态从 mitigated 更新为 resolved 并注明本单)、BUG-1215 / 1218(Narayana 未裁定)。另新增 BUG-1344:全量解读 Ashtottari 无出生余额、时间线整体偏移(修复 T6,状态按验收证据写)。docs/tasks/PROGRESS-dasha-unify-20261013.md:T1 基线红 / 修后绿输出、映射表最终版、/api/chart与全量解读两次逐字节对照、T4 每处替换清单。CHANGELOG.md:两条(/api/dasha合同变化:未实现体系 400、precision取值变化;报告里 Ashtottari 大运日期修正为含出生余额)。Skill 版本不 bump。frontend/DESIGN.md:不改。
7. 让步顺序
时间不够按此顺序砍:T4 年数表部分 → T4 余额函数部分。T1、T2、T3、T5、T6 不可砍(T2 是 BUG-1341 本体,T6 是产品 10-14 决定)。
8. 验收命令分工
| 谁 | 命令 |
|---|---|
| Grok 自跑 | python -m pytest tests/test_dasha_api_engine_parity.py tests/test_api_server_security.py tests/test_api_server_growth_contract.py tests/test_rashi_dashas_native.py tests/test_narayana_legacy_label.py tests/test_panchottari_dasha.py tests/test_satabdika_dasha.py tests/test_chaturshitisama_dasha.py tests/test_shastihayani_dasha.py tests/test_dwisaptatisama_dasha.py tests/test_shattrimshatsama_dasha.py tests/test_shodashottari_dasha.py tests/test_dwadashottari_dasha.py tests/test_full_reading_conditional_dashas.py tests/test_rectification_validation_integrity_gate.py tests/test_sealed_holdout_contract_freshness.py -q;python tests/run_all.py;两次逐字节对照。快速门、全量 pytest 不跑,以 Claude 为准 |
| Claude 验收 | 上述 + 快速门 + 全量 pytest 与基线逐名比对(引擎相关改动,必须全量)+ 屏蔽 PyJHora 再跑一遍 T1 以外的测试(模拟生产镜像无 PyJHora)+ 前端 chart-view-zero-year 等用到 chara golden 的单文件测试(证明 /api/chart 不变) |
| 不需要 | test:db |
9. 开工前置命令
git fetch origin --prune
git log --oneline origin/staging | grep -i "api-chart-subprocess-gate\|python-hygiene" # 前置已合入
git worktree add -b codex/dasha-unify-20261013 .worktrees/dasha-unify-20261013 origin/staging
cd .worktrees/dasha-unify-20261013
grep -n "^## BUG-" docs/BUG_HISTORY.md | tail -3
python - <<'EOF'
import json, sys; sys.path[:0] = ["scripts/research", "scripts", "."]
from sealed_holdout_rerun import PRODUCTION_FILES, RESEARCH_FILES
print(PRODUCTION_FILES, RESEARCH_FILES)
print(json.load(open("references/real_case_calibration/minute_rectification_holdout_v3.json"))["frozen_scoring"]["files"])
EOF
grep -rn "extended_dashas\|conditional_dashas\|_fallback_dasha_periods\|available_dashas\|precision.*generic" tests frontend/tests scripts mcp_server.py
grep -rnE "Rahu['\"]?\s*:\s*18" --include=*.py scripts mcp_server.py jyotish_vedic
wc -l scripts/jyotish_api_server.py
python -m pytest tests -q -p no:cacheprovider > /tmp/baseline-full.txt 2>&1; tail -3 /tmp/baseline-full.txt
对照脚本要点(写作时用过,可照抄):swisseph 设 SIDM_LAHIRI,julday(1990,1,1,12-5.5) 取月亮 / 太阳 / 平均罗睺 / Placidus 上升;独立模块入参加 standard_table_profile="pl9_reports_all_dasha_tables_v1"(与报告一致,强制出表)、navamsa_ascendant_sign_index、tenth_lord_sign_index;PyJHora 用 drik.Place("f",28.6139,77.2090,5.5)、get_dhasa_bhukthi(dob, tob, place, dhasa_level_index=1)。
10. BUG 编号起点
BUG-1341(写作时最大号 BUG-1317)。开工核对,被占用则顺延并说明。
11. 交付物清单
- 代码:新
scripts/dasha_registry.py、新scripts/dasha_common.py;scripts/jyotish_api_server.py(_compute_dasha_system、_compute_chart_sync一行、删_fallback_dasha_periods);6 个条件大运模块;第 2.5 节非冻结年数表所在文件;删scripts/extended_dashas.py、scripts/conditional_dashas.py;(可能)scripts/jyotish_engine.py抽共享入参函数 - 测试:
tests/test_dasha_api_engine_parity.py、去重等价测试、年数表计数测试;tests/run_all.py、tests/test_api_server_growth_contract.py三栏改动 - 文档:
docs/tasks/PROGRESS-dasha-unify-20261013.md、docs/BUG_HISTORY.md(BUG-1341,更新 BUG-1284)、CHANGELOG.md - 不改:
frontend/**、frontend/DESIGN.md、冻结身份文件、Narayana 三个文件
后续阶段(本单不做)
available_dashas裁剪到真实可算的体系(改/api/chart合同与 golden,需拍板)。- 冻结身份内
dasha_analyzer.py/divisional_charts_extended.py的年数表,在下次因其他理由重新冻结时一起收。 - Narayana 旧算法 vs Rath 版(BUG-1215 / 1218,等产品看 T5 试算)。
dasha_calculator.py、dasha_calculator_enhanced.py、nakshatra_dasha.calc_ashtottari_dasha等其余重复实现的归并。