fix(yoga): Neecha Bhanga lists the conditions that hold, never Raja Yoga (T8, BUG-1205)

Astrologer ruling 乙14. One condition source, yogas_doshas.calc_nicha_bhanga_raj_yoga
(conditions A-F, now also structured). New scripts/main_yoga_detectors.py turns it
into yoga_engine-shaped rows (zh + en twins) that YogaEngine.detect appends; the
four neechabhanga_* rules are reference_only (kept, renamed, not evaluated).
Card: traditional_yogas canonical 落陷取消(Neecha Bhanga) with 成立条件; compact
yoga entries keep conditions/reductions. Report: category 落陷取消, mixed group,
not raja; personal-report alias neecha_bhanga -> special.

Changed assertion (test_consultation_yoga_detection_merge): canonical name
'Neecha Bhanga Raja Yoga' -> '落陷取消(Neecha Bhanga)' (ruling 乙14).

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 11:38:52 +08:00
co-authored by Claude Opus 5.5
parent 0cf2249e6b
commit df21e591e8
10 changed files with 342 additions and 26 deletions
+19 -2
View File
@@ -759,7 +759,8 @@ TRADITIONAL_YOGAS = (
("Malavya Yoga", ("malavya",)),
("Sasa Yoga", ("sasa", "shasha")),
("Gajakesari Yoga", ("gajakesari",)),
("Neecha Bhanga Raja Yoga", ("neechabhanga", "neechbhanga")),
# BUG-1205 (乙14): never called Raja Yoga; the card lists the conditions that hold.
("落陷取消(Neecha Bhanga)", ("neechabhanga", "neechbhanga")),
# Budha-Aditya: the engine also emits the same Sun-Mercury conjunction as
# "Surya-Budha Yoga"; both map to one row.
("Budha-Aditya Yoga", ("budhaaditya", "budhaditya", "suryabudha")),
@@ -787,6 +788,7 @@ def build_traditional_yogas(modules: dict) -> dict:
if sieve.get("status") != "executed":
return _blocked("raman_support_yogas_missing")
found: dict[str, list[str]] = {}
notes: dict[str, list[str]] = {}
for entry in list(sieve.get("hits") or []) + list(sieve.get("ungoverned_detected") or []):
row = _record(entry)
if row.get("hit") is False:
@@ -798,8 +800,23 @@ def build_traditional_yogas(modules: dict) -> dict:
for planet in row.get("planets") or []:
if isinstance(planet, str) and planet not in planets:
planets.append(planet)
# Main-detector rows: the conditions that hold (Neecha Bhanga, BUG-1205)
# and what weakens a formed Pancha Mahapurusha yoga (BUG-1207).
bucket = notes.setdefault(canonical, [])
for key, prefix in (("conditions", "成立条件:"), ("reductions", "部分减弱:")):
items = [str(item) for item in row.get(key) or [] if item]
if items:
text = prefix + ";".join(items)
if text not in bucket:
bucket.append(text)
def _label(canonical: str) -> str:
parts = ["、".join(found[canonical])] if found[canonical] else []
parts += notes.get(canonical) or []
return f"{canonical}({';'.join(parts)})" if parts else canonical
names = [
f"{canonical}({'、'.join(found[canonical])})" if found[canonical] else canonical
_label(canonical)
for canonical, _prefixes in TRADITIONAL_YOGAS
if canonical in found
]