fix(engine): even-sign D30 per BPHS ch. 6 and Venus malefic for Aries / Scorpio per BPHS 34 (BUG-1560, BUG-1570)

Product decision 2026-11-04: the calculation follows the sources even though
rectification scores move.

- scripts/varga.py: even-sign Trimsamsa 12-20 Pisces, 20-25 Capricorn,
  25-30 Scorpio (was Capricorn / Scorpio / Pisces). KNOWN DEFECT comment
  removed; the strict xfail in tests/test_varga_bphs_starts_audit_g.py is now
  an ordinary test and the 0.1-degree BPHS sweep covers varga.py on all signs.
- scripts/functional_benefics.py: ROLE_OVERRIDES adds Venus = malefic for
  Aries (BPHS 34.20) and Scorpio (34.35); profile doc lists both cells.

Both feed rectification scoring; the scoring-13 identity bump, memoization
golden v6 and the ERR-110 re-freeze follow in the next commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y
This commit is contained in:
2026-11-04 17:33:26 +08:00
co-authored by Claude Opus 5.5
parent 607a48b6c3
commit 18c685c428
5 changed files with 30 additions and 20 deletions
+4 -3
View File
@@ -17,9 +17,10 @@ not automatically malefic. Sun/Moon eighth lordship has the stated exception.
Unspecified angular or second-house-only cases remain neutral/mixed.
Explicit exceptions are recorded in `ROLE_OVERRIDES`: Aries Mars helpful in
association (mixed); Taurus Venus challenging, Sun supportive; Gemini Jupiter
challenging; Virgo Jupiter challenging; Libra Venus mixed, Mars challenging;
Scorpio Mars mixed and Sun yoga-supporting; Sagittarius Jupiter/Mercury mixed
association (mixed), Venus challenging (34.20); Taurus Venus challenging, Sun
supportive; Gemini Jupiter challenging; Virgo Jupiter challenging; Libra Venus
mixed, Mars challenging; Scorpio Mars mixed, Sun yoga-supporting, Venus
challenging (34.35); Sagittarius Jupiter/Mercury mixed
(Mercury's association with Sun is separate); Capricorn Moon challenging;
Aquarius Mars challenging, Mercury mixed; Pisces Mercury challenging.
The remaining general fallbacks are identified in each planet's `role_basis`;
+4 -2
View File
@@ -31,12 +31,14 @@ SOURCE = "strict_functional_benefic_malefic_v2"
# precedence. Neutral means mixed/association-dependent, not harmless.
PROFILE = "bphs_ch34_general_with_sign_exceptions_v2"
ROLE_OVERRIDES = {
"Aries": {"Mars": "neutral"},
# BPHS 34.20 (Aries) and 34.35 (Scorpio) name Venus (sita) a malefic
# (BUG-1570, product decision 2026-11-04; scoring-13).
"Aries": {"Mars": "neutral", "Venus": "malefic"},
"Taurus": {"Venus": "malefic", "Sun": "benefic"},
"Gemini": {"Jupiter": "malefic"},
"Virgo": {"Jupiter": "malefic"},
"Libra": {"Venus": "neutral", "Mars": "malefic"},
"Scorpio": {"Mars": "neutral", "Sun": "benefic"},
"Scorpio": {"Mars": "neutral", "Sun": "benefic", "Venus": "malefic"},
"Sagittarius": {"Jupiter": "neutral", "Mercury": "neutral"},
"Capricorn": {"Moon": "malefic"},
"Aquarius": {"Mars": "malefic", "Mercury": "neutral"},
+7 -9
View File
@@ -57,17 +57,15 @@ def _d30_map(si, pi):
if pi<25: return 2
return 6
else:
# KNOWN DEFECT, held back (BUG-1560, blocked): BPHS ch. 6 even signs are
# Venus 5, Mercury 7, Jupiter 8, Saturn 5, Mars 5 -> Taurus, Virgo, Pisces,
# Capricorn, Scorpio (PyJHora method 1; shadbala._d30_sign; trimshamsa_d30;
# divisional_charts_extended). The last three below are Capricorn / Scorpio /
# Pisces. Rectification scoring reads this D30, so the correction needs a
# scoring-13 algorithm identity and a new memoization golden; product decides.
# BPHS ch. 6 even signs: Venus 5, Mercury 7, Jupiter 8, Saturn 5, Mars 5 ->
# Taurus, Virgo, Pisces, Capricorn, Scorpio (PyJHora method 1; shadbala._d30_sign;
# trimshamsa_d30; divisional_charts_extended). Rectification scoring reads this
# D30, so the 2026-11-04 correction raised the identity to scoring-13 (BUG-1560).
if pi<5: return 1
if pi<12: return 5
if pi<20: return 9
if pi<25: return 7
return 11
if pi<20: return 11
if pi<25: return 9
return 7
def varga_map(si, pi, div):
"""BPHS分盘映射:星座索引si的第pi份→目标星座索引"""
@@ -45,3 +45,14 @@ def test_sole_twelfth_lord_is_not_automatically_malefic() -> None:
# Virgo: the Sun owns only the 12th; Leo: the Moon owns only the 12th.
assert "Sun" not in derive_functional_benefic_malefic("Virgo")["functional_malefics"]
assert "Moon" not in derive_functional_benefic_malefic("Leo")["functional_malefics"]
@pytest.mark.parametrize(("sign", "houses"), [("Aries", [2, 7]), ("Scorpio", [7, 12])])
def test_venus_is_functional_malefic_for_aries_and_scorpio(sign: str, houses: list[int]) -> None:
# BUG-1570(产品 2026-11-04 决定):BPHS 34.20「mandasaumyasitāḥ pāpāḥ」、34.35「sitajñaśanayaḥ pāpāḥ」
# 点名金星(sita)为凶;逐上升明文优先于总则(只管角宫 / 2 宫 → 中性)。期望值独立写出,不读 ROLE_OVERRIDES。
result = derive_functional_benefic_malefic(sign)
assert result["owned_houses"]["Venus"] == houses
assert "Venus" in result["functional_malefics"]
assert "Venus" not in result["functional_neutrals"] + result["functional_benefics"]
assert result["role_basis"]["Venus"] == "BPHS ch34 sign-specific exception"
+4 -6
View File
@@ -68,15 +68,13 @@ def test_trimsamsa_matches_bphs_in_the_extended_trimshamsa_and_shadbala_tables()
assert _extended_sign(longitude, 30) == expected, ("extended", sign, degree)
assert trimshamsa_d30.calc_d30_sign(longitude) == expected, ("trimshamsa_d30", sign, degree)
assert shadbala._d30_sign(sign, degree) == expected, ("shadbala", sign, degree)
if sign % 2 == 0: # odd signs: varga.py already agrees
assert calc_varga(longitude, 30)["sign_idx"] == expected, ("varga", sign, degree)
assert calc_varga(longitude, 30)["sign_idx"] == expected, ("varga", sign, degree)
@pytest.mark.xfail(strict=True, reason=(
"BUG-1560 blocked: varga.py even-sign D30 (12-30 deg) still Capricorn/Scorpio/Pisces; the fix moves "
"rectification scores and needs a scoring-13 identity, pending product decision. Remove this mark with the fix."
))
def test_varga_py_even_sign_trimsamsa_matches_bphs() -> None:
# 原值: strict xfail(BUG-1560 blocked,偶数星座 12-30° 仍是摩羯 / 天蝎 / 双鱼)
# 新值: 普通测试,必须通过
# 原因: 产品 2026-11-04 决定按 BPHS 改正 varga.py 偶数星座 D30,校正身份升到 scoring-13
for sign in range(1, 12, 2):
for tenth in range(300):
degree = tenth / 10 + 0.05