From 3bae79fbcae80a8db644f7f0252fadb1c9a5c4bc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 11:51:04 +0800 Subject: [PATCH 1/7] docs(upstream): mirror upstream SKILL.md at main 5fb8379c (sync 10) Run scripts/import_yinduzhanxing.py --apply from a clean clone of upstream main 5fb8379c, the upstream HEAD on 2026-11-04. Only the root SKILL.md moves into references/upstream/yinduzhanxing; the six semantic-merge paths stay review_required and untouched. The new mirror drops the private "yn-1993" sample wording the b9a0ef8f mirror still carried. source-manifest.json and import-plan.json now pin 5fb8379c. The b9a0ef8f import record stays as history and its test now checks only the record. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- ...8379c4f3e649cb4804065c9a3059e4bf633fe.json | 79 +++++++++++++++++++ references/upstream/yinduzhanxing/SKILL.md | 32 ++++---- .../upstream/yinduzhanxing/import-plan.json | 6 +- .../yinduzhanxing/source-manifest.json | 16 ++-- scripts/run_quality_gate.py | 1 + tests/test_upstream_git_import_5fb8379c.py | 37 +++++++++ tests/test_upstream_git_import_b9a0ef8f.py | 22 +++--- tests/test_upstream_import_plan.py | 16 ++-- 8 files changed, 167 insertions(+), 42 deletions(-) create mode 100644 references/cross_project_contract/imports/commit-5fb8379c4f3e649cb4804065c9a3059e4bf633fe.json create mode 100644 tests/test_upstream_git_import_5fb8379c.py 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/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/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"]) From 297e27e0bda9232d7de1c5747e1a14be3845db62 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 11:54:12 +0800 Subject: [PATCH 2/7] docs(privacy): swap the leftover private sample for upstream's public example Upstream replaced the redacted-year sample (April 17, 14:45, about 36.5N 114.2E) with a public birth (1935-06-21 11:00, Cajarc, France) in the reading workflow and in the narayana-dasha / solar-return usage text. Our copies still carried the private date, time and place. The PDF guide's example coordinates now name the San Francisco values it already labels. Doc and docstring text only; no calculation changes. The same sample is still a quality-gate fixture in scripts/run_quality_gate.py and appears in older docs/research notes; neither is changed here. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- references/ai-reading-workflow-prompt.md | 2 +- references/pdf-chart-reading-guide.md | 12 ++++++------ scripts/cmd_narayana_dasha.py | 4 ++-- scripts/cmd_solar_return.py | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/references/ai-reading-workflow-prompt.md b/references/ai-reading-workflow-prompt.md index df8457e3..b6866c0a 100644 --- a/references/ai-reading-workflow-prompt.md +++ b/references/ai-reading-workflow-prompt.md @@ -307,7 +307,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/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/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 From 2539d57fee9da72f97045577279aaa3bdb3796fc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 11:55:13 +0800 Subject: [PATCH 3/7] docs(skill): take upstream's source corrections to five reference guides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream corrected guides whose calculations we already ported. Our guides still described the old rules: - argala: secondary Argala is house 5, blocked by house 9 (PVR Integrated Approach 10.5-10.7). The 8th-house and planet-gender rules are dropped. scripts/argala.py already does this (BUG-1386). - sahams: the calculator carries all 36 PVR Table 74 formulas with day and night exceptions (BUG-1474). references/saham_rules.json is byte-equal to upstream. - shodasavarga: Shodasa-varga weights are D27 0.5 and D30 1. The old 1 and 1.5 summed to 21. They now match vimsopaka_calculator.py. - yogi-avayogi: the mixed historical notes are marked as not a formula. The live profile is Sun + Moon + 93 deg 20 min in scripts/annual_native/yogi_sphuta.py; the upstream path is adapted. - ai-reading workflow: adds the 2026-10-02 full-text evidence boundary (conditional openings, untested is not "absent", no celebrity fill-in). event_judgment_wealth and SKILL.md now name finance-timing-strict with wealth-timing-strict as the legacy alias, as the router already does. Chat binds only the 商业运行时路由 section of SKILL.md and router sections, so no chat prompt text changes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- SKILL.md | 2 +- references/ai-reading-workflow-prompt.md | 10 ++++++++++ references/argala-complete-guide.md | 11 ++++++----- references/event_judgment_wealth.md | 2 +- references/sahams-execution-guide.md | 5 ++++- references/shodasavarga-complete-guide.md | 4 ++-- references/yogi-avayogi-system.md | 7 +++++++ 7 files changed, 31 insertions(+), 10 deletions(-) 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/references/ai-reading-workflow-prompt.md b/references/ai-reading-workflow-prompt.md index b6866c0a..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. **不锚定**:先输出星盘推导,再等用户反馈,不先知道用户生活再"找依据" 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/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/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/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 From 4b6aa08726cb5ec4c179c7ad89ec3e25ecfbe7d6 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 11:59:20 +0800 Subject: [PATCH 4/7] fix(varga): count D12 linkage houses from the D12 ascendant (BUG-1541, upstream cf6c316d) The full reading's inter-chart linkage appended D12 with a hard-coded None ascendant, so every D12 row had house None even though d12_asc_idx was already computed a few lines above. An unknown sign name also fell back to Aries. Port upstream cf6c316d and f60568a0: pass d12_asc_idx through, and raise on an unknown sign or a non 0-11 ascendant index. The full reading records the error and the consult native layer marks it blocked. Test ported from upstream tests/test_inter_chart_d12_house.py. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- docs/BUG_HISTORY.md | 16 ++++++++ scripts/jyotish_engine.py | 18 ++++++--- tests/test_upstream_sync10_d12_linkage.py | 47 +++++++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 tests/test_upstream_sync10_d12_linkage.py diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 4bb099da..a07ac714 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -21208,3 +21208,19 @@ - 关联记录:BUG-1132、BUG-1164、BUG-1165、BUG-1182、BUG-1244、BUG-1253、BUG-1255、BUG-1256、issue #113 C1。 - 复发自: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`,待合并。 diff --git a/scripts/jyotish_engine.py b/scripts/jyotish_engine.py index 4b5ec89c..9dba50e1 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 @@ -15834,7 +15840,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 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) From 8137c692b1bbe862bef83b13910437cb2549eb05 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 12:00:44 +0800 Subject: [PATCH 5/7] fix(full-reading): one observation date for Narayana, nakshatra and annual age (BUG-1542, upstream 933f254e) today_str and transit_reference_date already followed transit_date, then today, then now. Narayana's current period read only args.today, and the nakshatra-dasha age and the annual fallback age used datetime.now(). A dated report therefore mixed two dates. The annual fallback age also used days // 365, which moves to the next year of age early. All three now take the same observation date. The fallback age counts calendar birthdays. Tests are ported from upstream test_full_reading_observation_date.py and test_tajika_report_age.py and adapted to cmd_full_reading. Same-day web consultations are unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- docs/BUG_HISTORY.md | 16 +++ scripts/jyotish_engine.py | 18 ++- .../test_upstream_sync10_observation_date.py | 109 ++++++++++++++++++ 3 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 tests/test_upstream_sync10_observation_date.py diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index a07ac714..5c16165a 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -21224,3 +21224,19 @@ - 关联记录: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`,待合并。 diff --git a/scripts/jyotish_engine.py b/scripts/jyotish_engine.py index 9dba50e1..ecd9b65b 100644 --- a/scripts/jyotish_engine.py +++ b/scripts/jyotish_engine.py @@ -16108,7 +16108,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, @@ -16435,7 +16438,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 @@ -16474,8 +16479,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/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) From 9d69cd5625456c452949da2e47c7ad68332b2b46 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 12:01:47 +0800 Subject: [PATCH 6/7] fix(kp): feed sub-lord analysis unrounded cusps, degrees and time (BUG-1543, upstream 26d5a52d) cmd_kp handed calc_kp_analysis the cusps from calculate_bhava_boundaries (rounded to 4 decimals), degree_in_sign (4 decimals) and the chart's display julian_day (6 decimals). KP star, sub and sub-sub boundaries are cut on raw longitude, so a body or cusp sitting on a boundary could land one cell off. The fix recomputes the JD from the civil instant with the compute_chart_data formula, uses calculate_cusps directly, and uses degree_raw % 30. Only the KP part of 26d5a52d is ported. Its Vivaha Saham day/night change and Yoga status pass-through are left for a separate decision. Test ported from upstream tests/test_kp_unrounded_inputs.py. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- docs/BUG_HISTORY.md | 17 +++++++ scripts/jyotish_engine.py | 16 +++++-- ...est_upstream_sync10_kp_unrounded_inputs.py | 48 +++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 tests/test_upstream_sync10_kp_unrounded_inputs.py diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 5c16165a..d35bd2b4 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -21240,3 +21240,20 @@ - 关联记录: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`,待合并。 diff --git a/scripts/jyotish_engine.py b/scripts/jyotish_engine.py index ecd9b65b..bfbe45a5 100644 --- a/scripts/jyotish_engine.py +++ b/scripts/jyotish_engine.py @@ -13749,6 +13749,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 = { @@ -13762,7 +13768,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( @@ -13776,16 +13783,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 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' From a6f5ffbb4fefc862d1121f0ab00daae63680b6cb Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 12:08:51 +0800 Subject: [PATCH 7/7] docs(sync10): record sync 10 in the ledger, changelog and progress note Ledger entry for upstream 5fb8379c with target 9d69cd56 and rollback list. The progress note has the inventory, what was not ported and why, test results, and the status of the two 2026-10-03 upstream issues. Both are fixed upstream: KARAKA_8 now follows BPHS order, and Narayana profiles now reach calc_narayana_mahadasha. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- CHANGELOG.md | 10 ++++ docs/research/upstream_notice_2026_10_03.md | 5 ++ .../PROGRESS-upstream-sync10-20261104.md | 55 +++++++++++++++++++ docs/tasks/README.md | 1 + .../cross_project_contract/sync_ledger.json | 30 ++++++++++ 5 files changed, 101 insertions(+) create mode 100644 docs/tasks/PROGRESS-upstream-sync10-20261104.md 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/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/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" } ] }