diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c786d28..66f74e3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # 印度占星 Skill 更新日志 +## 2026-11-04 — 上游同步第十轮:研究快照移到上游最新版,三处计算按上游修正 + +- 上游研究版 Skill 的只读快照更新到上游 main 最新提交 `5fb8379c`;新快照不再带私人样例的说法。 +- 解读流程、PDF 读盘说明和两个命令的用法里残留的私人出生样例,换成上游用的公开名人样例。 +- 五份参考说明按上游的原文更正:Argala 次级看 5 宫、由 9 宫阻挡;Saham 用表 74 全部 36 项;十六分盘权重 D27 为 0.5、D30 为 1;Yogi 点说明标明当前公式;解读流程加上「开场也要有条件、没查到不等于不成立」的全文证据边界。 +- 分盘联动里 D12 一列现在有宫位(以前一直是空的);星座名异常时报错,不再当成白羊(BUG-1541)。 +- 指定日期的完整解读里,Narayana 当前段、星宿大运年龄、年运备用年龄都按那一天算;年运备用年龄到公历生日才加一岁(BUG-1542)。 +- KP 子主星和宫头用原始度数和含秒的时刻,不再用显示用的四舍五入值;只影响贴着分界的行星(BUG-1543)。 +- 网页对话的提示词没有变化。Skill 版本不变;没有数据库变更。 + ## 2026-11-03 — 输入框:单行起步,字多了变两层(产品定的方案 2) - 空着或只有一行字时,输入框是一条圆胶囊:电脑上 64px 高,手机上 56px 高,左边「常用问题」,中间输入,右边一颗 40px 的圆形发送键(可点区域 44px)。占位字仍是「想聊什么都可以」。 diff --git a/SKILL.md b/SKILL.md index f38e5397..4908903a 100644 --- a/SKILL.md +++ b/SKILL.md @@ -261,7 +261,7 @@ adapter available 解释为已完成 VedAstro、PyJHora/JHora 或 jyotishganit r **凡是用户询问事业、婚恋、财务、事件应期、历史回测或技法可靠性,必须先读取 `references/strict-workflow-router.md`。** 核心要求: -1. 先判断问题类型,再自动选择 `career-timing-strict` / `relationship-timing-strict` / `wealth-timing-strict` / `event-timing-strict` / `event-verification-strict`。 +1. 先判断问题类型,再自动选择 `career-timing-strict` / `relationship-timing-strict` / `finance-timing-strict`(兼容 `wealth-timing-strict`)/ `event-timing-strict` / `event-verification-strict`。 2. 用户不需要知道 Chara Dasha、A10、Argala、Shadbala、Ashtakavarga 等技法名称;AI 必须按问题类型自动调用。 3. 必须产出 Technique Audit Table,说明每项高级技法是否调用、结果是什么、缺失会如何降低置信度。网页由界面折叠展示,口语正文不要再贴该表。 4. 不得把未实现或未调用的技法静默省略;A10/Karma Pada、Pushkara、Vargottama、Dasha Sandhi 已进入 full-reading 输出;Bhava Chalit 与 Sudarshana Chakra 已进入 complete,可正常纳入 Technique Audit Table。 diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index d5900d52..156c34e0 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -21209,6 +21209,54 @@ - 复发自:BUG-1165 / BUG-1182(示例句被照抄)。BUG-1182 的防复发只禁了示例括号里「宫位 → 人物行为」的读法,没有禁写死的请求句和范围句,也没有给规则总量设上限,所以未拦住。 - 修复版本:分支 `codex/consult-pattern-voice-20261104`。 +## BUG-1541 | 完整解读的分盘联动里 D12 一列永远没有宫位;坏星座名被当成白羊 + +- 状态:resolved(代码 + 回归测试;待部署) +- 首次发现 / 最近更新:2026-11-04(上游同步第十轮核对上游 `cf6c316d`、`f60568a0`)/ 2026-11-04 +- 影响面:`scripts/jyotish_engine.py` `calc_inter_chart_linkage`、`calc_all_inter_chart_linkages` 与 `_cmd_full_reading` 的调用;完整解读 `modules.inter_chart_linkage`;咨询数据卡在分盘本身没有宫位时回落读这里(`scripts/consult_card_domain_facts.py`)。 +- 现象:D12 一列每颗星的 `house` 都是 `None`;D1 / D9 / D10 某颗星的星座名不在十二星座表里时,按白羊算宫位,不报错。 +- 触发条件:任何有 D12 的完整解读;星座名异常时。 +- 根因:D12 加进联动表时上升位置写死成 `None`,完整解读已经算好的 `d12_asc_idx` 没有传进去;星座查表失败时默认取下标 0。上游同一缺陷,10-16 修复,我方第九轮只搬了甲批,没有核对这两个提交。 +- 修复:两个函数加 `d12_asc_idx` 参数,完整解读把已算好的 D12 上升传进去;星座名不认识、上升下标不是 0–11 的整数时抛 `ValueError`,由完整解读的 `try` 记进 `errors`(咨询原生层由 `_guard` 记成 `blocked`),不再静默当白羊。 +- 验证:新测试 `tests/test_upstream_sync10_d12_linkage.py`(移植上游 `tests/test_inter_chart_d12_house.py` 并补坏上升下标):十二个 D12 上升各算一次宫位;缺 D12 上升仍为 `None`;坏星座、坏上升报错;完整解读调用的最后一个参数是 `d12_asc_idx`。改前 18 条失败、改后 19 条通过;`test_consultation_native_layers.py`、`test_flying_star_audit.py`、`test_full_reading_module_parity.py` 20 条通过。 +- 影响输出:完整解读联动表的 D12 宫位从空变成有值;咨询卡 D12 宫位只在分盘本身没有宫位、且出生时间精度够时才会读到(精度不够仍是 withheld)。计算口径没有改,不涉及冻结身份文件。 +- 防复发:上述测试。 +- 关联记录:BUG-1483~BUG-1490(第九轮甲批)、issue #141。 +- 复发自:无 +- 修复版本:分支 `codex/upstream-skill-sync-20261104`,待合并。 + +## BUG-1542 | 指定日期的完整解读里,Narayana 当前段、星宿大运年龄和年运备用年龄仍按今天算 + +- 状态:resolved(代码 + 回归测试;待部署) +- 首次发现 / 最近更新:2026-11-04(上游同步第十轮核对上游 `933f254e`)/ 2026-11-04 +- 影响面:`scripts/jyotish_engine.py` `cmd_full_reading` 的 Step 4.13 Narayana(`today_dt`)、星宿大运年龄(`nak_age`)、Step 9 Tajika 备用年龄(`age`)。 +- 现象:传了 `transit_date`(回放、年运、指定日期的报告)时,Vimshottari 与行运按该日期算,Narayana 当前段和星宿大运按运行当天算,同一份报告里混着两个日期。没给年龄时,年运备用年龄用 `天数 // 365`,每四年早一天进下一岁,生日前几天就按新一岁起年盘。 +- 触发条件:完整解读带 `transit_date`,或只带 `today`;年运没给年龄。 +- 根因:这三处各自写了「今天」:Narayana 只读 `args.today`,星宿大运和年运备用年龄直接用 `datetime.now()`;而 `today_str` 与 `transit_reference_date` 已经按 `transit_date → today → now` 取。上游同一缺陷,10-16 修复,第九轮没核对这个提交。 +- 修复:三处都按 `transit_date → today → now` 取观察日期;年运备用年龄按公历生日算(到了生日才加一岁)。提示文字不变。没有照搬上游的 `target_year` 分支和 `total_seconds` 年龄(我方这里没有 `target_year`,Narayana 年龄沿用 `days / 365.25`)。 +- 验证:新测试 `tests/test_upstream_sync10_observation_date.py`(移植上游 `test_full_reading_observation_date.py`、`test_tajika_report_age.py`):四个日期变量对四种入参取同一日期;年运备用年龄在生日前后、`transit_date` 优先、给了年龄不覆盖;2000-01-01 到 2039-12-25 是 39 岁不是 40;星宿大运年龄按观察日期。改前 10 条失败、改后 11 条通过。 +- 影响输出:只在请求日期不是今天时变化(回放、年运、指定日期);网页咨询按当天算的结果不变。年运备用年龄在生日前约 N/4 天内(N 为年龄)会少一岁,对应的年盘年份随之更正。不涉及冻结身份文件。 +- 防复发:上述测试;同一份报告里新增日期变量要沿用同一取法。 +- 关联记录:BUG-1541、BUG-1220(年盘保留秒)。 +- 复发自:无 +- 修复版本:分支 `codex/upstream-skill-sync-20261104`,待合并。 + +## BUG-1543 | KP 子主星与宫头用的是显示用的四舍五入值 + +- 状态:resolved(代码 + 回归测试;待部署) +- 首次发现 / 最近更新:2026-11-04(上游同步第十轮核对上游 `26d5a52d`)/ 2026-11-04 +- 影响面:`scripts/jyotish_engine.py` `cmd_kp`(命令 `kp`、`/api/kp` 一类入口)。KP 在商业覆盖层仍是 `reference_only`。 +- 现象:行星落在星主 / 子主 / 子子主分界附近、或宫头附近时,KP 表可能差一格。 +- 触发条件:行星度数或宫头离 KP 分界小于约 0.0001 度;出生时间带秒。 +- 根因:`cmd_kp` 把 `calculate_bhava_boundaries()` 返回的宫头(保留 4 位小数)、行星的 `degree_in_sign`(保留 4 位小数)和星盘结果里的 `julian_day`(保留 6 位小数,约 0.09 秒)交给 KP 分析,KP 分界是按原始经度切的。上游同一缺陷,10-16 修复,第九轮没核对。 +- 修复:按出生时刻重算儒略日(与 `compute_chart_data` 同一公式,含秒);宫头改用 `calculate_cusps()` 原值;行星度数用 `degree_raw % 30`,没有原值时才退回显示值。 +- 验证:新测试 `tests/test_upstream_sync10_kp_unrounded_inputs.py`(移植上游 `tests/test_kp_unrounded_inputs.py`):儒略日、宫头、行星度数都是原值;改前失败、改后通过。`test_kp_system.py`、`test_kp_cusp_house_assignment.py`、`test_kp_monthly_report_packet.py` 5 条通过、2 条跳过;公开名人盘实跑 `cmd_kp` 正常返回。 +- 影响输出:只有贴着分界的行星或宫头会换一格,其余逐字节相同。不涉及冻结身份文件。 +- 未搬:同一上游提交里的 Vivaha Saham 昼夜公式与 Yoga 燃烧状态透传,见本轮进度记录。 +- 防复发:上述测试;显示用的取整值不进计算。 +- 关联记录:BUG-1383(KP 边界,第八轮)、BUG-1541、BUG-1542。 +- 复发自:无 +- 修复版本:分支 `codex/upstream-skill-sync-20261104`,待合并。 ## BUG-1545 | Narayana 大运(旧算法)过了第一轮就把第一轮重放,六七十岁以上的人拿到错的当前运 - 状态:resolved(引擎 + 回归测试;未部署) diff --git a/docs/research/upstream_notice_2026_10_03.md b/docs/research/upstream_notice_2026_10_03.md index efc3e686..d2166e55 100644 --- a/docs/research/upstream_notice_2026_10_03.md +++ b/docs/research/upstream_notice_2026_10_03.md @@ -25,3 +25,8 @@ - **事件映射(甲8)**:在 77 例上,±10 / ±30 / ±60 分钟三档的回放头名分别下降 13~17、10~16、8~21 个百分点,主要来自事业一行扩到 10、6、9、11 宫。 - **月亮行运(甲9)**:按 0.25 权重加入月亮起点,结果不变;按 1.0 权重,±60 分钟档降 1.3 个百分点。 - 详细记录:`affliction_weight_trial_2026_10_03.md`、`rectification_mapping_trial_2026_10_03.md`。 + +## 2026-11-04 复核(上游 main `5fb8379c`) + +1. Narayana:上游 `narayana_dasha_full_report` 已把 seed / sequence / duration / dual-lord profile 传给 `calc_narayana_mahadasha`,另加 `cycle_count` 与 AD `strength_profile`。用虚构行星位置在三个上升上实跑,`kn_rao` 与 `legacy_local` 的大运序列不同,第 1 条已修。命令行默认仍叫 `kn_rao`,这个名字指什么仍未说明。我方默认 legacy,标为旧算法,另给 Rath 视图(BUG-1215、BUG-1218)。我方没有第二周期,也没有 AD 强度口径(`6859b263`,第九轮决定不做)。 +2. 8 星制 Karaka:上游 `jaimini.KARAKA_8` 已改为 BPHS 次序,罗睺按 30 减星座内度数排;我方同样已修,两边一致。 diff --git a/docs/tasks/PROGRESS-upstream-sync10-20261104.md b/docs/tasks/PROGRESS-upstream-sync10-20261104.md new file mode 100644 index 00000000..85ec7eab --- /dev/null +++ b/docs/tasks/PROGRESS-upstream-sync10-20261104.md @@ -0,0 +1,55 @@ +# PROGRESS · 上游同步第十轮(2026-11-04) + +- 分支:`codex/upstream-skill-sync-20261104`;基线 `origin/staging` `db2fc3fc` +- 上游:`732642856/yinduzhanxing` main `5fb8379c4f3e649cb4804065c9a3059e4bf633fe`(2026-10-21,到 11-04 仍是 HEAD) +- 上次镜像:`b9a0ef8f`(已被上游改写,提交时间对应 `dc3b8fef`);第九轮甲 / 乙批已按语义移植到 `5fb8379c`,但没有动镜像 +- 范围:本轮查 skill 文档层(SKILL.md、references/)和第九轮没有核对过的引擎修正;不推送,不碰 staging / main +- 预检:`scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45` 全部通过(外部引擎适配、远端可见性、碎片扫描、聚焦测试) + +## 做了什么 + +| 提交 | 内容 | 风险 | +| --- | --- | --- | +| `3bae79fb` | 用导入器 `--apply` 从干净克隆镜像上游 `SKILL.md` 到 `references/upstream/yinduzhanxing/`;`source-manifest.json`、`import-plan.json`、导入记录 `commit-5fb8379c….json` 改钉 `5fb8379c`;b9a0ef8f 的导入记录和测试留作历史 | 仅文档 | +| `297e27e0` | 把残留的私人样例(4 月 17 日 14:45、约北纬 36.5 东经 114.2)换成上游用的公开样例(1935-06-21 Cajarc):`ai-reading-workflow-prompt.md`、`pdf-chart-reading-guide.md`、`cmd_narayana_dasha.py` / `cmd_solar_return.py` 的用法说明 | 仅文档 | +| `2539d57f` | 五份参考文档按上游的原文更正:Argala 次级为 5 / 9 宫、Saham 表 74 全 36 项、十六分盘 D27 0.5 / D30 1、Yogi 公式说明(路径改成我方 `scripts/annual_native/yogi_sphuta.py`)、解读流程加 10-02 全文证据边界;`finance-timing-strict` 别名对齐 | 仅文档 | +| `4b6aa087` | BUG-1541:D12 分盘联动按 D12 上升算宫位;坏星座名报错,不再当白羊(上游 `cf6c316d`、`f60568a0`) | 改对外输出 | +| `8137c692` | BUG-1542:完整解读 Narayana 当前段、星宿大运年龄、年运备用年龄统一按 `transit_date → today → now` 取日期;备用年龄按公历生日(上游 `933f254e`) | 改计算 | +| `9d69cd56` | BUG-1543:KP 用原始宫头、原始行星度数和含秒的儒略日(上游 `26d5a52d` 的 KP 部分) | 改计算(仅分界附近) | + +三处引擎文件都不在校正冻结清单(`sealed_holdout_rerun.PRODUCTION_FILES`、dataset `frozen_scoring.files`)里,不需要按 ERR-110 重冻。 + +## 没有搬的 + +| 上游 | 原因 | +| --- | --- | +| `SKILL.md` 正文(真源索引 09-15、分类 skill 联用、技法查漏编译链、PL9 / VedAstro 长段、P0/P1 回填长段、Shadbala / Dasha 升格门槛措辞) | 我方 `SKILL.md` 是商业入口,导入策略把它列为保护路径;这些段落指向我方没有的文件(`skills//`、`technique_ontology.json`、`current_capability_truth_index_2026_09_15.md` 等),或已有更短的商业版。研究原文已在快照里 | +| `references/strict-workflow-router.md`:MEVG 改成「可选」、外部 oracle 缺口不再 `blocked`、新增 `interpretation_status_contract.md` | 与本仓 AGENTS.md §3、§4(MEVG 必做、缺口必须 blocked 或降级)冲突;路由章节会进对话提示词 | +| `bphs-ch48-narayana-dasha.md` 新版 | 描述的第二周期(`cycle_count`)与 AD 强度口径我方没有;我方默认是标为旧算法的 legacy 加 Rath 视图(BUG-1215、BUG-1218) | +| `quick-reference-guide.md`、`technique-capability-matrix.md`、`functional-role-profile.md` 的差异 | 我方较新(`marriage-match` 七项、#163 盘面说明) | +| 上游独有的 113 个顶层 references(rangacharya / raman 研究包、web intake、skill family router、MEVG 审计等)和 1 600 多个 oracle / 书籍 / 案例文件 | 研究资产或依赖上游分类 skill;不进商业包 | +| `26d5a52d` 的 Vivaha Saham 昼夜公式 | 需要在完整解读 Step 16 之前算昼夜,还要改 `consultation_native_layers.build_vivah_saham`(没有出生地时刻);夜生盘的婚姻点会变,需要产品与数据核对 | +| `26d5a52d` 的 Yoga 燃烧状态透传 | 我方 `cmd_yoga` 已分叉(第 5 字段 `R` 逆行),要另核 | +| `0acf7833`、`01f53764`(报告表格与英文报告) | 上游 PL9 渲染器,我方报告链不同 | +| `b4dada9a`、`7e5498f6`(年运计算缓存范围) | 依赖上游 `annual_calculation_cache.py` 等研究回放 | +| `6859b263`(Narayana 小运强度口径)、`2f52cfca`(karaka_calculator)、33 个未接入大运模块 | 第九轮已决定不做或另评估,原因不变 | + +## 跑过的检查 + +见本文件末尾「验证」一节。 + +## 两处 10-03 上游问题的现状 + +1. Narayana 的 K.N. Rao profile:上游 `narayana_dasha_full_report` 现在会把 seed / sequence / duration / dual-lord 传给 `calc_narayana_mahadasha`(另加 `cycle_count`、`strength_profile`)。拿虚构行星位置在三个上升上实跑,`kn_rao` 与 `legacy_local` 的大运序列不同,机制上已修。上游命令行默认仍是 `variant_profile='kn_rao'`,「K.N. Rao 版 Narayana」这个名字指什么仍未说明。我方:默认 legacy 并标为旧算法,另给 Rath 视图,已删 `--variant-profile`;非 legacy profile 同样会传进去。我方没有第二周期和 AD 强度口径。 +2. 8 星制 Chara Karaka:上游 `jaimini.KARAKA_8` 已改为 BPHS 次序(第 5 位 PiK、第 8 位 DK),并有罗睺倒数度数(`974403a6`);我方同样已修。两边一致。 + +## 验证 + +| 检查 | 命令 | 结果 | +| --- | --- | --- | +| 预检 | `scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45` | 全部通过 | +| Python 快速门 | `python scripts/run_quality_gate.py --profile quick --skip-frontend-runtime`(在第 6 个提交 `9d69cd56` 上) | 通过(py_compile 448 个文件,pytest 395 秒) | +| 新测试 | `tests/test_upstream_sync10_*.py` 三个文件 | 31 条通过;改前 D12 18 条失败、日期 10 条失败、KP 1 条失败 | +| 快照与导入 | `test_upstream_import_plan.py`、`test_upstream_git_import_{b9a0ef8f,5fb8379c}.py`、`test_report_orchestrator_reader_contract.py`、`test_repo_privacy_markers.py`、`test_cross_project_contract.py`、`test_import_yinduzhanxing.py` | 通过 | +| 联动相关 | `test_consultation_native_layers.py`、`test_flying_star_audit.py`、`test_full_reading_module_parity.py` | 20 条通过(台账里提到的不稳定用例这次没有失败) | +| 前端 skill | `npx tsx --test tests/skill-*.test.ts tests/consultation-methodology.test.ts` | 48 条:47 通过、1 跳过(本机没有 docker) | diff --git a/docs/tasks/README.md b/docs/tasks/README.md index d5aac2b8..12ddb031 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -552,3 +552,4 @@ | — | — | **#163 星盘页术语与盘上标注(占星师答复 4–8)**:六力达标线、大运 / 小运 / 细运、运主落宫边框与「从运主起算」、Chara 运主、敌友默认上升主星与三种关系、瑜伽不连线、八分法 / Arudha 映射(Claude 直接执行) | 待验收 | `codex/163-terms-overlays` | | `TASK-birth-time-source-intake-20261102.md` | `PROGRESS-birth-time-source-intake-20261102.md` | **#144 第一单(jesse 选 A)**:建档六个出生时间来源选项;不再把来源换成固定误差,改填最早 / 最晚或「不清楚误差」;加列 `id_document`、记录方式、误差是否已知、时辰(只加不删);第二单(分项算稳定性)另开排队 | 待验收(Claude 11-02 完成 T1–T7;T5 原句仓内 0 处;迁移在本机 PostgreSQL 16 跑通,门禁 PG17 待复核;顺带 BUG-1513、1514) | `codex/birth-time-intake-144` | | —(issue #165,Claude 直接执行) | `PROGRESS-birth-time-stability-20261103.md` | **#144 第二单**:星盘页按用户给的出生时间范围逐分钟算九项(上升星座 / 度数 / 宫头、D9、D10 上升、月亮星座 / 星宿 / pada / 宫位),各说一致 / 有变化;先核实原始资料的提示(日期有疑问、时区未确认、上升或月亮星宿变化);大运周期边界原文;上升预检与两个扫描脚本显式传岁差与交点(BUG-1515);只记得时辰第三问,不改数据库 | 本地提交,待验收 | `codex/birth-time-stability-165` | +| —(Claude 直接执行) | `PROGRESS-upstream-sync10-20261104.md` | **上游同步第十轮**:研究快照镜像移到上游 main `5fb8379c`;私人样例换成公开样例;五份参考文档按上游原文更正;引擎三处修正(BUG-1541 D12 联动宫位、BUG-1542 同一观察日期、BUG-1543 KP 原始输入)。Vivaha Saham 昼夜公式与 Yoga 燃烧透传未搬,待产品决定 | 待验收 | `codex/upstream-skill-sync-20261104` | diff --git a/references/ai-reading-workflow-prompt.md b/references/ai-reading-workflow-prompt.md index df8457e3..867f5215 100644 --- a/references/ai-reading-workflow-prompt.md +++ b/references/ai-reading-workflow-prompt.md @@ -15,6 +15,16 @@ ## ⚠️ 核心原则 +### 全文证据边界(2026-10-02) + +标题、开场第一句、摘要、卡片、正文、结尾建议均受相同证据约束。 +先回答问题不等于先作确定断言;条件性迹象在开场也必须保持条件性。 +计算完成、规则条件满足、传统解释、外部复核和现实事件是不同状态。 +未检测、检测失败或尚未验证不能写成“不成立”;可保留带规则与限制的候选格局。 +事业分别说明行业候选、工作方式、公众可见度与发展节奏,逐项绑定10宫星、10宫主、 +D10、A10、功能角色和强度等实际证据。禁止无依据的“幕后、专业交付、晚成”模板。 +不得用名人姓名或已知职业补全盲读;职业象征须保留相近候选与现实核验问题。 + 1. **不跳步**:每个阶段必须完成后才能进入下一阶段 2. **不猜测**:数据缺失时标注"缺失",不编造数据 3. **不锚定**:先输出星盘推导,再等用户反馈,不先知道用户生活再"找依据" @@ -307,7 +317,7 @@ Step 0.5: Karaka 系统识别与声明 **示例输入**: - "1990年1月1日 12:00(示例) 示例城市" - "我出生于 1990-08-15 早上6:30 北京" -- "April 17, REDACTED_YEAR, 2:45 PM, San Francisco, China (36.6N, 114.5E)" +- "June 21, 1935, 11:00 AM, Cajarc, France (44.486N, 1.842E)" **执行指令**(一条命令搞定所有计算): diff --git a/references/argala-complete-guide.md b/references/argala-complete-guide.md index d83b8c28..250f65e0 100644 --- a/references/argala-complete-guide.md +++ b/references/argala-complete-guide.md @@ -1,5 +1,7 @@ # Argala 行星干预体系完整指南 +> 2026-10-14 原文裁决:当前计算以 PVR《Integrated Approach》§§10.5–10.7(正文来源页117–118)为依据。第5宫是次级干预,第9宫是对应阻挡;计都所在参照星座逆向计数;双方数量相等不能直接判受阻,须比较强弱。本指南其余历史扩展(第8宫、性别条件、占位数量直接换算Rajayoga等级)没有在上述原文中得到支持,不得用作当前计算依据。下方旧矩阵只作为历史记录,不代表已核验规则。 + > **适用场景**:分析行星对宫位的"锁门/开门"效应——哪些行星在帮助/阻碍某个宫位的结果实现 > **版本**:v1.0.0 | **创建日期**:2026-04-24 > **优先级**:⭐⭐⭐⭐⭐(K.N. Rao PAC-DARES方法论核心组件) @@ -57,12 +59,12 @@ ### 2.2 次Argala(Secondary Argala) -**规则**:从目标宫位起算,第5宫和第8宫有次级Argala效应: +**当前规则**:从目标宫位起算,第5宫有次级Argala,第9宫阻挡它: | 干预宫位 | 名称 | 含义 | 条件 | |----------|------|------|------| -| **第5宫** | 次级创造Argala | 智慧/创造力的干预 | 仅当该宫内有**男性行星**(Sun/Mars/Jupiter)时生效 | -| **第8宫** | 次级隐藏Argala | 隐藏资源/危机转化的干预 | 仅当该宫内有**女性行星**(Moon/Venus)或**暗行星**(Rahu/Ketu/Saturn)时生效 | +| **第5宫** | 次级Argala | 干预 | 与第9宫配对比较,不附加行星性别条件 | +| **第9宫** | Virodhargala | 阻挡第5宫干预 | 数量相等时须比较强弱 | ### 2.3 Virodha Argala(反干预/解锁) @@ -74,7 +76,6 @@ | 第4宫 | **第10宫** | 外部职责抵消基础支持 | | 第11宫 | **第3宫** | 个人行动抵消网络收益 | | 第5宫(次级) | **第9宫** | 信仰/远见抵消创造力干预 | -| 第8宫(次级) | **第2宫** | 资源消耗抵消隐藏力量 | **判断规则**: - 如果主Argala宫有行星 → 有Argala @@ -272,4 +273,4 @@ E (Exchange) → S (Strength) - K.N. Rao PAC-DARES体系 - B.V. Raman《Three Hundred Important Combinations》 - Sanjay Rath《Jaimini Sutras》Argala章节 -- Parasara《Brihat Parasara Hora Sastra》Argala Adhyaya \ No newline at end of file +- Parasara《Brihat Parasara Hora Sastra》Argala Adhyaya diff --git a/references/cross_project_contract/imports/commit-5fb8379c4f3e649cb4804065c9a3059e4bf633fe.json b/references/cross_project_contract/imports/commit-5fb8379c4f3e649cb4804065c9a3059e4bf633fe.json new file mode 100644 index 00000000..8ed9b97c --- /dev/null +++ b/references/cross_project_contract/imports/commit-5fb8379c4f3e649cb4804065c9a3059e4bf633fe.json @@ -0,0 +1,79 @@ +{ + "generated_at": "2026-10-21T15:49:21Z", + "mirror_files": [ + { + "license": "MIT", + "source": "SKILL.md", + "source_sha256": "5c1c0738fbe6f586fad36cfa13f602934dcea8f8d54c536655f6cb2091773d8d", + "status": "applied", + "target": "references/upstream/yinduzhanxing/SKILL.md", + "target_sha256_after": "5c1c0738fbe6f586fad36cfa13f602934dcea8f8d54c536655f6cb2091773d8d", + "target_sha256_before": "6bed459b8b971b189895e62aabfb90e41dbdf8f5b9e80a4b5330d40922fe21eb" + } + ], + "operator_review_required": true, + "policy_version": 2, + "privacy_scan": { + "rejections": [], + "scanned_files": 1, + "status": "pass" + }, + "protected_rejections": [], + "schema_version": 1, + "semantic_merge_files": [ + { + "diff_summary": "manual semantic merge required: +93/-94 lines", + "path": "SKILL.md", + "source_sha256": "5c1c0738fbe6f586fad36cfa13f602934dcea8f8d54c536655f6cb2091773d8d", + "status": "review_required", + "target_sha256": "552080ec6901198de896012fa38badb4f055857183f7f263038f7dbf232eef4a" + }, + { + "diff_summary": "manual semantic merge required: +234/-177 lines", + "path": "AGENTS.md", + "source_sha256": "34165d7d95ba0b85f18e75ba526fd630ba9fed9abbf522cc2008f99a7d6e90f7", + "status": "review_required", + "target_sha256": "521292fa13037bc06f08c7cc002f506c3f0651eb4c9f2da55290969a8c615594" + }, + { + "diff_summary": "manual semantic merge required: +17/-6 lines", + "path": "references/strict-workflow-router.md", + "source_sha256": "a0bb91ba04bfb1218fa494ec07011c271e073fa9cac4e6f16fe05157554e9a96", + "status": "review_required", + "target_sha256": "b2dc3b8a86cf74d875841ee4714fd1be64bb594503088311e6622556175b90bc" + }, + { + "diff_summary": "manual semantic merge required: +700/-385 lines", + "path": "scripts/unified_consultation_orchestrator.py", + "source_sha256": "2c836861bc0132dee035050ecb4ae14ab9215140d2559086f0d3728c80e546fe", + "status": "review_required", + "target_sha256": "d249528a9f281917c6b895d198053cc81fb0c7d06666302658b11ef79495e7c3" + }, + { + "diff_summary": "manual semantic merge required: +83/-141 lines", + "path": "scripts/report_orchestrator.py", + "source_sha256": "27956a632793735ec660da312b66be967a35c0345edab90b8efe4a65663e4262", + "status": "review_required", + "target_sha256": "0fb80002561a6718088eceed0b20df2a8faa0adbc222756fadf8cd158a6957a6" + }, + { + "diff_summary": "manual semantic merge required: +10060/-4011 lines", + "path": "scripts/jyotish_api_server.py", + "source_sha256": "689468af6d4cfefb4e378ae0de2a6550d5b7b2f04f357a4b16adbb9b245139e5", + "status": "review_required", + "target_sha256": "b3aead367ef53003c72fc2cbdea3d827463d629fc90a173a8095eb81812b0d74" + } + ], + "source_commit": "5fb8379c4f3e649cb4804065c9a3059e4bf633fe", + "source_mode": "git", + "source_repository": "732642856/yinduzhanxing", + "source_repository_url": "https://github.com/732642856/yinduzhanxing", + "source_skill_sha256": "5c1c0738fbe6f586fad36cfa13f602934dcea8f8d54c536655f6cb2091773d8d", + "source_tree_hash": "ee8bd9865cf11a6eb7f90705a262db8b2d145ae0cee6ee6eda32b311548f5aa2", + "target_base_commit": "db2fc3fcfe155c5f5ed64000c46e2a669de54a1f", + "target_repository": "root/Jyotisha", + "tests_run": [ + "tests/test_cross_project_contract.py", + "tests/test_import_yinduzhanxing.py" + ] +} diff --git a/references/cross_project_contract/sync_ledger.json b/references/cross_project_contract/sync_ledger.json index 7a49c416..d488bbd6 100644 --- a/references/cross_project_contract/sync_ledger.json +++ b/references/cross_project_contract/sync_ledger.json @@ -208,6 +208,36 @@ ], "hash_contract_result": "pass: pinned mirror remains b9a0ef8f; batch A is a semantic port of upstream main 5fb8379c; b9a0ef8f was rewritten and its timestamp matches dc3b8fef; SKILL.md was not re-mirrored; importer --apply was not run", "rollback": "git revert 8aed605a43ad8e235cd341ebcaeb7675503325e6 bd09485243000ea7012b865dbee04fc00c535005 c1bd59e97a76e914b884b650cae1adfb13b1e858 7bede9c99a056446648aa1e8fa36b3a4734a36eb 96312893b7ad7660e72081c81bf4bf031f0b38a0 b35c424945e578abe95fca69de5eff1a8e14c0e1 16bae006158aad8b493362ce1a51391626a95dca 9d3f59a87206d4b7f6c10f94c56565bbd98089a6 d226b0e5388cc3606272956bc5b9c2072685210d on branch codex/upstream-sync9-20261028" + }, + { + "source_repository": "732642856/yinduzhanxing", + "source_commit": "5fb8379c4f3e649cb4804065c9a3059e4bf633fe", + "target_repository": "jesse-ux/Jyotisha", + "target_commit": "9d69cd5625456c452949da2e47c7ad68332b2b46", + "change_class": "calculation_contract", + "copied_files": [ + "references/upstream/yinduzhanxing/SKILL.md", + "references/upstream/yinduzhanxing/source-manifest.json", + "references/upstream/yinduzhanxing/import-plan.json", + "references/cross_project_contract/imports/commit-5fb8379c4f3e649cb4804065c9a3059e4bf633fe.json", + "references/ai-reading-workflow-prompt.md", + "references/pdf-chart-reading-guide.md", + "references/argala-complete-guide.md", + "references/sahams-execution-guide.md", + "references/shodasavarga-complete-guide.md", + "references/yogi-avayogi-system.md", + "references/event_judgment_wealth.md", + "scripts/cmd_narayana_dasha.py", + "scripts/cmd_solar_return.py", + "scripts/jyotish_engine.py" + ], + "dependency_delta": "none", + "privacy_review": "pass: mirrored SKILL.md drops the private sample wording; the leftover private sample date, time and place in two references and two usage strings are replaced with upstream's public example; no production configuration, secret, or user data", + "focused_tests": [ + "python -m pytest tests/test_upstream_import_plan.py tests/test_upstream_git_import_b9a0ef8f.py tests/test_upstream_git_import_5fb8379c.py tests/test_report_orchestrator_reader_contract.py tests/test_repo_privacy_markers.py tests/test_upstream_sync10_d12_linkage.py tests/test_upstream_sync10_observation_date.py tests/test_upstream_sync10_kp_unrounded_inputs.py" + ], + "hash_contract_result": "pass: git-mode mirror moved to 5fb8379c with scripts/import_yinduzhanxing.py --apply from a clean clone; semantic-merge paths stay review_required; three engine fixes are semantic ports of cf6c316d/f60568a0, 933f254e and the KP part of 26d5a52d", + "rollback": "git revert 9d69cd5625456c452949da2e47c7ad68332b2b46 8137c692b1bbe862bef83b13910437cb2549eb05 4b6aa08726cb5ec4c179c7ad89ec3e25ecfbe7d6 2539d57fee9da72f97045577279aaa3bdb3796fc 297e27e0bda9232d7de1c5747e1a14be3845db62 3bae79fbcae80a8db644f7f0252fadb1c9a5c4bc on branch codex/upstream-skill-sync-20261104" } ] } diff --git a/references/event_judgment_wealth.md b/references/event_judgment_wealth.md index 03ea84d4..003bc978 100644 --- a/references/event_judgment_wealth.md +++ b/references/event_judgment_wealth.md @@ -158,7 +158,7 @@ - `lakshmi_dhana_activation_chain` - `yogi_asc_tight_orb_wealth` -- `strict-workflow-router.md` 的 `wealth-timing-strict` +- `strict-workflow-router.md` 的 `finance-timing-strict`(兼容 `wealth-timing-strict`) - `divisional-chart-deep-reading.md` 中的财富链 `D2 -> D4 -> D10 -> D11` 若调用不到,必须在 Audit Table 里标注其对置信度的削弱。 diff --git a/references/pdf-chart-reading-guide.md b/references/pdf-chart-reading-guide.md index 5b20483c..68267544 100644 --- a/references/pdf-chart-reading-guide.md +++ b/references/pdf-chart-reading-guide.md @@ -53,9 +53,9 @@ | 出生日期 | 1990-01-01 | 年月日 | | 出生时间 | 12:00:19 | 时分秒,越精确越好 | | 出生地点 | 示例城市 | 城市名 | -| 经度 | 115.5°E | 东经/西经 | -| 纬度 | 36.6°N | 北纬/南纬 | -| 时区 | UTC+8 | 相对于UTC | +| 经度 | 122.4°W | 东经/西经 | +| 纬度 | 37.8°N | 北纬/南纬 | +| 时区 | UTC-8 | 相对于UTC | | 性别 | Male/Female | 用于关系分析征象星选择 | | Ayanamsa | Lahiri 23°51'11" | 恒星黄道校正值 | @@ -244,9 +244,9 @@ "date": "1990-01-01", "time": "12:00:19", "location": "San Francisco", - "latitude": 36.6, - "longitude": 115.5, - "timezone": "UTC+8", + "latitude": 37.7749, + "longitude": -122.4194, + "timezone": "UTC-8", "gender": "Male", "ayanamsa": "Lahiri 23°51'11\"" }, diff --git a/references/sahams-execution-guide.md b/references/sahams-execution-guide.md index 2af6b335..9440189d 100644 --- a/references/sahams-execution-guide.md +++ b/references/sahams-execution-guide.md @@ -47,7 +47,10 @@ ## 四、当前真实边界 -当前 skill 已能输出常见 36 个 Saham 与婚姻点等专项点。 +2026-10-14计算链更新:通用计算器已接入PVR表74全部36项及其昼夜例外。 +来源为《Integrated Approach》印刷页394–396;并保留独立婚姻点入口。 +额外旧候选项仍单独标为 `legacy_candidate_unverified`,不属于这36项的认证范围。 +这项源码更新不等于客户冻结版本已经更新或报告已经重新发布。 但必须保留的边界: diff --git a/references/shodasavarga-complete-guide.md b/references/shodasavarga-complete-guide.md index 0f25b24e..3b861b6a 100644 --- a/references/shodasavarga-complete-guide.md +++ b/references/shodasavarga-complete-guide.md @@ -56,8 +56,8 @@ BPHS提供的行星功能性吉凶量化系统(与Shadbala天文强度互补 | D16 | — | — | 1 | 2 | | D20 | — | — | — | 0.5 | | D24 | — | — | — | 0.5 | -| D27 | — | — | — | 1 | -| D30 | 1 | 1 | 1.5 | 1.5 | +| D27 | — | — | — | 0.5 | +| D30 | 1 | 1 | 1.5 | 1 | | D40 | — | — | — | 0.5 | | D45 | — | — | — | 0.5 | | D60 | — | — | **5** | **4** | diff --git a/references/upstream/yinduzhanxing/SKILL.md b/references/upstream/yinduzhanxing/SKILL.md index f5ee73ee..87585a4b 100644 --- a/references/upstream/yinduzhanxing/SKILL.md +++ b/references/upstream/yinduzhanxing/SKILL.md @@ -7,11 +7,14 @@ description: 印度占星(Jyotish)专业解盘与推运系统。核心能力 # 印度占星专业解盘与推运系统 > **版本**:v6.9.15 | **详细变更**:`CHANGELOG.md` -> **对标状态**:中文用户端与技法覆盖领先;D1/D9/AV/Chara 等有守门,Dasha/Shadbala 外部 oracle 扩充仍在进行。 +> **对标状态**:中文用户端与技法覆盖领先;D1/D9/AV/Chara 等有守门,Dasha/Shadbala/Tajika 已进入分层证据治理阶段:部分 target-set / packet / source-bounded 链路已闭合,但 formal case admission、same-chart parity、production tuning 与 global oracle closure 仍不得过度宣称。 > -> **真相边界**:当前问题已不是“完全缺技法名称”,而是少数高价值传统深度仍未闭环;请优先修复精度与裁决链,而不是继续表面堆功能名。读取能力状态时必须优先使用 `references/oracle/effective_skill_capability_view_2026_07_19.json` 与 `references/oracle/skill_truth_overlay_2026_07_19.json`,不得直接把 `references/technique_registry.json` 的旧 `covered` 当作完整闭环。 -> **Ashtottari(2026-09-01)**:规则族冲突锁已部分解开 → `selected_rule_family_parameter_sensitive`;主源验证文档 `docs/research/ashtottari_primary_text_verification_2026_09_01.md`,适用族 `rahu_from_lagna_lord_kendra_trikona_plus_paksha_daynight`({4,5,7,9,10});未闭环项:第二引擎 oracle(`attach_external_same_profile_comparison`)。yn-1993 本盘不适用。 +> **当前真源索引(2026-09-15)**:读取能力状态时,优先使用 `docs/research/current_capability_truth_index_2026_09_15.md`、`python3 scripts/current_report_authority_snapshot.py --format json`、`python3 scripts/workspace_governance_audit.py`、`docs/research/pl9_full_parity_dashboard_2026_09_11.md` 与当前质量门输出。`references/oracle/effective_skill_capability_view_2026_07_19.json`、`references/oracle/skill_truth_overlay_2026_07_19.json`、`skill_gap_truth_audit_latest.md` 等旧快照只能作历史边界,不得覆盖 9 月以来的 closure / authority / report-export 事实。 +> **真相边界**:当前问题已不是“完全缺技法名称”,而是高价值传统深度的升格门槛:packet complete、source-bounded wired、external verified、formal admitted、production tuning allowed、global closure 必须分开写。请优先修复精度、证据仲裁、报告交付和状态真源,而不是继续表面堆功能名。不得直接把 `references/technique_registry.json` 的旧 `covered` 当作完整闭环。 +> **Ashtottari(2026-09-01)**:规则族冲突锁已部分解开 → `selected_rule_family_parameter_sensitive`;主源验证文档 `docs/research/ashtottari_primary_text_verification_2026_09_01.md`,适用族 `rahu_from_lagna_lord_kendra_trikona_plus_paksha_daynight`({4,5,7,9,10});未闭环项:第二引擎 oracle(`attach_external_same_profile_comparison`)。适用性必须按当前输入独立计算。 > **执行总控**:`references/quick-reference-guide.md` +> **分类 skill 联用**:按 `references/skill_family_router.json` 与本轮 `selected_skill_families` 逐个读取 `skills//SKILL.md`,遵循 `references/skill_family_handoff.md` 交接。综合模式各传统并列解释;路由记录不代表真实执行。 +> **技法查漏编译链**:`references/technique_ontology.json` 统一身份与别名。先运行 `python3 scripts/technique_source_adapters.py --write` 刷新 JH/PyJHora 命名技法和本地书籍主题候选,再运行 `python3 scripts/technique_gap_scanner.py --write --check-web` 对照注册表、书籍规则链和源码,生成各 skill 的缺口队列与用法包。文本命中或身份归并不等于可执行。 > **严格路由**:`references/strict-workflow-router.md`(涉及事业/婚恋/财务/应期/技法验证时必须优先读取) > **机器注册表**:`references/oracle/effective_skill_capability_view_2026_07_19.json` + `references/oracle/skill_truth_overlay_2026_07_19.json` + `references/technique_registry.json` + `scripts/audit_capabilities.py` > **文章级细节模板**:`references/interpretation_template_registry.json` + `scripts/validate_interpretation_templates.py` @@ -25,7 +28,7 @@ description: 印度占星(Jyotish)专业解盘与推运系统。核心能力 | 分盘 | D1-D144 + D2/D3变体 + 复合D-m×n + 自定义D-N(2-300) | | Bhava Chalit | Sripati/Porphyry/Equal/Whole Sign/Placidus/Koch 不等宫位调整 | | Sudarshana | Asc/Moon/Sun 三参考点盘 + 宫位收敛分析 | -| Shadbala | absolute Rupa 分量求和;内部不变量通过,外部绝对值 oracle 扩充中 | +| Shadbala | absolute Rupa 分量求和;入口 profile parity 与 packet fields 已推进,same-chart absolute parity / residual attribution / second oracle 仍需守门 | | Ashtakavarga | BAV+SAV+PAV(展开式)+Sodhita(净化式) | | KP系统 | reference-only / partial;不得按完整主链能力宣称,须以 `skill_truth_overlay` 为准 | | 合盘 | 16因子36分制(Ashtakoot+Kuta) | @@ -122,7 +125,7 @@ adapter available 解释为已完成 VedAstro、PyJHora/JHora 或 jyotishganit r VedAstro 官方 MCP 端点(`https://mcp.vedastro.org/api/mcp/public`)initialize/tools-list 正常, 但 tools/call 层已知 resolver 故障(`Invalid or Outdated Call`)。**默认计算通路改为 REST 桥直连**: -- 脚本:`scripts/vedastro_rest_bridge.py`(horoscope / dasa 子命令,默认 yn-1993 参数) +- 脚本:`scripts/vedastro_rest_bridge.py`(horoscope / dasa 子命令,默认公开回放基准参数) - 端点:`https://api.vedastro.org/api/Calculate`(已实测 `Status: Pass`,绕过 MCP resolver 故障) - 决策记录:`docs/research/vedastro_rest_bridge_default_decision_2026_09_01.md` - REST 桥返回 `Status: Pass` 视为官方 raw evidence 可用(对应 `official_verified`);失败时降级 `local_fallback` @@ -421,15 +424,16 @@ python3 scripts/validate_interpretation_templates.py --format markdown - Bhrigu Pada / RTN / Tithi Lord / Pancha Pakshi 必须作为辅助确认层;若没有主承诺和推运激活,输出置信度不得超过 C。 - 所有文章/课程/网上说法默认归入 B/C 级线索,必须经过注册表中的 `required_cross_checks` 与 `forbidden_claims` 过滤。 -## 当前最硬的未闭环点 +## 当前最硬的升格门槛 -> 这部分比“再加几个技法名”更重要,决定 skill 距离传统软件级深度还有多远。 +> 这部分不是说基础能力不存在,而是说明哪些层级尚未从 `packet/source-bounded/verified slice` 升格到 `formal admitted/global closure/production tuning allowed`。 -1. **Dasha 外部绝对边界闭环** - - 当前 `ready_for_calibration: 4` - - Dasha-only 目标集已由 PyJHora 黑盒证据推进到 3/3,但全局 Dasha/Shadbala 队列仍有非 Dasha 靶点缺字段,`production_tuning_allowed: false` -2. **Shadbala 外部绝对值闭环** - - 当前 absolute Rupa 结构自洽,但还不是外部绝对值完全校准 +1. **Dasha promotion gates** + - 多数 Dasha 链路已 source-bounded wired;下一步是 p92 selector、p93-p103 Rashi selector/duration、p109-p120 row-date / child-row profile、p132/p133/p135 minute-boundary gates。 + - 不得把同案 producer 输出直接升格为 PL9 universal selector 或 formal case admission。 +2. **Shadbala same-chart absolute parity** + - 入口 profile parity 与 packet fields 已推进;当前重点是固定同盘/ayanamsa/node mode 后,拿到安全第二来源原始表并解释 Mercury Chesta / Sthana residual。 + - `production_tuning_allowed: false` 前,禁止调生产常数贴合单一截图或 PDF。 3. **Chara Dasha 共主仲裁尾差** - KN Rao benchmark 已过,但 Aquarius/Scorpio 的 Rahu/Ketu 共主强弱仲裁仍有尾差 4. **KP ruling planets / 事件裁决细节** @@ -437,7 +441,7 @@ python3 scripts/validate_interpretation_templates.py --format markdown 5. **Prashna 分支工作流** - 问事类型分支、裁决链、时机判断仍需更稳定的传统链路 6. **Varshaphala / Tajika / Sahams 年运裁决深度** - - 年盘骨架已在,第一条 Steve Jobs 1984 外部年运样本已闭环;但整体仍只有 `1/5`,事件裁决、权重层、Einstein 1905 等后续样本仍需继续成熟 + - 第一目标包已推进到 target-set verified;下一步是第二波公开样本、多年/边界样本、judgment templates 与 Mudda Dasha 事件链,不是继续证明年盘骨架存在。 7. **Kalachakra / Narayana 等替代 Dasha 的成熟度边界** - 已有覆盖,但部分子层、边界口径、外部黑盒对照仍需继续收紧 8. **高阶解释层整合** @@ -558,7 +562,7 @@ $PYTHON $SCRIPT <子命令> [参数] | `aspects` | 度数精确相位系统 | | `jaimini` | Jaimini Karaka/Karakamsha、A1-A12/UL、Graha Pada、Special Lagnas;Chara Dasha timing 为 KN Rao Method(covered;仍需保留共主仲裁与外部对标边界) | | `nakshatra-adv` | 高级Nakshatra(Tara Bala+Chandra Bala+Sub-Lord) | -| `nakshatra-dasha` | 星宿大运推演(Ashtottari + Nakshatra-level Vimshottari);**Ashtottari 状态 2026-09-01 解冻**:`blocked_rule_source_conflict` → `selected_rule_family_parameter_sensitive`(可执行但 unverified;适用判定需走 `rahu_from_lagna_lord_kendra_trikona_plus_paksha_daynight` 族,详见 `docs/research/ashtottari_primary_text_verification_2026_09_01.md`,yn-1993 本盘因 Rahu 距太阳 8 宫不适用) | +| `nakshatra-dasha` | 星宿大运推演(Ashtottari + Nakshatra-level Vimshottari);**Ashtottari 状态 2026-09-01 解冻**:`blocked_rule_source_conflict` → `selected_rule_family_parameter_sensitive`(可执行但 unverified;适用判定需走 `rahu_from_lagna_lord_kendra_trikona_plus_paksha_daynight` 族,详见 `docs/research/ashtottari_primary_text_verification_2026_09_01.md`,结果随当前盘独立判定) | | `nakshatra-full` | 星宿综合报告(本命 + 大运 + 过境星宿) | | `argala` | Argala门闩系统:主 Argala + Virodhargala + Rajayoga 分类 | | `tajika` | Tajika年运盘(Muntha+YearLord+Mudda Dasha) | diff --git a/references/upstream/yinduzhanxing/import-plan.json b/references/upstream/yinduzhanxing/import-plan.json index b5cb54b0..af60c07b 100644 --- a/references/upstream/yinduzhanxing/import-plan.json +++ b/references/upstream/yinduzhanxing/import-plan.json @@ -2,9 +2,9 @@ "schema_version": 1, "source_repository": "732642856/yinduzhanxing", "source_repository_url": "https://github.com/732642856/yinduzhanxing", - "source_commit": "b9a0ef8f0f0de8714982a5aa7100f5aea16c9d09", - "source_tree_sha256": "9ae737b02a48b38dca7d78094413b961a75e5abad17f2fe206f7dc3b1401d51a", - "boundary": "Task 1 git-mode snapshot b9a0ef8f from origin/codex/add-birth-time-rectification-skill (not on main). The importer still mirrors only the pinned upstream root SKILL.md under references/upstream; none of these research scripts is copied into the commercial runtime. Semantic-merge destinations remain review-only except for files this commercial round copies by hand.", + "source_commit": "5fb8379c4f3e649cb4804065c9a3059e4bf633fe", + "source_tree_sha256": "ee8bd9865cf11a6eb7f90705a262db8b2d145ae0cee6ee6eda32b311548f5aa2", + "boundary": "Tenth sync: git-mode snapshot 5fb8379c, upstream main HEAD on 2026-11-04. The nine plan sources below still exist at that commit. The importer still mirrors only the pinned upstream root SKILL.md under references/upstream; none of these research scripts is copied into the commercial runtime. Semantic-merge destinations remain review-only except for files this commercial round copies by hand.", "entries": [ { "source": "scripts/evidence_maturity.py", diff --git a/references/upstream/yinduzhanxing/source-manifest.json b/references/upstream/yinduzhanxing/source-manifest.json index 667e82ea..9abb1025 100644 --- a/references/upstream/yinduzhanxing/source-manifest.json +++ b/references/upstream/yinduzhanxing/source-manifest.json @@ -1,16 +1,16 @@ { - "boundary": "Git source b9a0ef8f0f0de8714982a5aa7100f5aea16c9d09 was origin/codex/add-birth-time-rectification-skill (branch=codex/add-birth-time-rectification-skill, not on main). Only root SKILL.md is mirrored into this directory; commercial and rectification Skills remain protected. skill_sha256 is retained as a legacy alias of source_skill_sha256. Semantic-merge paths were reviewed by the importer and not overwritten. Ninth sync on 2026-10-28 (issue #141) follows upstream main 5fb8379c4f3e649cb4804065c9a3059e4bf633fe for batch A only: Yogini d268d519, Tithi 33537b31, Saham cb8a1ca7, Ashtottari 0712f98f, Kalachakra f353a096, Arudha 8b55630a, Darakaraka 45b3b66b, dasha boundary test 07fc5be3, aspects e628118d. b9a0ef8f was rewritten and is absent; its commit time matches dc3b8fef7f769758dd0e63bdab037cde8be194bc. source_commit, source_git_tree, and the skill hashes stay on the mirrored SKILL.md and were not re-imported.", + "boundary": "Git source 5fb8379c4f3e649cb4804065c9a3059e4bf633fe is upstream main HEAD on 2026-11-04 (no upstream commits after it). Tenth sync on 2026-11-04 re-mirrors only root SKILL.md into this directory with scripts/import_yinduzhanxing.py --apply from a clean clone; commercial and rectification Skills remain protected. skill_sha256 is retained as a legacy alias of source_skill_sha256. Semantic-merge paths were reviewed by the importer and not overwritten. The previous mirror b9a0ef8f (rewritten upstream; commit time matches dc3b8fef) keeps its import record under references/cross_project_contract/imports as history. Engine content between dc3b8fef and 5fb8379c was ported semantically in syncs 8, 9 and 9b and is not implied by this mirror.", "import_policy_version": 2, - "imported_at": "2026-09-09T07:40:00Z", + "imported_at": "2026-11-04T04:00:00Z", "license": "MIT", "schema_version": 1, - "skill_sha256": "6bed459b8b971b189895e62aabfb90e41dbdf8f5b9e80a4b5330d40922fe21eb", - "source_commit": "b9a0ef8f0f0de8714982a5aa7100f5aea16c9d09", - "source_committed_at": "2026-09-08T17:36:09Z", - "source_git_tree": "864cab471cadb31b71a7256c995c12b7552d7cb5", + "skill_sha256": "5c1c0738fbe6f586fad36cfa13f602934dcea8f8d54c536655f6cb2091773d8d", + "source_commit": "5fb8379c4f3e649cb4804065c9a3059e4bf633fe", + "source_committed_at": "2026-10-21T15:49:21Z", + "source_git_tree": "6cd6160804ea7419d609ccd639cc537a8a81c3c9", "source_mode": "git", "source_repository": "732642856/yinduzhanxing", "source_repository_url": "https://github.com/732642856/yinduzhanxing", - "source_skill_sha256": "6bed459b8b971b189895e62aabfb90e41dbdf8f5b9e80a4b5330d40922fe21eb", - "source_tree_sha256": "9ae737b02a48b38dca7d78094413b961a75e5abad17f2fe206f7dc3b1401d51a" + "source_skill_sha256": "5c1c0738fbe6f586fad36cfa13f602934dcea8f8d54c536655f6cb2091773d8d", + "source_tree_sha256": "ee8bd9865cf11a6eb7f90705a262db8b2d145ae0cee6ee6eda32b311548f5aa2" } diff --git a/references/yogi-avayogi-system.md b/references/yogi-avayogi-system.md index cf5f1242..e1f07acd 100644 --- a/references/yogi-avayogi-system.md +++ b/references/yogi-avayogi-system.md @@ -1,5 +1,12 @@ # Yogi / Ava Yogi / Duplicate Yogi 行星系统 +> 2026-09-26 校正(上游 5fb8379c 同步,2026-11-04 移入):下文是保留的历史混合笔记,不是当前可执行公式。 +> 其中“Lagna+93°20′”、93°20′=6宿、结果为双子6°40′等说法相互矛盾,不能用于计算。 +> 当前原生 profile 为 Sun+Moon+93°20′,Avayogi 再加186°40′;93°20′是巨蟹3°20′, +> 精确宿界归入 Pushya(Saturn)。计算以 `scripts/annual_native/yogi_sphuta.py` +> (`YOGI_OFFSET`、`AVA_OFFSET`)为准。 +> 未找到明确来源的 Lagna 变体保持待考,不与当前日月公式合并。 + > 来源:公众号文章(1-4印度占星),整合 Tithi Lord、财富业力、灵性距离、Dasha/Transit 激活等跨文章知识点。 > 版本:v1.0 | 2026-04-25 diff --git a/scripts/cmd_narayana_dasha.py b/scripts/cmd_narayana_dasha.py index 679b9453..b74aad78 100644 --- a/scripts/cmd_narayana_dasha.py +++ b/scripts/cmd_narayana_dasha.py @@ -13,8 +13,8 @@ def cmd_narayana_dasha(args, chart_data): narayana-dasha 子命令:计算并报告 Narayana Dasha(Rishi Dasha)。 用法: - python jyotish_engine.py narayana-dasha --year REDACTED_YEAR --month 4 --day 17 \\ - --hour 14 --minute 45 --lat 36.47 --lon 114.2 --tz 8 --age 33 + python jyotish_engine.py narayana-dasha --year 1935 --month 6 --day 21 \\ + --hour 11 --minute 0 --lat 44.486 --lon 1.842 --tz 1 --age 33 """ if chart_data is None: return "\n".join([ diff --git a/scripts/cmd_solar_return.py b/scripts/cmd_solar_return.py index 9e7b1385..8f0b4688 100644 --- a/scripts/cmd_solar_return.py +++ b/scripts/cmd_solar_return.py @@ -5,8 +5,8 @@ cmd_solar_return: Solar Return (Varshaphala) CLI 子命令 Jyotish Vedic Astrology Skill — v6.0.18 用法: - python3 jyotish_engine.py solar-return --year REDACTED_YEAR --month 4 --day 17 \ - --hour 14 --minute 45 --lat 36.4667 --lon 114.2 --tz 8 --target-year 2026 + python3 jyotish_engine.py solar-return --year 1935 --month 6 --day 21 \ + --hour 11 --minute 0 --lat 44.486 --lon 1.842 --tz 1 --target-year 2026 """ import sys diff --git a/scripts/jyotish_engine.py b/scripts/jyotish_engine.py index 44aa60da..633f611a 100644 --- a/scripts/jyotish_engine.py +++ b/scripts/jyotish_engine.py @@ -587,7 +587,7 @@ def calc_dispositor_chain(planet_name, planets_data, max_depth=12): return chain -def calc_inter_chart_linkage(planet_name, d1_data, d9_data, d10_data, d12_data=None, d1_asc_idx=None, d9_asc_idx=None, d10_asc_idx=None): +def calc_inter_chart_linkage(planet_name, d1_data, d9_data, d10_data, d12_data=None, d1_asc_idx=None, d9_asc_idx=None, d10_asc_idx=None, d12_asc_idx=None): """ 计算行星在 D1/D9/D10 分盘之间的飞星落宫。 返回 {chart_type: {sign, house_in_chart, lord_of_sign_in_chart}} @@ -599,7 +599,8 @@ def calc_inter_chart_linkage(planet_name, d1_data, d9_data, d10_data, d12_data=N ('D10', d10_data, d10_asc_idx), ] if d12_data: - charts.append(('D12', d12_data, None)) + # D12 houses count from the D12 ascendant (upstream cf6c316d / f60568a0). + charts.append(('D12', d12_data, d12_asc_idx)) for chart_name, chart_data, asc_idx in charts: if not isinstance(chart_data, dict): @@ -608,9 +609,14 @@ def calc_inter_chart_linkage(planet_name, d1_data, d9_data, d10_data, d12_data=N if not isinstance(pdata, dict) or 'sign' not in pdata: continue sign = pdata['sign'] - sign_idx = SIGNS.index(sign) if sign in SIGNS else 0 + if sign not in SIGNS: + # An unknown sign used to become Aries silently. + raise ValueError(f'Unknown sign in {chart_name}: {sign!r}') + sign_idx = SIGNS.index(sign) house = None if asc_idx is not None: + if isinstance(asc_idx, bool) or not isinstance(asc_idx, int) or not 0 <= asc_idx < 12: + raise ValueError(f'Invalid ascendant sign index in {chart_name}: {asc_idx!r}') house = ((sign_idx - asc_idx) % 12) + 1 lord = SIGN_LORDS.get(sign, '') result[chart_name] = { @@ -655,7 +661,7 @@ def calc_all_dispositor_chains(planets_data): return result -def calc_all_inter_chart_linkages(d1_data, d9_data, d10_data, d12_data=None, d1_asc_idx=None, d9_asc_idx=None, d10_asc_idx=None): +def calc_all_inter_chart_linkages(d1_data, d9_data, d10_data, d12_data=None, d1_asc_idx=None, d9_asc_idx=None, d10_asc_idx=None, d12_asc_idx=None): """ 为所有行星计算 D1/D9/D10 分盘间飞星。 需要各分盘的 asc_idx 来正确计算落宫。 @@ -664,7 +670,7 @@ def calc_all_inter_chart_linkages(d1_data, d9_data, d10_data, d12_data=None, d1_ for pname in ['Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu']: result[pname] = calc_inter_chart_linkage( pname, d1_data, d9_data, d10_data, d12_data, - d1_asc_idx, d9_asc_idx, d10_asc_idx + d1_asc_idx, d9_asc_idx, d10_asc_idx, d12_asc_idx ) return result @@ -13765,6 +13771,12 @@ def cmd_kp(args): _apply_ayanamsa(requested_report_ayanamsa) return {"error": "swisseph未安装"} + # The chart's julian_day is rounded for display. KP cusp and sub-lord + # boundaries use the civil instant itself, seconds included (upstream + # 26d5a52d, BUG-1543). Same formula as compute_chart_data. + jd = swe.julday(args.year, args.month, args.day, + _birth_hour_decimal(args.hour, args.minute, _arg_second(args)) - args.tz) + asc_sign = chart.get("ascendant", {}).get("sign", "Aries") planets = chart.get("planets", {}) planet_lons = { @@ -13778,7 +13790,8 @@ def cmd_kp(args): from bhava_chalit import BhavaChalitCalculator kp_house_calculator = BhavaChalitCalculator() asc_lon = chart.get('ascendant', {}).get('degree_raw', chart.get('ascendant', {}).get('degree', 0.0)) - boundaries = kp_house_calculator.calculate_bhava_boundaries( + # Unrounded cusps; calculate_bhava_boundaries rounds them to 4 decimals. + raw_cusps = kp_house_calculator.calculate_cusps( float(asc_lon), 0.0, 'placidus', jd, args.lat, args.lon, ) bhava_chart = kp_house_calculator.get_bhava_chalit_chart( @@ -13792,16 +13805,19 @@ def cmd_kp(args): for pname, pdata in planets.items(): if isinstance(pdata, dict) and 'sign' in pdata: bhava_row = bhava_planets.get(pname) if isinstance(bhava_planets.get(pname), dict) else {} + raw_lon = planet_lons.get(pname) planet_positions[pname] = { 'sign': pdata['sign'], - 'degree': pdata.get('degree_in_sign', pdata.get('degree', 0) % 30), + # degree_in_sign is rounded to 4 decimals for display. + 'degree': (float(raw_lon) % 30 if raw_lon is not None + else pdata.get('degree_in_sign', pdata.get('degree', 0) % 30)), 'house': bhava_row.get('bhava_house', pdata.get('house', 1)), } result = calc_kp_analysis( planet_positions, asc_sign, - house_cusps=boundaries.get('cusps'), + house_cusps=raw_cusps, house_occupancy_basis='cusp_to_cusp', ) moon_lon = planets.get('Moon', {}).get('degree_raw', planets.get('Moon', {}).get('degree', 0.0)) if isinstance(planets.get('Moon'), dict) else 0.0 @@ -15856,7 +15872,7 @@ def cmd_full_reading(args): ic_result = calc_all_inter_chart_linkages( d1_for_dc, d9_for_ic, d10_for_ic, d12_for_ic, - asc_idx, d9_asc_idx, d10_asc_idx + asc_idx, d9_asc_idx, d10_asc_idx, d12_asc_idx ) report['modules']['inter_chart_linkage'] = ic_result @@ -16124,7 +16140,10 @@ def cmd_full_reading(args): from narayana_dasha import attach_narayana_method_comparison, narayana_dasha_full_report # 计算当前年龄(从出生到 today) birth_dt = _birth_datetime_from_args(args) - today_dt = datetime.strptime(args.today, '%Y-%m-%d') if hasattr(args, 'today') and args.today else datetime.now() + # One observation date for the whole report (upstream 933f254e, BUG-1542). + today_dt = datetime.strptime( + getattr(args, 'transit_date', None) or getattr(args, 'today', None) + or datetime.now().strftime('%Y-%m-%d'), '%Y-%m-%d') current_age = (today_dt - birth_dt).days / 365.25 narayana_result = narayana_dasha_full_report( lagna_sign_idx=asc_idx, @@ -16451,7 +16470,9 @@ def cmd_full_reading(args): if nak_age is None: try: birth_date = datetime(args.year, args.month, args.day) - nak_age = (datetime.now() - birth_date).days / 365.25 + nak_reference = getattr(args, 'transit_date', None) or getattr(args, 'today', None) + observation = datetime.strptime(nak_reference, '%Y-%m-%d') if nak_reference else datetime.now() + nak_age = (observation - birth_date).days / 365.25 except: nak_age = None @@ -16490,8 +16511,13 @@ def cmd_full_reading(args): if age is None: # 自动计算年龄 try: - birth_date = datetime(args.year, args.month, args.day) - age = (datetime.now() - birth_date).days // 365 + # Calendar-birthday age on the report's observation date; days // 365 + # drifted about one day early every four years (upstream 933f254e). + ref_today = datetime.strptime( + getattr(args, 'transit_date', None) or getattr(args, 'today', None) + or datetime.now().strftime('%Y-%m-%d'), '%Y-%m-%d') + age = ref_today.year - int(args.year) - ( + (ref_today.month, ref_today.day) < (int(args.month), int(args.day))) report['warnings'].append(f"未提供年龄,自动计算为 {age} 岁") except: age = None diff --git a/scripts/run_quality_gate.py b/scripts/run_quality_gate.py index c3d3a461..f13beb74 100644 --- a/scripts/run_quality_gate.py +++ b/scripts/run_quality_gate.py @@ -131,6 +131,7 @@ CORE_PYTEST_TARGETS = [ # Summary lines by default; failure keeps the last 200 lines and a full log (BUG-1230). "tests/test_run_quality_gate_output.py", "tests/test_upstream_git_import_b9a0ef8f.py", + "tests/test_upstream_git_import_5fb8379c.py", "tests/test_interpretation_template_registry.py", "tests/test_vedastro_external_technique_evidence.py", # Self-hosted heading font slices: 6500-character coverage, no overlap, 120 KB cap. diff --git a/tests/test_upstream_git_import_5fb8379c.py b/tests/test_upstream_git_import_5fb8379c.py new file mode 100644 index 00000000..94515a90 --- /dev/null +++ b/tests/test_upstream_git_import_5fb8379c.py @@ -0,0 +1,37 @@ +"""Git-mode import record for upstream main 5fb8379c (tenth sync, 2026-11-04). Does not replace the 5db72537 archive pin test.""" + +from __future__ import annotations + +import hashlib +import json +import re +from pathlib import Path + +from jsonschema import Draft202012Validator, FormatChecker + +ROOT = Path(__file__).resolve().parents[1] +RECORD = ROOT / "references/cross_project_contract/imports/commit-5fb8379c4f3e649cb4804065c9a3059e4bf633fe.json" +SOURCE_MANIFEST = ROOT / "references/upstream/yinduzhanxing/source-manifest.json" +SKILL_SNAPSHOT = ROOT / "references/upstream/yinduzhanxing/SKILL.md" +SCHEMA = ROOT / "references/cross_project_contract/import_manifest.schema.json" +COMMIT = "5fb8379c4f3e649cb4804065c9a3059e4bf633fe" + + +def test_git_import_record_matches_mirrored_skill_and_manifest() -> None: + record = json.loads(RECORD.read_text(encoding="utf-8")) + manifest = json.loads(SOURCE_MANIFEST.read_text(encoding="utf-8")) + schema = json.loads(SCHEMA.read_text(encoding="utf-8")) + Draft202012Validator(schema, format_checker=FormatChecker()).validate(record) + + assert record["source_mode"] == "git" + assert record["source_commit"] == COMMIT + assert re.fullmatch(r"[0-9a-f]{40}", record["source_commit"]) + assert re.fullmatch(r"[0-9a-f]{64}", record["source_tree_hash"]) + assert record["source_tree_hash"] == manifest["source_tree_sha256"] + assert record["source_skill_sha256"] == manifest["source_skill_sha256"] + snapshot_hash = hashlib.sha256(SKILL_SNAPSHOT.read_bytes()).hexdigest() + assert snapshot_hash == record["source_skill_sha256"] == record["mirror_files"][0]["target_sha256_after"] + assert record["mirror_files"][0]["status"] == "applied" + assert manifest["source_mode"] == "git" + assert manifest["source_commit"] == COMMIT + assert all(row["status"] == "review_required" for row in record["semantic_merge_files"]) diff --git a/tests/test_upstream_git_import_b9a0ef8f.py b/tests/test_upstream_git_import_b9a0ef8f.py index cb40d6fb..795d54c8 100644 --- a/tests/test_upstream_git_import_b9a0ef8f.py +++ b/tests/test_upstream_git_import_b9a0ef8f.py @@ -1,8 +1,12 @@ -"""Git-mode import record for upstream b9a0ef8f. Does not replace the 5db72537 archive pin test.""" +"""Git-mode import record for upstream b9a0ef8f, kept as history. + +原值: 本文件要求 source-manifest.json 与 SKILL.md 快照仍停在 b9a0ef8f +新值: 只核对这份历史记录自身完整,且已不是当前镜像 +原因: 2026-11-04 第十轮同步把镜像移到 5fb8379c(tests/test_upstream_git_import_5fb8379c.py) +""" from __future__ import annotations -import hashlib import json import re from pathlib import Path @@ -12,12 +16,12 @@ from jsonschema import Draft202012Validator, FormatChecker ROOT = Path(__file__).resolve().parents[1] RECORD = ROOT / "references/cross_project_contract/imports/commit-b9a0ef8f0f0de8714982a5aa7100f5aea16c9d09.json" SOURCE_MANIFEST = ROOT / "references/upstream/yinduzhanxing/source-manifest.json" -SKILL_SNAPSHOT = ROOT / "references/upstream/yinduzhanxing/SKILL.md" SCHEMA = ROOT / "references/cross_project_contract/import_manifest.schema.json" COMMIT = "b9a0ef8f0f0de8714982a5aa7100f5aea16c9d09" +HISTORICAL_SKILL_SHA256 = "6bed459b8b971b189895e62aabfb90e41dbdf8f5b9e80a4b5330d40922fe21eb" -def test_git_import_record_matches_mirrored_skill_and_manifest() -> None: +def test_historical_git_import_record_stays_valid_and_is_not_the_current_mirror() -> None: record = json.loads(RECORD.read_text(encoding="utf-8")) manifest = json.loads(SOURCE_MANIFEST.read_text(encoding="utf-8")) schema = json.loads(SCHEMA.read_text(encoding="utf-8")) @@ -25,13 +29,9 @@ def test_git_import_record_matches_mirrored_skill_and_manifest() -> None: assert record["source_mode"] == "git" assert record["source_commit"] == COMMIT - assert re.fullmatch(r"[0-9a-f]{40}", record["source_commit"]) assert re.fullmatch(r"[0-9a-f]{64}", record["source_tree_hash"]) - assert record["source_tree_hash"] == manifest["source_tree_sha256"] - assert record["source_skill_sha256"] == manifest["source_skill_sha256"] - snapshot_hash = hashlib.sha256(SKILL_SNAPSHOT.read_bytes()).hexdigest() - assert snapshot_hash == record["source_skill_sha256"] == record["mirror_files"][0]["target_sha256_after"] + assert record["source_skill_sha256"] == HISTORICAL_SKILL_SHA256 + assert record["mirror_files"][0]["target_sha256_after"] == HISTORICAL_SKILL_SHA256 assert record["mirror_files"][0]["status"] == "applied" - assert manifest["source_mode"] == "git" - assert manifest["source_commit"] == COMMIT assert all(row["status"] == "review_required" for row in record["semantic_merge_files"]) + assert manifest["source_commit"] != COMMIT diff --git a/tests/test_upstream_import_plan.py b/tests/test_upstream_import_plan.py index 8ebf3872..9539703d 100644 --- a/tests/test_upstream_import_plan.py +++ b/tests/test_upstream_import_plan.py @@ -41,16 +41,20 @@ EXPECTED_REJECTIONS = { "weaken_stability_gate", "remove_commercial_routes", } +# 原值: b9a0ef8f(树 9ae737b0…、git 树 864cab47…、SKILL 6bed459b…、提交时间 2026-09-08T17:36:09Z) +# 新值: 5fb8379c(上游 main 2026-11-04 的 HEAD) +# 原因: 第十轮同步用导入器重新镜像 SKILL.md;旧记录留作历史,见 test_upstream_git_import_b9a0ef8f.py EXPECTED_UPSTREAM_IDENTITY = { "source_repository": "732642856/yinduzhanxing", "source_repository_url": "https://github.com/732642856/yinduzhanxing", - "source_commit": "b9a0ef8f0f0de8714982a5aa7100f5aea16c9d09", - "source_tree_sha256": "9ae737b02a48b38dca7d78094413b961a75e5abad17f2fe206f7dc3b1401d51a", + "source_commit": "5fb8379c4f3e649cb4804065c9a3059e4bf633fe", + "source_tree_sha256": "ee8bd9865cf11a6eb7f90705a262db8b2d145ae0cee6ee6eda32b311548f5aa2", } -EXPECTED_SOURCE_GIT_TREE = "864cab471cadb31b71a7256c995c12b7552d7cb5" -EXPECTED_SOURCE_SKILL_SHA256 = "6bed459b8b971b189895e62aabfb90e41dbdf8f5b9e80a4b5330d40922fe21eb" +EXPECTED_SOURCE_GIT_TREE = "6cd6160804ea7419d609ccd639cc537a8a81c3c9" +EXPECTED_SOURCE_SKILL_SHA256 = "5c1c0738fbe6f586fad36cfa13f602934dcea8f8d54c536655f6cb2091773d8d" +EXPECTED_SOURCE_COMMITTED_AT = "2026-10-21T15:49:21Z" LEGACY_ARCHIVE_COMMIT = "5db72537741fcedaa7b5498502d4a31b0f9fc147" -CURRENT_IMPORT_RECORD = ROOT / "references/cross_project_contract/imports/commit-b9a0ef8f0f0de8714982a5aa7100f5aea16c9d09.json" +CURRENT_IMPORT_RECORD = ROOT / "references/cross_project_contract/imports/commit-5fb8379c4f3e649cb4804065c9a3059e4bf633fe.json" def _load(path: Path) -> dict: @@ -128,7 +132,7 @@ def test_source_manifest_pins_valid_git_commit_tree_and_root_skill_hash() -> Non assert manifest["source_git_tree"] == EXPECTED_SOURCE_GIT_TREE assert "archive_sha256" not in manifest assert "archive_file_count" not in manifest - assert manifest["source_committed_at"] == "2026-09-08T17:36:09Z" + assert manifest["source_committed_at"] == EXPECTED_SOURCE_COMMITTED_AT assert re.fullmatch(r"[0-9a-f]{40}", manifest["source_commit"]) assert re.fullmatch(r"[0-9a-f]{64}", manifest["source_tree_sha256"]) source_skill_sha256 = manifest.get("source_skill_sha256", manifest["skill_sha256"]) diff --git a/tests/test_upstream_sync10_d12_linkage.py b/tests/test_upstream_sync10_d12_linkage.py new file mode 100644 index 00000000..01f9e1d0 --- /dev/null +++ b/tests/test_upstream_sync10_d12_linkage.py @@ -0,0 +1,47 @@ +"""D12 houses in the inter-chart linkage count from the D12 ascendant. + +Ported from upstream yinduzhanxing cf6c316d and f60568a0 (sync 10). Before +the fix the D12 row always had house None, and an unknown sign became Aries. +""" +import ast +import sys +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / 'scripts')) +from jyotish_engine import calc_all_inter_chart_linkages # noqa: E402 + + +@pytest.mark.parametrize('ascendant', range(12)) +def test_d12_every_house_from_its_own_ascendant(ascendant): + frame = {'Sun': {'sign': 'Taurus'}} + actual = calc_all_inter_chart_linkages(frame, frame, frame, frame, 0, 4, 8, ascendant)['Sun'] + assert actual['D12']['house'] == (1 - ascendant) % 12 + 1 + assert actual['D1']['house'] == 2 + assert actual['D9']['house'] == 10 + assert actual['D10']['house'] == 6 + + +def test_missing_d12_ascendant_is_not_aries(): + assert calc_all_inter_chart_linkages({}, {}, {}, {'Sun': {'sign': 'Taurus'}})['Sun']['D12']['house'] is None + + +def test_bad_sign_does_not_become_aries(): + with pytest.raises(ValueError): + calc_all_inter_chart_linkages({'Sun': {'sign': 'unknown'}}, {}, {}, d1_asc_idx=0) + + +@pytest.mark.parametrize('bad', [True, -1, 12, 1.0]) +def test_bad_ascendant_index_is_rejected(bad): + with pytest.raises(ValueError): + calc_all_inter_chart_linkages({'Sun': {'sign': 'Taurus'}}, {}, {}, d1_asc_idx=bad) + + +def test_full_reading_passes_the_precomputed_d12_index(): + tree = ast.parse((ROOT / 'scripts/jyotish_engine.py').read_text(encoding='utf-8')) + calls = [n for n in ast.walk(tree) if isinstance(n, ast.Call) and isinstance(n.func, ast.Name) + and n.func.id == 'calc_all_inter_chart_linkages'] + assert calls + assert all(isinstance(c.args[-1], ast.Name) and c.args[-1].id == 'd12_asc_idx' for c in calls) diff --git a/tests/test_upstream_sync10_kp_unrounded_inputs.py b/tests/test_upstream_sync10_kp_unrounded_inputs.py new file mode 100644 index 00000000..9db80a38 --- /dev/null +++ b/tests/test_upstream_sync10_kp_unrounded_inputs.py @@ -0,0 +1,48 @@ +"""KP must not reuse display-rounded time, cusps or planet degrees (BUG-1543). + +Ported from upstream yinduzhanxing 26d5a52d tests/test_kp_unrounded_inputs.py +(sync 10). Synthetic chart only. +""" +import sys +from pathlib import Path +from types import SimpleNamespace + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / 'scripts')) +import jyotish_engine as engine # noqa: E402 +from bhava_chalit import BhavaChalitCalculator # noqa: E402 + + +def test_kp_passes_raw_time_cusps_and_longitudes(monkeypatch): + args = SimpleNamespace(year=2000, month=1, day=2, hour=3, minute=4, second=5, + lat=20., lon=75., tz=5.5, ayanamsa='raman') + chart = {'ascendant': {'sign': 'Aries', 'degree_raw': 12.123456789}, + 'planets': {'Sun': {'sign': 'Aries', 'degree_raw': 1.234567891, 'degree_in_sign': 1.2346}, + 'Moon': {'sign': 'Taurus', 'degree_raw': 42.987654321, 'degree_in_sign': 12.9877}}} + raw_cusps = [12.123456789 + 30 * i for i in range(12)] + seen = {} + monkeypatch.setattr(engine, '_compute_chart_from_args', lambda a: (chart, 0, 2451545., 23.)) + + def cusps(self, asc, mc, hs, jd, lat, lon): + seen['jd'] = jd + return raw_cusps + + monkeypatch.setattr(BhavaChalitCalculator, 'calculate_cusps', cusps) + monkeypatch.setattr(BhavaChalitCalculator, 'get_bhava_chalit_chart', + lambda *a, **k: {'planets': {'Sun': {'bhava_house': 12}, 'Moon': {'bhava_house': 2}}}) + + def analyze(planets, asc, **kwargs): + seen['planets'] = planets + seen.update(kwargs) + return {} + + monkeypatch.setattr(engine, 'calc_kp_analysis', analyze) + monkeypatch.setattr(engine, 'attach_calculation_profile', lambda result, args: result) + monkeypatch.setattr(engine, '_build_response_envelope', lambda command, result, **k: result) + engine.cmd_kp(args) + expected = engine.swe.julday(2000, 1, 2, 3 + 4 / 60 + 5 / 3600 - 5.5) + assert seen['jd'] == expected and seen['jd'] != round(expected, 6) + assert seen['house_cusps'] == raw_cusps + assert seen['planets']['Sun']['degree'] == 1.234567891 + assert seen['planets']['Moon']['degree'] == 42.987654321 % 30 + assert seen['house_occupancy_basis'] == 'cusp_to_cusp' diff --git a/tests/test_upstream_sync10_observation_date.py b/tests/test_upstream_sync10_observation_date.py new file mode 100644 index 00000000..dc9be62b --- /dev/null +++ b/tests/test_upstream_sync10_observation_date.py @@ -0,0 +1,109 @@ +"""A dated full reading uses one observation date everywhere (BUG-1542). + +Ported from upstream yinduzhanxing 933f254e tests/test_full_reading_observation_date.py +and tests/test_tajika_report_age.py (sync 10). The expressions are taken from +scripts/jyotish_engine.py by name, so the real code is exercised without +generating a report. +""" +import ast +from datetime import datetime +from functools import lru_cache +from pathlib import Path +from types import SimpleNamespace + +import pytest + +ENGINE = Path(__file__).resolve().parents[1] / 'scripts/jyotish_engine.py' + + +@lru_cache(maxsize=1) +def _tree(): + return ast.parse(ENGINE.read_text(encoding='utf-8')) + + +@lru_cache(maxsize=1) +def clock_expressions(): + found = {} + for node in ast.walk(_tree()): + if not isinstance(node, ast.Assign): + continue + names = [t.id for t in node.targets if isinstance(t, ast.Name)] + for name in ('today_str', 'transit_reference_date', 'today_dt', 'ref_today'): + if name in names: + assert name not in found, f'{name} assigned twice; the test would read only one' + found[name] = node.value + return {k: compile(ast.Expression(v), '', 'eval') for k, v in found.items()} + + +class _Clock(datetime): + @classmethod + def now(cls, tz=None): + return cls(2030, 6, 7) + + +@pytest.mark.parametrize('arguments,expected', [ + ({'transit_date': '2001-02-03', 'today': '2020-04-05'}, '2001-02-03'), + ({'transit_date': '2001-02-03'}, '2001-02-03'), + ({'today': '2020-04-05'}, '2020-04-05'), + ({}, '2030-06-07'), +]) +def test_all_report_periods_observe_the_same_requested_date(arguments, expected): + values = {k: eval(code, {'args': SimpleNamespace(**arguments), 'datetime': _Clock}) + for k, code in clock_expressions().items()} + values = {k: v.strftime('%Y-%m-%d') if isinstance(v, datetime) else v for k, v in values.items()} + assert values == dict.fromkeys(('today_str', 'transit_reference_date', 'today_dt', 'ref_today'), expected) + + +def _block_after(predicate, length): + for function in _tree().body: + if not isinstance(function, ast.FunctionDef) or function.name not in ('_cmd_full_reading', 'cmd_full_reading'): + continue + for node in ast.walk(function): + body = getattr(node, 'body', None) + if not isinstance(body, list): + continue + for index, child in enumerate(body): + if predicate(child): + return compile(ast.Module(body=body[index:index + length], type_ignores=[]), '', 'exec') + raise AssertionError('block not found in cmd_full_reading') + + +def _assigns_args_age(node, target): + return (isinstance(node, ast.Assign) and [getattr(t, 'id', None) for t in node.targets] == [target] + and isinstance(node.value, ast.Attribute) and node.value.attr == 'age' + and isinstance(node.value.value, ast.Name) and node.value.value.id == 'args') + + +@pytest.mark.parametrize('extra,expected', [ + ({'today': '2020-12-30'}, 20), + ({'today': '2021-01-01'}, 21), + ({'today': '2021-01-01', 'transit_date': '2020-12-30'}, 20), + ({'today': '2020-12-30', 'age': 18}, 18), +]) +def test_fallback_annual_age_changes_at_the_calendar_birthday(extra, expected): + code = _block_after(lambda n: _assigns_args_age(n, 'age'), 2) + args = SimpleNamespace(**{'year': 2000, 'month': 1, 'day': 1, 'age': None, **extra}) + state = {'args': args, 'datetime': datetime, 'report': {'warnings': []}} + exec(code, state) + assert state['age'] == expected + + +def test_fallback_annual_age_is_not_days_over_365(): + # 2000-01-01 to 2039-12-25 is 14603 days; 14603 // 365 = 40, but the person is 39. + code = _block_after(lambda n: _assigns_args_age(n, 'age'), 2) + args = SimpleNamespace(year=2000, month=1, day=1, age=None, today='2039-12-25') + state = {'args': args, 'datetime': datetime, 'report': {'warnings': []}} + exec(code, state) + assert state['age'] == 39 + + +@pytest.mark.parametrize('extra,expected', [ + ({'transit_date': '2010-01-01', 'today': '2020-01-01'}, 3653 / 365.25), + ({'today': '2020-01-01'}, 7305 / 365.25), +]) +def test_nakshatra_dasha_age_uses_the_observation_date(extra, expected): + code = _block_after(lambda n: _assigns_args_age(n, 'nak_age'), 2) + args = SimpleNamespace(**{'year': 2000, 'month': 1, 'day': 1, 'age': None, **extra}) + state = {'args': args, 'datetime': _Clock} + exec(code, state) + assert state['nak_age'] == pytest.approx(expected)