fix(yoga): D9 debilitation only weakens Pancha Mahapurusha (BUG-1213)

Round-4 astrologer ruling 2026-10-03 Q1: the yoga stays formed with
d9_dignity_modifier='weakened' and the reason 'D9 落陷,兑现减弱'; only a failed
D1 condition is not_formed. The D9 sign is derived from the longitude when the
caller does not pass navamsa_sign, so the card, report and /api/pancha_mahapurusha
paths all check it. None of the nine public celebrity charts change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-10-03 16:18:06 +08:00
co-authored by Claude Opus 5.5
parent 52b3061fe5
commit bc03bba26b
4 changed files with 117 additions and 47 deletions
+2 -1
View File
@@ -7127,7 +7127,8 @@ class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryM
sun_lon = (planets.get('Sun') or {}).get('lon')
for y in pancha_mahapurusha_rows(planets, sun_degree=sun_lon):
yogas.append({'name': y['name'], 'planets': y['planets'], 'category': 'PMC',
'reductions': y['reductions']})
'reductions': y['reductions'],
'd9_dignity_modifier': y.get('d9_dignity_modifier')})
except Exception as e:
import logging
logging.warning(f"[api_server] pancha_mahapurusha detection failed: {e}")
+4 -2
View File
@@ -15,8 +15,9 @@ English reader edition).
only the cancellation conditions that hold are listed, never called Raja Yoga.
- Pancha Mahapurusha (BUG-1207, 甲5): `pancha_mahapurusha.detect_pancha_mahapurusha`;
kendras from the Lagna only, retrograde does not cancel, combustion / malefic
conjunction or aspect only mark the yoga `reduced` (部分减弱), D9 debilitation
still cancels it (current behaviour; not in the ruling, asked next round).
conjunction or aspect only mark the yoga `reduced` (部分减弱); D9 debilitation
only weakens it (round-4 ruling 2026-10-03 Q1, BUG-1213): the yoga stays formed
with `d9_dignity_modifier: "weakened"` and the reason 「D9 落陷,兑现减弱」.
- Kemadruma (BUG-1206, 甲7): `yoga_expansion.detect_kemadruma`; the 2nd / 12th
from the Moon count only Mars, Mercury, Jupiter, Venus, Saturn, and there is no
kendra cancellation (planets in kendras, including only Rahu / Ketu there, do
@@ -185,6 +186,7 @@ def pancha_mahapurusha_rows(planets: Dict[str, Any], sun_degree: Optional[float]
'combination_en': combination_en,
'reductions': reductions,
'reductions_en': reductions_en,
'd9_dignity_modifier': item.get('d9_dignity_modifier'),
'effects': ['卓越才能', '领域领军', '人格魅力'],
'effects_en': ['Outstanding abilities', "Leader in one's field", 'Personal charisma'],
'strength': '部分减弱' if reductions else '强',
+58 -28
View File
@@ -12,7 +12,9 @@ Pancha Mahapurusha Yoga(五王瑜伽)完整检测模块 v1.0
- Shasha Yoga (Saturn)
破格口径按占星师定稿 2026-10-03 甲5(BUG-1207):逆行不破格;燃烧、与凶星
同宫或被照只记「部分减弱」;D9 落陷判不成立(现状,待问)。本模块是五大人格的
同宫或被照只记「部分减弱」。D9 落陷按占星师第四轮裁定(2026-10-03 问 1,BUG-1213)
只算减弱:格局仍成立(`status: formed`),加 `d9_dignity_modifier: "weakened"`;只有
D1 条件(命宫角宫 + 入庙/入旺)不成立时才是 `not_formed`。本模块是五大人格的
唯一主检测器,`yoga_rules.json` 同名规则与 `yogas_doshas.calc_pancha_mahapurusha_yoga`
为 reference_only。
"""
@@ -108,6 +110,17 @@ def _malefic_aspectors(planet_sign: str, all_planets: Dict, planet: str = None)
return casters
def navamsa_sign_of(lon: float) -> str:
"""D9 sign of a sidereal longitude (Parashari navamsa, 3°20' each)."""
return SIGNS[int((float(lon) % 360.0) * 9.0 / 30.0) % 12]
def _debilitation_sign(planet: str) -> str:
if planet not in EXALTATION:
return ''
return SIGNS[(SIGNS.index(EXALTATION[planet][0]) + 6) % 12]
def _longitude(pd: Dict) -> Optional[float]:
"""Sidereal longitude of a row; `degree` may be a longitude or a degree in sign."""
for key in ('lon', 'degree_raw', 'longitude'):
@@ -127,26 +140,31 @@ PLANET_ZH = {'Sun': '太阳', 'Mars': '火星', 'Mercury': '水星', 'Jupiter':
'Saturn': '土星', 'Rahu': '罗睺', 'Ketu': '计都'}
def detect_pancha_mahapurusha(planets: Dict, sun_degree: float = None) -> List[Dict]:
def detect_pancha_mahapurusha(planets: Dict, sun_degree: float = None,
include_not_formed: bool = False) -> List[Dict]:
"""
检测所有Pancha Mahapurusha Yoga(主检测器)。
占星师定稿 2026-10-03 甲5(BUG-1207):
占星师定稿 2026-10-03 甲5(BUG-1207)与第四轮问 1(BUG-1213):
- 只从命宫(Lagna)角宫计算:行星在上升起第 1/4/7/10 宫且入庙或入旺;
这一条不满足才是 `not_formed`;
- 逆行不破格;
- 燃烧不破格,只记「部分减弱」(`strength: reduced`);
- 与凶星同宫或被凶星照只降低强度,不消灭格局;
- D9 落陷判不成立(`is_valid: False`)保留现状:只在传入 `navamsa_sign` 时检查,
产品路径不传;定稿未提及,待问占星师。
- D9 落陷只算减弱:`status` 仍是 `formed`,加 `d9_dignity_modifier: "weakened"`,
原因列「D9 落陷,兑现减弱」。D9 星座取调用方给的 `navamsa_sign`,没给时由
黄道经度推出,所以各路径(咨询、报告、`/api/pancha_mahapurusha`)都检查 D9。
Args:
planets: {planet: {'sign': str, 'house': int, 'lon'|'degree': float, 'navamsa_sign'?: str}}
`house` 从上升数;经度取 `lon` / `degree_raw` / `longitude`,否则 `degree`
(≥30 视为经度,否则按星座内度数)。
sun_degree: 太阳黄道经度(燃烧检测);缺省时取 planets['Sun'] 的经度。
include_not_formed: 为 True 时,D1 条件不成立的五星也返回一行
(`status: not_formed`、`is_valid: False`)。
Returns:
检测到的PMC Yoga列表(含 strength: full / reduced / cancelled)
检测到的PMC Yoga列表(含 status: formed / not_formed,strength: full / reduced)
"""
yogas = []
sun_deg = sun_degree
@@ -161,10 +179,23 @@ def detect_pancha_mahapurusha(planets: Dict, sun_degree: float = None) -> List[D
sign = pd.get('sign', '')
house = pd.get('house', 0)
# 基本条件:在Kendra + own/exalted
if house not in KENDRA_HOUSES:
continue
if not _is_exalted_or_own(planet, sign):
# 基本条件:在Kendra + own/exalted(D1)。不满足即 not_formed。
if house not in KENDRA_HOUSES or not _is_exalted_or_own(planet, sign):
if include_not_formed:
yogas.append({
'name': MAHAPURUSHA_NAMES[planet],
'planet': planet,
'sign': sign,
'house': house,
'status': 'not_formed',
'is_valid': False,
'strength': 'not_formed',
'strength_cn': '不成立',
'reason': 'd1_kendra_own_or_exalted_not_met',
'reductions': [],
'reductions_en': [],
'cancellations': [],
})
continue
yoga_name = MAHAPURUSHA_NAMES[planet]
@@ -173,7 +204,6 @@ def detect_pancha_mahapurusha(planets: Dict, sun_degree: float = None) -> List[D
cancellations = []
reductions = []
reductions_en = []
is_valid = True
# 1. 燃烧:部分减弱(容许度表与 jyotish_engine.COMBUSTION_ORBS 一致,
# tests/test_combustion_orb_tables_agree.py)
@@ -197,27 +227,28 @@ def detect_pancha_mahapurusha(planets: Dict, sun_degree: float = None) -> List[D
reductions.append(f"受凶星照:{'、'.join(PLANET_ZH.get(m, m) for m in aspectors)}")
reductions_en.append(f"aspected by malefic(s): {', '.join(aspectors)}")
# 4. Navamsa弱势(D9中落陷):不成立。现状保留——只在调用方给出
# `navamsa_sign` 时检查;产品路径(chart.yogas、规则引擎、报告)都不传,
# 所以这一条目前不生效。定稿未提及,记为下轮问题,不在本单启用。
navamsa_sign = pd.get('navamsa_sign', '')
if navamsa_sign in OWN_SIGNS.get(planet, set()) or (planet in EXALTATION and EXALTATION[planet][0] == navamsa_sign):
pass # D9 also strong → yoga reinforced
elif navamsa_sign:
deb_sign_idx = (SIGNS.index(EXALTATION[planet][0]) + 6) % 12 if planet in EXALTATION else -1
if deb_sign_idx >= 0 and navamsa_sign == SIGNS[deb_sign_idx]:
cancellations.append(f"{planet}在Navamsa落陷 — Yoga根基不稳")
is_valid = False
# 4. D9 落陷:只算减弱(第四轮问 1,BUG-1213),格局仍成立。
navamsa_sign = pd.get('navamsa_sign') or ''
if not navamsa_sign and planet_deg is not None:
navamsa_sign = navamsa_sign_of(planet_deg)
d9_modifier = None
if navamsa_sign and navamsa_sign == _debilitation_sign(planet):
d9_modifier = 'weakened'
reductions.append('D9 落陷,兑现减弱')
reductions_en.append('debilitated in D9: delivery weakened')
strength = 'cancelled' if not is_valid else ('reduced' if reductions else 'full')
strength = 'reduced' if reductions else 'full'
yogas.append({
'name': yoga_name,
'planet': planet,
'sign': sign,
'house': house,
'is_valid': is_valid,
'status': 'formed',
'is_valid': True,
'strength': strength,
'strength_cn': {'full': '完整', 'reduced': '部分减弱', 'cancelled': '不成立'}[strength],
'strength_cn': {'full': '完整', 'reduced': '部分减弱'}[strength],
'd9_sign': navamsa_sign or None,
'd9_dignity_modifier': d9_modifier,
'effect': effect,
'reductions': reductions,
'reductions_en': reductions_en,
@@ -239,13 +270,11 @@ def assess_pmc_strength(planets: Dict, sun_degree: float = None) -> Dict:
invalid = [y for y in yogas if not y['is_valid']]
reduced = [y['name'] for y in valid if y['strength'] == 'reduced']
d9_weakened = [y['name'] for y in valid if y.get('d9_dignity_modifier') == 'weakened']
if len(valid) >= 2:
assessment = "多重大王瑜伽 — 命主在不同领域均有杰出天赋"
elif len(valid) == 1:
assessment = f"{valid[0]['name']} — 命主在特定领域有卓越潜力"
elif len(invalid) > 0:
st = [y['planet'] for y in invalid]
assessment = f"瑜伽潜质存在但因{', '.join(st)}在 D9 落陷而不成立"
else:
assessment = "无Pancha Mahapurusha Yoga"
@@ -254,6 +283,7 @@ def assess_pmc_strength(planets: Dict, sun_degree: float = None) -> Dict:
'yogas': yogas,
'valid_count': len(valid),
'reduced': reduced,
'd9_weakened': d9_weakened,
'invalid_count': len(invalid),
'assessment': assessment,
}