From d32ad7b7b19a9804c1cc75d689f124f4f4e3256b Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Wed, 14 Oct 2026 00:48:25 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=A9=9A=E6=81=8B=E8=AF=84=E7=BA=A7?= =?UTF-8?q?=E6=94=B9=E6=88=90=E8=AF=81=E6=8D=AE=E6=A1=A3=E4=BD=8D=EF=BC=8C?= =?UTF-8?q?=E9=85=8D=E5=81=B6=E6=98=9F=E5=88=86=E6=95=B0=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=86=99=E6=88=90=E7=99=BE=E5=88=86=E5=88=B6=EF=BC=88BUG-1318?= =?UTF-8?q?=EF=BC=8C=E4=B8=8A=E6=B8=B8=20f891a2c1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 五档不再写稳定、离婚、再婚或婚前咨询,并写明这是结构指数。公开盘中英文各锁一条。配偶星证据行改成 0 到 1,不乘 100。pytest tests/test_d9_evidence_bands.py 3 通过。 Co-Authored-By: Grok (agent loop) --- docs/BUG_HISTORY.md | 15 ++++ scripts/jyotish_api_server.py | 2 +- scripts/marriage_counting.py | 48 ++++++++++-- tests/test_d9_evidence_bands.py | 134 ++++++++++++++++++++++++++++++++ 4 files changed, 191 insertions(+), 8 deletions(-) create mode 100644 tests/test_d9_evidence_bands.py diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 5a6348ff..e2e95680 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -17720,3 +17720,18 @@ - 复发自:BUG-1306。 - 修复版本:分支 `codex/rectification-step0-entry-20261013`,尚未合入 staging。 +## BUG-1318 | 婚恋评级仍写成稳定或婚前咨询,配偶星分数被写成百分制 + +- 状态:fixed-pending-verify +- 首次发现 / 最近更新:2026-10-14 / 2026-10-14 +- 来源:issue #56。上游 `f891a2c1`。任务书 `docs/tasks/TASK-upstream-sync7-20261014.md` T1。 +- 现象:D9 综合评级仍写「婚姻稳定」「强烈建议婚前咨询」。这份文字进全读导出包,模型能读到。配偶星证据行把 0 到 1 的指数写成「婚姻质量 x/100」。 +- 触发条件:算出婚恋几何指数,或关系时机证据带出配偶星。 +- 根因:同步第六轮按标题筛提交时,漏了 10-02 到 10-03 的这条措辞修正。分数行把 0 到 1 当成了百分制。 +- 修复:五档改成证据档位,并写明这是结构指数,不预测婚姻次数、稳定、离婚、伴侣好坏,也不提示婚前咨询。中文报告用中文档位,英文报告用英文档位。证据行改成「婚姻质量指数 x(0到1)」,不乘 100。 +- 验证:`tests/test_d9_evidence_bands.py` 3 通过。公开盘中英文各一条快照。配偶星行不再出现 `/100`。 +- 防复发:五档文字不得含「稳定、离婚、再婚、婚前咨询、几段」;快照锁住公开盘的两句。 +- 相关记录:BUG-1183。 +- 复发自:无。 +- 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。 + diff --git a/scripts/jyotish_api_server.py b/scripts/jyotish_api_server.py index 6fc0e672..c915df6e 100644 --- a/scripts/jyotish_api_server.py +++ b/scripts/jyotish_api_server.py @@ -7551,7 +7551,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryM evidence.append({ 'label': 'Darakaraka (DK)', 'value': f"{dk_planet} · H{darakaraka.get('dk_house', '-')}", - 'note': f"{darakaraka.get('core_profile') or '配偶象征星'};婚姻质量 {darakaraka.get('marriage_quality_score', '-')}/100", + 'note': f"{darakaraka.get('core_profile') or '配偶象征星'};婚姻质量指数 {darakaraka.get('marriage_quality_score', '-')}(0到1)", }) except Exception as exc: darakaraka = {'error': str(exc), 'dk_planet': dk_planet} diff --git a/scripts/marriage_counting.py b/scripts/marriage_counting.py index 8cad913c..fca23e04 100644 --- a/scripts/marriage_counting.py +++ b/scripts/marriage_counting.py @@ -21,6 +21,23 @@ from typing import Dict, Optional, List SIGN_CN = ['白羊座','金牛座','双子座','巨蟹座','狮子座','处女座', '天秤座','天蝎座','射手座','摩羯座','水瓶座','双鱼座'] +# D9 结构证据档位(BUG-1318)。中文给报告正文,英文键与英文短句给英文版。 +# 不写婚姻稳定、离婚、再婚、段数或婚前咨询。 +D9_CLAIM_BOUNDARY = ( + 'D9 结构证据指数,不预测婚姻次数、稳定、离婚、伴侣好坏,也不提示需要婚前咨询' +) +D9_CLAIM_BOUNDARY_EN = ( + 'D9 structural evidence index. It does not predict marriage count, stability, ' + 'divorce, or partner quality, and it does not suggest premarital counseling.' +) +D9_EVIDENCE_BANDS = ( + ('supportive_evidence_stronger', '支持证据较强', 'Stronger supportive evidence'), + ('supportive_evidence_mixed_positive', '支持证据偏正', 'Mixed supportive evidence'), + ('mixed_evidence', '证据混杂', 'Mixed evidence'), + ('pressure_evidence_mixed', '压力证据偏杂', 'Mixed pressure evidence'), + ('pressure_evidence_stronger', '压力证据较强', 'Stronger pressure evidence'), +) + def sign_of(lon: float) -> int: """从黄道经度计算星座序号 (0-11)""" @@ -348,25 +365,42 @@ def _assess_d9_marriage_quality( quality_points -= 1 factors.append("D9 Ketu在7宫 → 关系中的分离倾向") - # 综合评级 + # 证据档位,不是婚姻结局(BUG-1318,上游 f891a2c1)。 if quality_points >= 4: - rating = "高(关系质量好,伴侣支持强,婚姻稳定)" + band = D9_EVIDENCE_BANDS[0] elif quality_points >= 1: - rating = "中上(关系质量尚可,需经营但基础好)" + band = D9_EVIDENCE_BANDS[1] elif quality_points >= 0: - rating = "中(关系质量一般,需用心经营)" + band = D9_EVIDENCE_BANDS[2] elif quality_points >= -2: - rating = "中下(关系有挑战,需提前沟通和调整期望)" + band = D9_EVIDENCE_BANDS[3] else: - rating = "低(关系质量有重大挑战,强烈建议婚前咨询)" + band = D9_EVIDENCE_BANDS[4] return { - 'quality_rating': rating, + 'quality_band': band[0], + 'quality_rating': band[1], + 'quality_rating_en': band[2], 'quality_points': quality_points, 'factors': factors, + 'claim_boundary': D9_CLAIM_BOUNDARY, + 'claim_boundary_en': D9_CLAIM_BOUNDARY_EN, } +def render_d9_evidence_report(quality: Dict, language: str = 'zh') -> str: + """中文报告用证据档位短句;英文版用英文短句。两边都带同一条边界。""" + if not isinstance(quality, dict): + quality = {} + if language == 'en': + band = quality.get('quality_rating_en') or quality.get('quality_band') or '' + boundary = quality.get('claim_boundary_en') or D9_CLAIM_BOUNDARY_EN + return f"D9 evidence band: {band}\n{boundary}" + band = quality.get('quality_rating') or '' + boundary = quality.get('claim_boundary') or D9_CLAIM_BOUNDARY + return f"D9 证据档位:{band}\n{boundary}" + + def _marriage_recommendations(base: Dict, d9_quality: Dict) -> List[str]: """几何指数与伴侣数量、事件断语分开(BUG-1183);不随指数大小给不同结论。""" return [ diff --git a/tests/test_d9_evidence_bands.py b/tests/test_d9_evidence_bands.py new file mode 100644 index 00000000..b6384f9b --- /dev/null +++ b/tests/test_d9_evidence_bands.py @@ -0,0 +1,134 @@ +"""BUG-1318: D9 marriage rating is an evidence band, not an outcome claim. + +Upstream f891a2c1. The five bands must not say the relationship is stable, +ending, repeated, or in need of premarital counseling. The DK evidence line +keeps the 0-1 index and does not print a fake /100 score. +""" + +from __future__ import annotations + +import importlib.util +import json +import os +import re +import sys +from pathlib import Path + +ROOT = os.path.join(os.path.dirname(__file__), "..") +SCRIPTS = os.path.join(ROOT, "scripts") +if SCRIPTS not in sys.path: + sys.path.insert(0, SCRIPTS) + +from jyotish_engine import compute_chart_data # noqa: E402 + + +def _api_handler(): + """Reuse the existing unbound handler helper. Do not add another forgery site.""" + path = os.path.join(os.path.dirname(__file__), "test_api_server_security.py") + spec = importlib.util.spec_from_file_location("_api_security_helpers", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module._handler() +from marriage_counting import ( # noqa: E402 + D9_CLAIM_BOUNDARY, + D9_EVIDENCE_BANDS, + _assess_d9_marriage_quality, + marriage_counting_full_analysis, + render_d9_evidence_report, +) +from varga import calc_varga # noqa: E402 + +BANNED = re.compile(r"稳定|离婚|再婚|婚前咨询|几段") + + +def _quality(points_chart: dict) -> dict: + return _assess_d9_marriage_quality(points_chart, None) + + +def test_five_bands_have_no_outcome_claims_and_keep_the_boundary() -> None: + samples = { + "supportive_evidence_stronger": {"Venus": 331.0, "Mars": 271.0, "Jupiter": 91.0}, + "supportive_evidence_mixed_positive": {"Venus": 31.0}, + "mixed_evidence": {"Venus": 10.0}, + "pressure_evidence_mixed": {"Venus": 151.0}, + "pressure_evidence_stronger": {"Venus": 151.0, "Mars": 91.0, "Jupiter": 271.0}, + } + seen = [] + for key, chart in samples.items(): + quality = _quality(chart) + assert quality["quality_band"] == key + assert quality["claim_boundary"] == D9_CLAIM_BOUNDARY + assert quality["claim_boundary_en"] + assert not BANNED.search(quality["quality_rating"]) + assert not BANNED.search(quality["quality_rating_en"]) + seen.append(quality["quality_band"]) + assert seen == [band[0] for band in D9_EVIDENCE_BANDS] + + +def test_public_chart_report_snapshots_in_both_languages() -> None: + cases = json.loads( + Path(ROOT, "references/real_case_calibration/minute_rectification_development_v1.json").read_text(encoding="utf-8") + )["cases"] + birth = next(case for case in cases if case["case_id"] == "steve_jobs_1955_development")["birth"] + assert birth["source"]["rodden_rating"] == "AA" + year, month, day = (int(part) for part in birth["date"].split("-")) + hour, minute = (int(part) for part in birth["time"].split(":")) + chart, *_ = compute_chart_data( + year, month, day, hour, minute, + birth["latitude"], birth["longitude"], birth["timezone_offset"], + ayanamsa_name="raman", + ) + longitudes = {name: row["degree_raw"] for name, row in chart["planets"].items()} + d9 = {} + for name, lon in longitudes.items(): + divisional = calc_varga(lon, 9) + d9[name] = divisional["sign_idx"] * 30 + divisional["degree_in_sign"] + result = marriage_counting_full_analysis(chart["houses"]["house_7"]["lord"], longitudes, d9) + quality = result["d9_marriage_quality"] + assert quality["quality_band"] == "mixed_evidence" + assert quality["quality_points"] == 0 + chinese = render_d9_evidence_report(quality, "zh") + english = render_d9_evidence_report(quality, "en") + assert chinese == ( + "D9 证据档位:证据混杂\n" + "D9 结构证据指数,不预测婚姻次数、稳定、离婚、伴侣好坏,也不提示需要婚前咨询" + ) + assert english == ( + "D9 evidence band: Mixed evidence\n" + "D9 structural evidence index. It does not predict marriage count, stability, " + "divorce, or partner quality, and it does not suggest premarital counseling." + ) + assert "/100" not in chinese + assert "/100" not in english + + +def test_dk_evidence_line_prints_the_zero_to_one_index() -> None: + cases = json.loads( + Path(ROOT, "references/real_case_calibration/minute_rectification_development_v1.json").read_text(encoding="utf-8") + )["cases"] + birth = next(case for case in cases if case["case_id"] == "steve_jobs_1955_development")["birth"] + year, month, day = (int(part) for part in birth["date"].split("-")) + hour, minute = (int(part) for part in birth["time"].split(":")) + chart, asc_idx, *_ = compute_chart_data( + year, month, day, hour, minute, + birth["latitude"], birth["longitude"], birth["timezone_offset"], + ayanamsa_name="raman", + ) + longitudes = {name: row["degree_raw"] for name, row in chart["planets"].items()} + handler = _api_handler() + evidence = handler._compute_relationship_timing_evidence( + {}, + chart["planets"], + longitudes, + asc_idx, + chart["ascendant"]["sign"], + ) + notes = [row["note"] for row in evidence["evidence"] if row.get("label") == "Darakaraka (DK)"] + assert len(notes) == 1 + assert "/100" not in notes[0] + assert "婚姻质量指数" in notes[0] + assert "(0到1)" in notes[0] + score = evidence["darakaraka"]["marriage_quality_score"] + assert isinstance(score, float) + assert 0.0 <= score <= 1.0 + assert str(score) in notes[0] From 2748e4387118d70d3b92f60750c4b3d8fee7b1b9 Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Wed, 14 Oct 2026 00:48:38 +0800 Subject: [PATCH 2/5] =?UTF-8?q?KP=20=E6=94=B9=E6=88=90=E5=AE=AB=E5=A4=B4?= =?UTF-8?q?=E5=88=B0=E5=AE=AB=E5=A4=B4=E5=88=A4=E5=AE=AB=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E5=86=99=E6=98=8E=E5=8F=A3=E5=BE=84=EF=BC=88BUG-1319=EF=BC=8C?= =?UTF-8?q?=E4=B8=8A=E6=B8=B8=200337b77f=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本命 Bhava Chalit 表仍按中点,默认宫位不变。传了宫头却不声明就拒绝;没传宫头记整宫代理。报告多一行判宫口径。pytest tests/test_kp_cusp_house_assignment.py 3 通过,tests/test_bhava_chalit.py 仍通过。 Co-Authored-By: Grok (agent loop) --- docs/BUG_HISTORY.md | 15 +++++++ scripts/bhava_chalit.py | 25 ++++++++++- scripts/jyotish_engine.py | 8 +++- scripts/kp_system.py | 7 ++++ scripts/pl9_reader_export.py | 1 + tests/test_kp_cusp_house_assignment.py | 58 ++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 tests/test_kp_cusp_house_assignment.py diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index e2e95680..e16ccbe4 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -17735,3 +17735,18 @@ - 复发自:无。 - 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。 +## BUG-1319 | KP 落宫沿用本命中点判宫,传了宫头也不声明口径 + +- 状态:fixed-pending-verify +- 首次发现 / 最近更新:2026-10-14 / 2026-10-14 +- 来源:issue #56。上游 `0337b77f`。任务书 T2。 +- 现象:KP 用的行星落宫和本命 Bhava Chalit 表一样,按相邻宫头的中点判宫。宫头传进 KP 分析时,结果不写明判宫口径。 +- 触发条件:打开 KP。本命 Bhava Chalit 表不走这条。 +- 根因:同步第六轮漏了夹在上游隐私提交里的判宫修正。本命表和 KP 共用了中点函数。 +- 修复:KP 改成宫头到下一宫头。本命表默认仍是中点,宫位数字不变。传了宫头却不声明口径就拒绝;没传宫头记整宫代理。KP 报告写出判宫口径。 +- 验证:`tests/test_kp_cusp_house_assignment.py` 3 通过。同一颗星在两种口径下落入不同宫;默认中点与改前一致。 +- 防复发:未知口径直接报错;报告正文必须出现 House occupancy basis。 +- 相关记录:BUG-1318。 +- 复发自:无。 +- 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。 + diff --git a/scripts/bhava_chalit.py b/scripts/bhava_chalit.py index 17412e0a..54a0bf18 100644 --- a/scripts/bhava_chalit.py +++ b/scripts/bhava_chalit.py @@ -272,6 +272,20 @@ class BhavaChalitCalculator: # fallback: 最近的宫 return 1 + def _planet_bhava_from_cusps(self, planet_lon: float, cusps: List[float]) -> int: + """KP 宫头到宫头:宫 i(0 起)的范围是 cusp[i] → cusp[(i+1)%12]。 + + 落在下一宫头上的点归下一宫。本命 Bhava Chalit 表不用这条。 + """ + for i in range(12): + start = cusps[i] + end = cusps[(i + 1) % 12] + arc = _angular_dist(start, end) + pos = _angular_dist(start, planet_lon) + if arc > 0 and pos < arc: + return i + 1 + return 1 + def _planet_rashi_house(self, planet_lon: float, asc_lon: float) -> int: """整宫制宫位 (Rashi house)。""" p_si = _sign_idx(planet_lon) @@ -330,7 +344,8 @@ class BhavaChalitCalculator: asc_lon: float, mc_lon: float, house_system: str = 'sripati', jd: float = None, lat: float = None, - lon: float = None) -> Dict: + lon: float = None, + house_assignment: str = 'sandhi') -> Dict: """根据 Bhava 边界重新分配行星宫位。 Parameters @@ -346,13 +361,18 @@ class BhavaChalitCalculator: dict with keys: house_system, boundaries, planets """ + if house_assignment not in ('sandhi', 'cusp_to_cusp'): + raise ValueError("house_assignment must be 'sandhi' or 'cusp_to_cusp'") cusps = self.calculate_cusps(asc_lon, mc_lon, house_system, jd, lat, lon) sandhis = self.calculate_sandhis(cusps) planets = {} for pname, plon in planet_lons.items(): - bhava = self._planet_bhava(plon, sandhis) + if house_assignment == 'cusp_to_cusp': + bhava = self._planet_bhava_from_cusps(plon, cusps) + else: + bhava = self._planet_bhava(plon, sandhis) rashi = self._planet_rashi_house(plon, asc_lon) si = _sign_idx(plon) deg_in_sign = plon - si * 30 @@ -380,6 +400,7 @@ class BhavaChalitCalculator: return { 'house_system': house_system, 'house_system_desc': self.HOUSE_SYSTEMS.get(house_system, ''), + 'house_assignment': house_assignment, 'ascendant_lon': round(asc_lon, 4), 'mc_lon': round(mc_lon, 4), 'planets': planets, diff --git a/scripts/jyotish_engine.py b/scripts/jyotish_engine.py index 4d85c653..e2f14143 100644 --- a/scripts/jyotish_engine.py +++ b/scripts/jyotish_engine.py @@ -13963,6 +13963,7 @@ def cmd_kp(args): ) bhava_chart = kp_house_calculator.get_bhava_chalit_chart( planet_lons, float(asc_lon), 0.0, 'placidus', jd, args.lat, args.lon, + house_assignment='cusp_to_cusp', ) bhava_planets = bhava_chart.get('planets') if isinstance(bhava_chart.get('planets'), dict) else {} @@ -13977,7 +13978,12 @@ def cmd_kp(args): 'house': bhava_row.get('bhava_house', pdata.get('house', 1)), } - result = calc_kp_analysis(planet_positions, asc_sign, house_cusps=boundaries.get('cusps')) + result = calc_kp_analysis( + planet_positions, + asc_sign, + house_cusps=boundaries.get('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 try: birth_moment = datetime(int(args.year), int(args.month), int(args.day), int(args.hour), int(getattr(args, 'minute', 0) or 0)) diff --git a/scripts/kp_system.py b/scripts/kp_system.py index fcd1cbfb..09067377 100644 --- a/scripts/kp_system.py +++ b/scripts/kp_system.py @@ -371,6 +371,7 @@ def calc_kp_analysis( planet_positions: Dict, asc_sign: str = 'Aries', house_cusps: Optional[List[float]] = None, + house_occupancy_basis: Optional[str] = None, ) -> Dict: """ 完整KP分析(基于 diliprk/VedicAstro MIT 算法)。 @@ -379,6 +380,7 @@ def calc_kp_analysis( planet_positions: 行星位置 {planet: {'sign': str, 'degree': float, 'house': int}} asc_sign: 上升星座名称(无显式宫头时用于 whole-sign 代理) house_cusps: 可选的 12 个实际宫头黄经。提供时优先用于 KP 宫头与显著星。 + house_occupancy_basis: 传了宫头就必须声明判宫口径;没传宫头时记 whole_sign_proxy。 Returns: 完整KP分析结果 @@ -405,8 +407,11 @@ def calc_kp_analysis( } explicit_cusps = house_cusps is not None + if explicit_cusps and not house_occupancy_basis: + raise ValueError('house_occupancy_basis is required when house_cusps are provided') if explicit_cusps and len(house_cusps) != 12: raise ValueError('house_cusps must contain exactly 12 longitudes') + occupancy_basis = house_occupancy_basis if explicit_cusps else 'whole_sign_proxy' # 2. 构建宫位信息(含 KP lords)。无实际宫头时保留历史 whole-sign 中点代理。 houses = [] @@ -438,6 +443,7 @@ def calc_kp_analysis( 'source': 'diliprk/VedicAstro MIT License', 'maturity_profile': kp_maturity_profile(), 'house_basis': 'explicit_cusps' if explicit_cusps else 'whole_sign_proxy', + 'house_occupancy_basis': occupancy_basis, 'planets': {pname: {'kp_lords': data['kp_lords'], 'significators': planet_sig.get(pname, {})} for pname, data in kp_planets.items()}, 'houses': {h['house']: {'sign': h['sign'], 'cusp_longitude': h['cusp_longitude'], 'kp_lords': h['kp_lords'], 'significators': house_sig.get(h['house'], {})} @@ -494,6 +500,7 @@ def build_kp_report_pack( f"- claim_status: `{maturity.get('claim_status')}`", f"- truth_matrix_allowed: `{maturity.get('truth_matrix_allowed')}`", f"- closed_oracle_parity_assets: `{', '.join(closed_parity_assets.keys())}`", + f"- House occupancy basis: `{kp_analysis.get('house_occupancy_basis') or 'whole_sign_proxy'}`", f"- planet_count: `{len(planets)}`", f"- house_count: `{len(houses)}`", f"- dba_period_count: `{len(periods)}`", diff --git a/scripts/pl9_reader_export.py b/scripts/pl9_reader_export.py index 830ba468..f0ec9f66 100644 --- a/scripts/pl9_reader_export.py +++ b/scripts/pl9_reader_export.py @@ -2140,6 +2140,7 @@ def render_pl9_parity_markdown(packet: dict) -> str: '### p123 KP Chart / Cusps', '', f"- House basis:{_cell(kp.get('house_basis'))}", + f"- House occupancy basis:{_cell(kp.get('house_occupancy_basis'))}", '', '| House | Sign | Cusp longitude | KP lord chain |', '|-------|------|----------------|---------------|', diff --git a/tests/test_kp_cusp_house_assignment.py b/tests/test_kp_cusp_house_assignment.py new file mode 100644 index 00000000..e33fa041 --- /dev/null +++ b/tests/test_kp_cusp_house_assignment.py @@ -0,0 +1,58 @@ +"""BUG-1319: KP house occupancy is cusp-to-cusp and must be declared. + +Upstream 0337b77f. Natal Bhava Chalit stays on the sandhi midpoint rule. +""" + +from __future__ import annotations + +import os +import sys + +import pytest + +SCRIPTS = os.path.join(os.path.dirname(__file__), "..", "scripts") +if SCRIPTS not in sys.path: + sys.path.insert(0, SCRIPTS) + +from bhava_chalit import BhavaChalitCalculator # noqa: E402 +from kp_system import build_kp_report_pack, calc_kp_analysis # noqa: E402 + + +def test_cusp_to_cusp_differs_from_sandhi_and_default_houses_stay() -> None: + calc = BhavaChalitCalculator() + planets = {"Sun": 40.0} + sandhi = calc.get_bhava_chalit_chart(planets, 15.0, 270.0, "equal") + explicit = calc.get_bhava_chalit_chart( + planets, 15.0, 270.0, "equal", house_assignment="sandhi", + ) + cusp = calc.get_bhava_chalit_chart( + planets, 15.0, 270.0, "equal", house_assignment="cusp_to_cusp", + ) + assert sandhi["house_assignment"] == "sandhi" + assert explicit["planets"]["Sun"]["bhava_house"] == sandhi["planets"]["Sun"]["bhava_house"] == 2 + assert cusp["house_assignment"] == "cusp_to_cusp" + assert cusp["planets"]["Sun"]["bhava_house"] == 1 + assert cusp["planets"]["Sun"]["bhava_house"] != sandhi["planets"]["Sun"]["bhava_house"] + + +def test_unknown_house_assignment_is_rejected() -> None: + calc = BhavaChalitCalculator() + with pytest.raises(ValueError, match="house_assignment"): + calc.get_bhava_chalit_chart({"Sun": 10.0}, 15.0, 270.0, "equal", house_assignment="whole") + + +def test_kp_cusps_require_an_occupancy_basis_and_the_report_names_it() -> None: + planets = {"Sun": {"sign": "Taurus", "degree": 10.0, "house": 2}} + cusps = [15.0 + 30.0 * index for index in range(12)] + with pytest.raises(ValueError, match="house_occupancy_basis"): + calc_kp_analysis(planets, "Aries", house_cusps=cusps) + with pytest.raises(ValueError, match="house_occupancy_basis"): + calc_kp_analysis(planets, "Aries", house_cusps=cusps, house_occupancy_basis="") + declared = calc_kp_analysis( + planets, "Aries", house_cusps=cusps, house_occupancy_basis="cusp_to_cusp", + ) + assert declared["house_occupancy_basis"] == "cusp_to_cusp" + proxy = calc_kp_analysis(planets, "Aries") + assert proxy["house_occupancy_basis"] == "whole_sign_proxy" + markdown = build_kp_report_pack(declared)["exports"]["markdown"] + assert "House occupancy basis: `cusp_to_cusp`" in markdown From 4cdb9978003db471dca7cd144da68cb191e46488 Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Wed, 14 Oct 2026 00:48:53 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E7=91=9C=E4=BC=BD=E8=A7=92=E5=AE=AB?= =?UTF-8?q?=E4=B8=BB=E5=92=8C=E4=B8=89=E6=96=B9=E4=B8=BB=E6=8C=89=E5=90=8D?= =?UTF-8?q?=E5=AD=97=E6=8E=92=E5=BA=8F=EF=BC=8C=E4=B8=8D=E5=86=8D=E9=9A=8F?= =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E5=93=88=E5=B8=8C=E5=8F=98=EF=BC=88BUG-1320?= =?UTF-8?q?=EF=BC=8C=E4=B8=8A=E6=B8=B8=200337b77f=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 同一张盘在两个哈希种子下,宫主名单和瑜伽参与者顺序一致。pytest tests/test_yoga_lord_order_stable.py 1 通过。 Co-Authored-By: Grok (agent loop) --- docs/BUG_HISTORY.md | 15 ++++++ scripts/yoga_engine.py | 6 ++- tests/test_yoga_lord_order_stable.py | 72 ++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 tests/test_yoga_lord_order_stable.py diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index e16ccbe4..bd8daf5f 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -17750,3 +17750,18 @@ - 复发自:无。 - 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。 +## BUG-1320 | 瑜伽角宫主和三方主的顺序随进程哈希变化 + +- 状态:fixed-pending-verify +- 首次发现 / 最近更新:2026-10-14 / 2026-10-14 +- 来源:issue #56。上游 `0337b77f`。任务书 T3。 +- 现象:同一张盘的角宫主、三方主列表用了集合去重。集合顺序会跟着进程哈希变,瑜伽参与者顺序不稳定。 +- 触发条件:规则表达式读取这两份宫主列表。 +- 根因:去重用了不保证顺序的集合。同步第六轮漏了上游把它们改成排序的那一处。 +- 修复:两处都改成先去重再按名字排序。 +- 验证:`tests/test_yoga_lord_order_stable.py` 1 通过。哈希种子 0 和 1 两个进程算出的参与者顺序相同。 +- 防复发:两个子进程的名单必须逐项相同,并且等于排好序的名单。 +- 相关记录:BUG-1319。 +- 复发自:无。 +- 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。 + diff --git a/scripts/yoga_engine.py b/scripts/yoga_engine.py index 66cf5740..0f4db644 100644 --- a/scripts/yoga_engine.py +++ b/scripts/yoga_engine.py @@ -1687,11 +1687,13 @@ class YogaEngine: # 辅助:获取角宫主星列表 def _kendra_lords_list(): ai = SIGNS.index(ctx.ascendant) if ctx.ascendant in SIGNS else 0 - return list(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 4, 7, 10]])) + # set 顺序随进程哈希变化;排序后同一张盘的角宫主参与者顺序才稳定。 + return sorted(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 4, 7, 10]])) # 辅助:获取三方宫主星列表 def _trikona_lords_list(): ai = SIGNS.index(ctx.ascendant) if ctx.ascendant in SIGNS else 0 - return list(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 5, 9]])) + # set 顺序随进程哈希变化;排序后同一张盘的三方主参与者顺序才稳定。 + return sorted(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 5, 9]])) safe_globals = { # 安全内置函数:只开放规则表达式需要的纯函数,避免 custom 规则因 all/any/len/set 缺失而静默失效。 diff --git a/tests/test_yoga_lord_order_stable.py b/tests/test_yoga_lord_order_stable.py new file mode 100644 index 00000000..8bf3ad9b --- /dev/null +++ b/tests/test_yoga_lord_order_stable.py @@ -0,0 +1,72 @@ +"""BUG-1320: kendra and trikona lord lists do not follow PYTHONHASHSEED. + +Upstream 0337b77f. Two processes with different hash seeds must list the +same yoga participants in the same order. +""" + +from __future__ import annotations + +import json +import os +import subprocess +import sys + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + +_PROBE = r""" +import json +import sys +sys.path.insert(0, "scripts") +from yoga_engine import YogaContext, YogaEngine, detect_yogas + +planets = { + "Sun": {"sign": "Leo", "house": 5, "degree": 10}, + "Moon": {"sign": "Cancer", "house": 4, "degree": 5}, + "Mars": {"sign": "Aries", "house": 1, "degree": 8}, + "Mercury": {"sign": "Virgo", "house": 6, "degree": 12}, + "Jupiter": {"sign": "Sagittarius", "house": 9, "degree": 15}, + "Venus": {"sign": "Libra", "house": 7, "degree": 3}, + "Saturn": {"sign": "Capricorn", "house": 10, "degree": 20}, + "Rahu": {"sign": "Taurus", "house": 2, "degree": 9}, + "Ketu": {"sign": "Scorpio", "house": 8, "degree": 9}, +} +rules = "references/yoga_rules.json" +engine = YogaEngine(rules) +ctx = YogaContext(planets, "Aries") +namespace = engine._build_custom_exec_globals(ctx, {}, {}) +rows = detect_yogas(planets, "Aries", rules) +print(json.dumps({ + "kendra": namespace["kendra_lords_list"](), + "trikona": namespace["trikona_lords_list"](), + "participants": [ + [row.get("rule_id"), str(row.get("combination"))] + for row in rows + ], +}, ensure_ascii=False)) +""" + + +def _run(seed: str) -> dict: + env = os.environ.copy() + env["PYTHONHASHSEED"] = seed + env["PYTHONIOENCODING"] = "utf-8" + completed = subprocess.run( + [sys.executable, "-c", _PROBE], + cwd=ROOT, + env=env, + check=True, + capture_output=True, + text=True, + encoding="utf-8", + ) + return json.loads(completed.stdout) + + +def test_yoga_participants_keep_the_same_order_across_hash_seeds() -> None: + first = _run("0") + second = _run("1") + assert first == second + assert first["kendra"] == sorted(first["kendra"]) + assert first["trikona"] == sorted(first["trikona"]) + assert first["kendra"] == ["Mars", "Moon", "Saturn", "Venus"] + assert first["trikona"] == ["Jupiter", "Mars", "Sun"] From aae3fea01100e7721091a95667bbd737ab3ba737 Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Wed, 14 Oct 2026 00:49:21 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=A4=A7=E8=BF=90?= =?UTF-8?q?=E5=B9=B4=E9=95=BF=E6=94=B9=E6=88=90=20365.25=20=E5=A4=A9?= =?UTF-8?q?=EF=BC=8C=E5=92=8C=E6=8E=A8=E8=BF=90=E8=84=9A=E6=9C=AC=E5=90=8C?= =?UTF-8?q?=E4=B8=80=E5=A4=A9=EF=BC=88BUG-1321=EF=BC=8C=E4=B8=8A=E6=B8=B8?= =?UTF-8?q?=204d543209=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 只改兜底时间线的结束日和进入本运年数这两处。同一大运起止相差 0 天。pytest tests/test_dasha_year_length_alignment.py 2 通过。增长合同仍过。 Co-Authored-By: Grok (agent loop) --- docs/BUG_HISTORY.md | 15 ++++++ scripts/jyotish_api_server.py | 4 +- tests/test_dasha_year_length_alignment.py | 62 +++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/test_dasha_year_length_alignment.py diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index bd8daf5f..920fabed 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -17765,3 +17765,18 @@ - 复发自:无。 - 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。 +## BUG-1321 | 接口里的大运年长和推运脚本差了一点,起止会对不齐 + +- 状态:fixed-pending-verify +- 首次发现 / 最近更新:2026-10-14 / 2026-10-14 +- 来源:issue #56。上游 `4d543209`。任务书 T4。 +- 现象:接口的兜底大运和「进入本运几年」用 365.25636 天为一年。同一条时间线的推运脚本用 365.25 天。年限一长,日期会差一天。 +- 触发条件:走兜底大运时间线,或计算进入当前大运的年数。 +- 根因:两处常量没有跟着推运脚本改。同步第六轮漏了上游 10-03 的这一处。 +- 修复:这两处改成 365.25,与推运脚本相同。其它大运系统的年长不动。 +- 验证:`tests/test_dasha_year_length_alignment.py` 2 通过。同一大运起止相差 0 天。158 年的旧常量会落到下一天,新常量不会。 +- 防复发:兜底时间线必须和推运脚本的起止是同一天;进入本运的年数按 365.25 除。 +- 相关记录:BUG-1320。 +- 复发自:无。 +- 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。 + diff --git a/scripts/jyotish_api_server.py b/scripts/jyotish_api_server.py index c915df6e..39d3826f 100644 --- a/scripts/jyotish_api_server.py +++ b/scripts/jyotish_api_server.py @@ -6378,7 +6378,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryM periods = [] current = birth_dt for lord in DASHA_ORDER: - end_date = current + timedelta(days=period_years * 365.25636) + end_date = current + timedelta(days=period_years * 365.25) periods.append({ 'lord': lord, 'years': period_years, @@ -7398,7 +7398,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryM current_idx, current_md = analyzer.find_current(timeline, today) antardashas = analyzer.build_antardasha(current_md) current_ad = analyzer.find_current_sub(antardashas, today) - years_into_md = max(0.0, (today - current_md['start']).days / 365.25636) + years_into_md = max(0.0, (today - current_md['start']).days / 365.25) five_levels = enhanced.calculate_five_level_dasha(current_md['lord'], years_into_md) formatted_timeline = [self._format_datetime_period(period) for period in timeline] formatted_antardashas = [self._format_datetime_period(period) for period in antardashas] diff --git a/tests/test_dasha_year_length_alignment.py b/tests/test_dasha_year_length_alignment.py new file mode 100644 index 00000000..525dd043 --- /dev/null +++ b/tests/test_dasha_year_length_alignment.py @@ -0,0 +1,62 @@ +"""BUG-1321: the API fallback dasha year matches dasha_analyzer's 365.25 days. + +Upstream 4d543209. The two 365.25636 constants in the API server made the +fallback timeline and the years-into-mahadasha figure drift from the analyzer. +""" + +from __future__ import annotations + +import importlib.util +import os +import sys +from datetime import datetime, timedelta + +SCRIPTS = os.path.join(os.path.dirname(__file__), "..", "scripts") +if SCRIPTS not in sys.path: + sys.path.insert(0, SCRIPTS) + +from dasha_analyzer import build_dasha_timeline, lon_to_nakshatra # noqa: E402 + + +def _api_handler(): + """Reuse the existing unbound handler helper. Do not add another forgery site.""" + path = os.path.join(os.path.dirname(__file__), "test_api_server_security.py") + spec = importlib.util.spec_from_file_location("_api_security_helpers_dasha", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module._handler() + + +def test_fallback_timeline_matches_analyzer_to_the_day() -> None: + birth = datetime(2000, 1, 1) + nak_info, progress, _pada = lon_to_nakshatra(0.0) + timeline, _elapsed, _remaining, _lord = build_dasha_timeline("2000-01-01", nak_info, progress) + handler = _api_handler() + years = timeline[0]["years"] + periods = handler._fallback_dasha_periods(birth, "generic", {"years": years * 9}) + assert periods[0]["lord"] == timeline[0]["lord"] + assert periods[0]["start"] == timeline[0]["start"].strftime("%Y-%m-%d") + assert periods[0]["end"] == timeline[0]["end"].strftime("%Y-%m-%d") + fallback_days = ( + datetime.strptime(periods[0]["end"], "%Y-%m-%d") + - datetime.strptime(periods[0]["start"], "%Y-%m-%d") + ).days + analyzer_days = (timeline[0]["end"].date() - timeline[0]["start"].date()).days + assert fallback_days - analyzer_days == 0 + + long_periods = handler._fallback_dasha_periods(birth, "generic", {"years": 1422}) + expected = (birth + timedelta(days=158 * 365.25)).strftime("%Y-%m-%d") + stale = (birth + timedelta(days=158 * 365.25636)).strftime("%Y-%m-%d") + assert expected != stale + assert long_periods[0]["end"] == expected + + +def test_years_into_mahadasha_uses_the_analyzer_year() -> None: + birth = datetime(2000, 1, 1) + today = datetime(2003, 1, 1) + handler = _api_handler() + layer = handler._compute_vimshottari_analysis_layer(birth, 0.0, current_date="2003-01-01") + days = (today - birth).days + years_into = layer["five_levels"]["mahadasha"]["years_into_period"] + assert years_into == days / 365.25 + assert years_into != days / 365.25636 From 6dd1ff7d0c3b5e242e693ff6495993f1acb7e177 Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Wed, 14 Oct 2026 00:50:09 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E8=AE=B0=E4=B8=8B=E6=98=9F=E5=8E=86?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=A0=B8=E6=9F=A5=EF=BC=9A=E6=9C=AC=E6=9C=BA?= =?UTF-8?q?=E4=BC=9A=E9=80=80=E5=9B=9E=E4=BD=8E=E7=B2=BE=E5=BA=A6=EF=BC=8C?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E9=95=9C=E5=83=8F=E6=9C=AA=E6=89=93=E5=BC=80?= =?UTF-8?q?=EF=BC=88BUG-1322=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T5 不改代码。进度、更新日志和任务索引一起补上。上游 e1909724 只作同类问题参照。 Co-Authored-By: Grok (agent loop) --- CHANGELOG.md | 7 ++++ docs/BUG_HISTORY.md | 15 +++++++ .../tasks/PROGRESS-upstream-sync7-20261014.md | 41 +++++++++++++++++++ docs/tasks/README.md | 2 +- 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 docs/tasks/PROGRESS-upstream-sync7-20261014.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 2422a574..d2056bfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 印度占星 Skill 更新日志 +## 2026-10-14 — 婚恋评级改成证据档位,KP 判宫和大运年长对齐(待合入 staging) + +- 婚恋结构指数不再写成婚姻稳定、离婚、再婚或建议婚前咨询。中文报告用中文档位,英文报告用英文档位。配偶星分数按 0 到 1 显示,不再写成百分制。 +- KP 按宫头到下一宫头判宫,报告写明口径。本命宫位表的判宫不变。 +- 接口里兜底大运的一年改成和推运脚本一样的 365.25 天。 +- 星历路径只核查,没改。Skill 版本不变(BUG-1318~1321;BUG-1322 仍在核查)。 + ## 2026-10-13 — 明说要做生时校正时,第一步就能打开(待合入 staging) - 新对话第一句就说「帮我做生时校正 / 生时纠正 / 核对出生时间」,会直接打开校正,只留一句过渡,不先写一段解读。 diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 920fabed..add22027 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -17780,3 +17780,18 @@ - 复发自:无。 - 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。 +## BUG-1322 | 星历路径被设成空字符串后,计算退回低精度星历 + +- 状态:investigating +- 首次发现 / 最近更新:2026-10-14 / 2026-10-14 +- 来源:issue #56。上游 `e1909724` 点出的同类问题。任务书 T5,本轮只核查、不改代码。 +- 现象:排盘和过境入口都把星历路径设成空字符串。本机随后计算太阳位置时,返回标记含低精度星历位,不含星历文件位。 +- 触发条件:调用排盘或过境计算。本轮没有改这条路径。 +- 根因:尚未在生产镜像里核实。本机仓库里的星历文件只在参考资料目录,不在计算进程会去找的路径上。空路径是进程全局的,后一次调用会盖掉前面设过的路径。 +- 修复:未改。建议另开一单:镜像里放一份星历文件,路径显式设上,并断言返回标记不再是低精度星历。 +- 验证:本机一次探针。生产镜像未打开,不能当作已确认。 +- 防复发:未做。等修复单再加断言。 +- 相关记录:BUG-1321。 +- 复发自:无。 +- 修复版本:未修复。 + diff --git a/docs/tasks/PROGRESS-upstream-sync7-20261014.md b/docs/tasks/PROGRESS-upstream-sync7-20261014.md new file mode 100644 index 00000000..17682170 --- /dev/null +++ b/docs/tasks/PROGRESS-upstream-sync7-20261014.md @@ -0,0 +1,41 @@ +# PROGRESS · 上游同步第七轮(2026-10-14) + +- 分支:`codex/upstream-sync7-20261014` +- 基线:任务书写的是 `77057a81`。开工时 `origin/staging` 已到 `99ef1bbd`,本分支从这里建。 +- 结论:T1 到 T4 已做完并推这个分支。T5 只核查,代码没改。星历在这台电脑上会退回低精度,生产镜像没打开,记 BUG-1322,状态是核查中。 + +## 做了什么 + +| 项 | 结果 | +| --- | --- | +| T1 婚恋断语 | 五档改成证据档位,中文给中文报告,英文给英文报告。写明不预测婚姻次数、稳定、离婚、伴侣好坏,也不提示婚前咨询。配偶星那一行改成 0 到 1 的指数,不再写 `/100`。BUG-1318,上游 `f891a2c1`。 | +| T2 KP 判宫 | KP 按宫头到下一宫头。本命宫位表仍按中点,默认宫位不变。传了宫头却不说口径就拒绝;没传记整宫代理。报告多一行判宫口径。BUG-1319,上游 `0337b77f`。 | +| T3 瑜伽排序 | 角宫主、三方主先去重再按名字排序。BUG-1320,上游 `0337b77f`。 | +| T4 大运年长 | 接口里两处 365.25636 改成 365.25,和推运脚本同一天。别的大运系统没动。BUG-1321,上游 `4d543209`。 | +| T5 星历 | 见下。不改代码。BUG-1322,核查中。 | + +## 检查 + +| 检查 | 命令 | 结果 | +| --- | --- | --- | +| 婚恋档位 | `pytest tests/test_d9_evidence_bands.py` | 3 通过 | +| KP 判宫 | `pytest tests/test_kp_cusp_house_assignment.py` | 3 通过 | +| 本命宫位表 | `pytest tests/test_bhava_chalit.py` | 通过(与上面一批共 20 通过、0 失败) | +| 几何指数旧测试 | `pytest tests/test_marriage_counting_geometry_only.py` | 通过(同上) | +| 瑜伽顺序 | `pytest tests/test_yoga_lord_order_stable.py` | 1 通过 | +| 大运年长 | `pytest tests/test_dasha_year_length_alignment.py` | 2 通过 | +| 接口增长合同 | `pytest tests/test_api_server_growth_contract.py` | 通过 | +| KP 报告标题回归 | `pytest tests/test_report_longform_parity.py` 里 KP 那一条 | 通过 | +| 快速门、前端全量 | 未跑 | 按执行分工,留给验收方 | +| 数据库 | 未跑 | 没动表 | + +## T5 星历核查 + +本机把路径设成空字符串后,算太阳位置的返回标记带低精度星历,不带星历文件。仓库里的星历文件在参考资料目录,计算时用不到。排盘和过境两处都设成同一个空路径,所以这两处彼此一致,但都会退回低精度。路径是整个进程共用的,谁后设谁生效。 + +生产镜像这台电脑打不开,不能说线上也是这样。健康检查只看星历库能不能导入,看不出用的是哪一套。建议另开一单再改。 + +## 偏离 + +- 关系时机里还有一处用 65 和 45 去比这个 0 到 1 的指数。任务书只允许改配偶星那一行文字,所以没动。它现在加不上那一档分数。建议另开小单。 +- 生时校正打分文件没有改。 diff --git a/docs/tasks/README.md b/docs/tasks/README.md index e9d145e2..9526c3b1 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -331,7 +331,7 @@ | `TASK-rectification-probe-supply-research-20260913.md` | — | 研究单:六题后引擎在剩余候选上再出带年月题的四种放宽规则,20 例公开 AA 数据离线量收益,有收益才立实现单 | 已合入(离线测量,不改线上) | `b063c668` | | `TASK-rectification-refresh-r3-r4-20260913.md` | `PROGRESS-rectification-refresh-r3-r4-20260913.md` | 刷新阶段 R3(MIN_BOUNDARY_DAYS 45→30)+ R4(pratyantar 与 D9/D10 上升 Narayana);首轮出题不变。即使放宽仍有 0 题例子,定向补事另线保留(BUG-664/665) | 已实现 `ab1ade59`,已部署;2026-09-13 验收通过(实现单由执行方自拟,无产品决策记录段;收益口径见研究文档) | `codex/rectification-refresh-r3-r4-20260913` | | `TASK-upstream-sync6-20261007.md` | [PROGRESS-upstream-sync6-20261007.md](PROGRESS-upstream-sync6-20261007.md) | **上游同步第六轮(`23be1807`)**:10-05 五个计算修正一行未合——D5/D6/D8/D11 映射(乔布斯盘四张分盘星座全不同)、Vimsopaka 权重、Yogini 顺序与出生余额、Panchadhikari 宫位参照、Narayana 并列、特殊上升、出生秒数、Neecha Bhanga 两条件、首段大运子运、岁差被覆盖;逐项对照原书核实;最先做 | 已验收(门禁补修 `2df6ee72`,已部署 staging) | [PROGRESS-upstream-sync6-20261007.md](PROGRESS-upstream-sync6-20261007.md) | -| `TASK-upstream-sync7-20261014.md` | — | **上游同步第七轮(BUG-1318 起)**:婚恋评级断语改证据档位、DK「婚姻质量 X/100」改对;KP 宫头到宫头判宫;瑜伽宫主排序去重;api_server 大运年长 365.25;星历路径只核查。不动校正打分。Grok 执行(agent 循环) | 待执行 | — | +| `TASK-upstream-sync7-20261014.md` | [PROGRESS-upstream-sync7-20261014.md](PROGRESS-upstream-sync7-20261014.md) | **上游同步第七轮(BUG-1318 起)**:婚恋评级断语改证据档位、DK「婚姻质量 X/100」改对;KP 宫头到宫头判宫;瑜伽宫主排序去重;api_server 大运年长 365.25;星历路径只核查。不动校正打分。Grok 执行(agent 循环) | 待验收 | `codex/upstream-sync7-20261014`(BUG-1318~1321 已改;BUG-1322 星历路径核查中,未改代码) | | `TASK-special-lagnas-native-20261008.md` | `PROGRESS-special-lagnas-native-20261008.md` | **特殊上升点原生计算(线上缺陷)**:sync6 U6 运行时调 PyJHora,生产镜像没有 PyJHora → staging 上 Hora/Ghati/Sree/Indu/Pranapada 全 blocked;原生实现、PyJHora 只作冻结对照、快速门禁止运行时 import jhora;最先做 | 已验收(已部署 staging `795b8845`,BUG-1273) | `codex/special-lagnas-native-20261008`(未上线;部署以 `/api/health` 为准) | | `TASK-ephemeris-nutation-20261008.md` | `PROGRESS-ephemeris-nutation-20261008.md` | **恒星黄道少扣章动(BUG-1275)**:行星 = 含章动热带视位置 − 平岁差,全体偏 Δψ(≤17″),大运日期差 2–3 天;改标准算法、PyJHora 冻结对照、scoring-12、v5 重跑 | 已验收(已部署 staging `db0134f9`,BUG-1275) | `codex/ephemeris-nutation-20261008`(未推送,未部署) | | `TASK-sync6-gate-red-20261007.md` | — | **sync6 后门禁仍红**:run 3213 快速门已过,「Frontend and database tests」开跑 20 秒后日志中断、15 分钟后判失败,后续步骤 failure 0 秒;疑运行机掉线;先 Re-run,红则本机带 Docker 跑 `CI=1 npm test` 对照 `0ed45a4b` | 已完成(Re-run 通过,BUG-1271,部署 `2df6ee72`) | — |