fix(engine): keep the Chinese report free of English glue from #166 triggers and cell notes (BUG-1596, BUG-1597, BUG-1590)
Quick gate: test_report_english_sweep_smoke counted 3 zh_han_function_glue
hits on chart #1, all new in 4b223eb8: the Matrunasa 199 trigger "Moon ->
Mercury -> Moon in house 8" (the renderer turns "house" into Chinese and
leaves "in") and the English ambiguous_cell_notes ("Lagna lord, ...").
- A custom rule's trigger item may be a (Chinese, English) pair; the Chinese
combination uses the Chinese items, combination_en and `triggers` the
English ones, `triggers_zh` the Chinese. Gajakesari, the variant and both
Matrunasa rules emit pairs (PLANET_ZH in the sandbox).
- ambiguous_cell_notes carry Chinese summary / basis plus summary_en /
basis_en, worded without the glue words.
No role or score changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8
This commit is contained in:
@@ -946,7 +946,7 @@
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"expr": "\n# #166 Q5 (BUG-1596): PVR strict definition. Jupiter in a kendra from the Moon (previous condition),\n# conjoined or aspected by a benefic, not debilitated, not combust, not in a natural enemy's sign.\n# The strong-Jupiter substitute is a separate rule (gajakesari_strong_jupiter_variant).\ntriggers = []\nbenefic_links = [b for b in ['Venus', 'Mercury'] if b in ctx.planets and (same_house(b, 'Jupiter') or aspect(b, 'Jupiter'))]\nok = bool(benefic_links) and not debil('Jupiter') and not combust('Jupiter') and not in_natural_enemy_sign('Jupiter')\nif ok:\n triggers = ['benefic association: ' + ', '.join(benefic_links)]\n__result__ = ok\n",
|
||||
"expr": "\n# #166 Q5 (BUG-1596): PVR strict definition. Jupiter in a kendra from the Moon (previous condition),\n# conjoined or aspected by a benefic, not debilitated, not combust, not in a natural enemy's sign.\n# The strong-Jupiter substitute is a separate rule (gajakesari_strong_jupiter_variant).\ntriggers = []\nbenefic_links = [b for b in ['Venus', 'Mercury'] if b in ctx.planets and (same_house(b, 'Jupiter') or aspect(b, 'Jupiter'))]\nok = bool(benefic_links) and not debil('Jupiter') and not combust('Jupiter') and not in_natural_enemy_sign('Jupiter')\nif ok:\n triggers = [('吉星关联:' + '、'.join(PLANET_ZH.get(p, p) for p in benefic_links), 'benefic association: ' + ', '.join(benefic_links))]\n__result__ = ok\n",
|
||||
"combo_template": "严格定义:木星受吉星合相或相照,且不落陷、不燃烧、不落敌宫",
|
||||
"combo_template_en": "Strict definition: Jupiter conjoined or aspected by a benefic, not debilitated, combust or in an enemy sign"
|
||||
}
|
||||
@@ -990,7 +990,7 @@
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"expr": "\n# #166 Q5: variant only when the strict definition does not hold.\nbenefic_links = [b for b in ['Venus', 'Mercury'] if b in ctx.planets and (same_house(b, 'Jupiter') or aspect(b, 'Jupiter'))]\nstrict = bool(benefic_links) and not debil('Jupiter') and not combust('Jupiter') and not in_natural_enemy_sign('Jupiter')\nstrong_jup = exalted('Jupiter') or own('Jupiter') or moola('Jupiter')\ntriggers = [f\"strong Jupiter: {dignity('Jupiter')}\"] if strong_jup and not strict else []\n__result__ = strong_jup and not strict and not debil('Jupiter')\n",
|
||||
"expr": "\n# #166 Q5: variant only when the strict definition does not hold.\nbenefic_links = [b for b in ['Venus', 'Mercury'] if b in ctx.planets and (same_house(b, 'Jupiter') or aspect(b, 'Jupiter'))]\nstrict = bool(benefic_links) and not debil('Jupiter') and not combust('Jupiter') and not in_natural_enemy_sign('Jupiter')\nstrong_jup = exalted('Jupiter') or own('Jupiter') or moola('Jupiter')\ntriggers = [(f\"木星强:{dignity('Jupiter')}\", f\"strong Jupiter: {dignity('Jupiter')}\")] if strong_jup and not strict else []\n__result__ = strong_jup and not strict and not debil('Jupiter')\n",
|
||||
"combo_template": "变体:木星强(入旺 / 本宫 / 根本三角)代替吉星关联",
|
||||
"combo_template_en": "Variant: a strong Jupiter (exalted / own / moolatrikona) in place of benefic association"
|
||||
}
|
||||
@@ -11578,7 +11578,7 @@
|
||||
"category": "durbhaga",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\n# #166 Q6 (BUG-1597): Raman 300 Combinations no. 198 only (no. 199 is bvr_matrunasa_199).\n# Hemmed by, associated with, or aspected by natural malefics (OR). Aspects are named Parashari graha\n# aspects (7th; Mars 4/8, Jupiter 5/9, Saturn 3/10); Rahu / Ketu count for conjunction and hemming,\n# not for aspect (implementation choice recorded in BUG-1597). No Jaimini rasi drishti.\ntriggers = []\nif 'Moon' in ctx.planets:\n moon_h = house_of('Moon')\n prev_h = ((moon_h - 2) % 12) + 1\n next_h = (moon_h % 12) + 1\n prev_m = [p for p in MALEFICS if p in ctx.planets and house_of(p) == prev_h]\n next_m = [p for p in MALEFICS if p in ctx.planets and house_of(p) == next_h]\n if prev_m and next_m:\n triggers.append('hemmed: ' + ', '.join(prev_m + next_m))\n conj = [p for p in MALEFICS if p in ctx.planets and same_house('Moon', p)]\n if conj:\n triggers.append('with Moon: ' + ', '.join(conj))\n asp = [p for p in ('Sun', 'Mars', 'Saturn') if p in ctx.planets and not same_house('Moon', p) and aspect(p, 'Moon')]\n if asp:\n triggers.append('aspects Moon: ' + ', '.join(asp))\n__result__ = bool(triggers)\n",
|
||||
"expr": "\n# #166 Q6 (BUG-1597): Raman 300 Combinations no. 198 only (no. 199 is bvr_matrunasa_199).\n# Hemmed by, associated with, or aspected by natural malefics (OR). Aspects are named Parashari graha\n# aspects (7th; Mars 4/8, Jupiter 5/9, Saturn 3/10); Rahu / Ketu count for conjunction and hemming,\n# not for aspect (implementation choice recorded in BUG-1597). No Jaimini rasi drishti.\ntriggers = []\nif 'Moon' in ctx.planets:\n moon_h = house_of('Moon')\n prev_h = ((moon_h - 2) % 12) + 1\n next_h = (moon_h % 12) + 1\n prev_m = [p for p in MALEFICS if p in ctx.planets and house_of(p) == prev_h]\n next_m = [p for p in MALEFICS if p in ctx.planets and house_of(p) == next_h]\n if prev_m and next_m:\n triggers.append(('凶星夹制:' + '、'.join(PLANET_ZH.get(p, p) for p in prev_m + next_m), 'hemmed: ' + ', '.join(prev_m + next_m)))\n conj = [p for p in MALEFICS if p in ctx.planets and same_house('Moon', p)]\n if conj:\n triggers.append(('与月亮同宫:' + '、'.join(PLANET_ZH.get(p, p) for p in conj), 'with Moon: ' + ', '.join(conj)))\n asp = [p for p in ('Sun', 'Mars', 'Saturn') if p in ctx.planets and not same_house('Moon', p) and aspect(p, 'Moon')]\n if asp:\n triggers.append(('相照月亮:' + '、'.join(PLANET_ZH.get(p, p) for p in asp), 'aspects Moon: ' + ', '.join(asp)))\n__result__ = bool(triggers)\n",
|
||||
"combo_template": "Raman 198:月亮受凶星夹制、合相或相照",
|
||||
"combo_template_en": "Raman 198: Moon hemmed, conjoined or aspected by malefics"
|
||||
},
|
||||
@@ -11603,7 +11603,7 @@
|
||||
"category": "durbhaga",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\n# #166 Q6: Raman no. 199. Lord of the D9 sign holding the 4th lord; then the lord of the D9 sign\n# that planet holds; that final planet in the 6th / 8th / 12th of D1.\ntriggers = []\nl4 = lord(4)\nnav_lord = navamsa_dispositor(l4) if l4 else None\nfinal_planet = navamsa_dispositor(nav_lord) if nav_lord else None\nif final_planet in ctx.planets and house_of(final_planet) in [6, 8, 12]:\n triggers.append(f'{l4} -> {nav_lord} -> {final_planet} in house {house_of(final_planet)}')\n__result__ = bool(triggers)\n",
|
||||
"expr": "\n# #166 Q6: Raman no. 199. Lord of the D9 sign holding the 4th lord; then the lord of the D9 sign\n# that planet holds; that final planet in the 6th / 8th / 12th of D1.\ntriggers = []\nl4 = lord(4)\nnav_lord = navamsa_dispositor(l4) if l4 else None\nfinal_planet = navamsa_dispositor(nav_lord) if nav_lord else None\nif final_planet in ctx.planets and house_of(final_planet) in [6, 8, 12]:\n triggers.append((f'{PLANET_ZH.get(l4, l4)} → {PLANET_ZH.get(nav_lord, nav_lord)} → {PLANET_ZH.get(final_planet, final_planet)},落第 {house_of(final_planet)} 宫', f'{l4} -> {nav_lord} -> {final_planet}, house {house_of(final_planet)}'))\n__result__ = bool(triggers)\n",
|
||||
"combo_template": "Raman 199:4宫主的 Navamsa 链最终落 6 / 8 / 12 宫",
|
||||
"combo_template_en": "Raman 199: the 4th lord's Navamsa chain ends in the 6th / 8th / 12th"
|
||||
},
|
||||
|
||||
@@ -53,14 +53,14 @@ ROLE_OVERRIDES = {
|
||||
# summary; the other lordship is kept as a duty, never dropped. Basis says whether
|
||||
# the sign's own verse states it or it is derived from the general rules.
|
||||
AMBIGUOUS_CELL_NOTES = {
|
||||
("Taurus", "Mercury"): ("mildly benefic; keeps 2nd-lord duties", "sign verse: somewhat auspicious"),
|
||||
("Gemini", "Mercury"): ("Lagna lord, supportive; not a Yogakaraka", "general rules"),
|
||||
("Gemini", "Saturn"): ("mixed; 9th-lord support, not a pure benefic", "general rules with sign context"),
|
||||
("Sagittarius", "Saturn"): ("challenging (3rd lord); Maraka (2nd lord) listed separately", "3rd-house rule plus sign verse"),
|
||||
("Cancer", "Jupiter"): ("mildly benefic; keeps 6th-lord duties", "sign verse"),
|
||||
("Leo", "Jupiter"): ("mildly benefic; keeps 8th-lord duties", "sign verse"),
|
||||
("Virgo", "Saturn"): ("mixed; not a pure benefic on the 5th lordship alone", "sign verse does not name it a benefic"),
|
||||
("Capricorn", "Mercury"): ("mildly benefic; keeps 6th-lord duties", "sign verse"),
|
||||
("Taurus", "Mercury"): ("偏吉,保留 2 宫主职责", "专条:有所吉利", "mildly benefic; keeps 2nd-lord duties", "sign verse: somewhat auspicious"),
|
||||
("Gemini", "Mercury"): ("上升主,支持;不自动认定 Yogakaraka", "通用规则推导", "Ascendant ruler, supportive; not a Yogakaraka", "general rules"),
|
||||
("Gemini", "Saturn"): ("混合,9 宫支持,不标纯吉", "通用规则与专条语境并列", "mixed; 9th-house support, not a pure benefic", "general rules with sign context"),
|
||||
("Sagittarius", "Saturn"): ("功能挑战(3 宫主);Maraka(2 宫主)另列", "3 宫通则加专条身份", "challenging (3rd house); Maraka (2nd house) listed separately", "3rd-house rule plus sign verse"),
|
||||
("Cancer", "Jupiter"): ("偏吉,保留 6 宫主职责", "专条明确支持", "mildly benefic; keeps 6th-house duties", "sign verse"),
|
||||
("Leo", "Jupiter"): ("偏吉,保留 8 宫主职责", "专条明确支持", "mildly benefic; keeps 8th-house duties", "sign verse"),
|
||||
("Virgo", "Saturn"): ("混合,不只凭 5 宫判纯吉", "专条未明确列为吉星", "mixed; not a pure benefic on the 5th house alone", "sign verse does not name it a benefic"),
|
||||
("Capricorn", "Mercury"): ("偏吉,保留 6 宫主职责", "专条明确支持", "mildly benefic; keeps 6th-house duties", "sign verse"),
|
||||
}
|
||||
|
||||
|
||||
@@ -158,8 +158,8 @@ def derive_functional_benefic_malefic(ascendant: Any) -> dict[str, Any]:
|
||||
"rule_reference": "references/functional-role-profile.md",
|
||||
"assertion_label": "parameter_sensitive",
|
||||
"ambiguous_cell_notes": {
|
||||
planet: {"summary": note, "basis": basis}
|
||||
for (sign, planet), (note, basis) in AMBIGUOUS_CELL_NOTES.items() if sign == asc_sign
|
||||
planet: {"summary": note, "basis": basis, "summary_en": note_en, "basis_en": basis_en}
|
||||
for (sign, planet), (note, basis, note_en, basis_en) in AMBIGUOUS_CELL_NOTES.items() if sign == asc_sign
|
||||
},
|
||||
"role_basis": {
|
||||
planet: "BPHS ch34 sign-specific exception" if planet in ROLE_OVERRIDES.get(asc_sign, {})
|
||||
|
||||
+14
-4
@@ -65,6 +65,9 @@ NATURAL_ENEMIES = {
|
||||
# Combustion orbs (direct motion), same values as jyotish_engine.COMBUSTION_ORBS
|
||||
# direct column; used only when the chart row carries no `combust` flag.
|
||||
COMBUSTION_ORBS_DIRECT = {'Moon': 12.0, 'Mars': 17.0, 'Mercury': 14.0, 'Jupiter': 11.0, 'Venus': 10.0, 'Saturn': 15.0}
|
||||
# Short Chinese planet names for bilingual rule triggers (#166 Q5 / Q6).
|
||||
PLANET_ZH = {'Sun': '太阳', 'Moon': '月亮', 'Mars': '火星', 'Mercury': '水星', 'Jupiter': '木星',
|
||||
'Venus': '金星', 'Saturn': '土星', 'Rahu': '罗睺', 'Ketu': '计都'}
|
||||
PLANET_CN = {
|
||||
"Ketu": "南交点Ketu", "Venus": "金星Venus", "Sun": "太阳Sun",
|
||||
"Moon": "月亮Moon", "Mars": "火星Mars", "Rahu": "北交点Rahu",
|
||||
@@ -823,6 +826,7 @@ class YogaEngine:
|
||||
"category": rule.get("category", ""),
|
||||
"rule_id": rule.get("id", ""),
|
||||
**({"triggers": m["triggers"]} if m.get("triggers") else {}),
|
||||
**({"triggers_zh": m["triggers_zh"]} if m.get("triggers_zh") else {}),
|
||||
**({"canonical_mapping": rule["canonical_mapping"]} if rule.get("canonical_mapping") else {}),
|
||||
# English twins for the reader's English edition; the Chinese
|
||||
# fields above are unchanged.
|
||||
@@ -1011,7 +1015,8 @@ class YogaEngine:
|
||||
"combination": merged_combo,
|
||||
"strength": sm.get("strength", match.get("strength")),
|
||||
"bindings": {**match.get("bindings", {}), **sm.get("bindings", {})},
|
||||
**({"triggers": (match.get("triggers") or []) + (sm.get("triggers") or [])}
|
||||
**({"triggers": (match.get("triggers") or []) + (sm.get("triggers") or []),
|
||||
"triggers_zh": (match.get("triggers_zh") or []) + (sm.get("triggers_zh") or [])}
|
||||
if match.get("triggers") or sm.get("triggers") else {}),
|
||||
})
|
||||
results = new_results
|
||||
@@ -1538,11 +1543,15 @@ class YogaEngine:
|
||||
strength = strength(ctx, bindings)
|
||||
# #166 Q6 (BUG-1597): a rule may set `triggers` (list of short strings)
|
||||
# to name what actually fired; they are appended to the combination.
|
||||
# An item is either one string (used in both editions) or a
|
||||
# (Chinese, English) pair, so the Chinese report never carries English glue.
|
||||
triggers = exec_globals.get("triggers")
|
||||
if isinstance(triggers, list) and triggers:
|
||||
triggers = [str(item) for item in triggers]
|
||||
combo = BiText(f"{combo}:{';'.join(triggers)}", f"{_en_text(combo)}: {'; '.join(triggers)}")
|
||||
return [{"combination": combo, "strength": strength, "triggers": triggers}]
|
||||
pairs = [(item[0], item[1]) if isinstance(item, (list, tuple)) and len(item) == 2 else (item, item) for item in triggers]
|
||||
zh_items = [str(zh) for zh, _ in pairs]
|
||||
en_items = [str(en) for _, en in pairs]
|
||||
combo = BiText(f"{combo}:{';'.join(zh_items)}", f"{_en_text(combo)}: {'; '.join(en_items)}")
|
||||
return [{"combination": combo, "strength": strength, "triggers": en_items, "triggers_zh": zh_items}]
|
||||
return [{"combination": combo, "strength": strength}]
|
||||
return []
|
||||
|
||||
@@ -1835,6 +1844,7 @@ class YogaEngine:
|
||||
"house_strength": house_strength, "strong": strong, "weak": weak,
|
||||
"associated": associated, "temporal_friend": temporal_friend, "natural_friend": natural_friend,
|
||||
"combust": combust, "in_natural_enemy_sign": in_natural_enemy_sign,
|
||||
"PLANET_ZH": PLANET_ZH,
|
||||
"occupants": occupants, "only_benefics_in_house": only_benefics_in_house,
|
||||
"only_malefics_in_house": only_malefics_in_house,
|
||||
"house_has_benefic": house_has_benefic, "house_has_malefic": house_has_malefic,
|
||||
|
||||
@@ -122,6 +122,7 @@ def test_q5_strict_gajakesari_holds_with_benefic_and_clean_jupiter() -> None:
|
||||
assert "Gajakesari Yoga" in rows
|
||||
# Venus conjoined; Mercury in Virgo aspects Pisces (7th).
|
||||
assert rows["Gajakesari Yoga"]["triggers"] == ["benefic association: Venus, Mercury"]
|
||||
assert rows["Gajakesari Yoga"]["triggers_zh"] == ["吉星关联:金星、水星"]
|
||||
assert "Strong-Jupiter Gajakesari Variant" not in rows
|
||||
|
||||
|
||||
@@ -142,4 +143,5 @@ def test_q6_matrunasa_198_rasi_drishti_alone_no_longer_fires_and_triggers_are_na
|
||||
rows = {row["name"]: row for row in _detect(planets, "Aries")}
|
||||
hit = rows.get("Matrunasa Yoga")
|
||||
assert hit is not None and hit["triggers"] == ["aspects Moon: Saturn"]
|
||||
assert hit["triggers_zh"] == ["相照月亮:土星"]
|
||||
assert hit["effects"] and "健康" not in hit["effects"][0].replace("不作母亲健康", "")
|
||||
|
||||
Reference in New Issue
Block a user