feat(narayana-rath): flag Wadiyar pairs as book_software_rule8_exception (BUG-1226)

Round-6 ruling Q6b: Table 25 Gemini/Sagittarius and Virgo/Pisces are the
book software's seventh-level Rule 8 special case; keep the general result
(Sagittarius, Pisces at level 5), every Rath output carries the note
(known_book_differences), test pins the two pairs.

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-04 07:35:37 +08:00
co-authored by Claude Opus 5.5
parent 57ef4560ce
commit 1dd8e11bff
2 changed files with 43 additions and 1 deletions
+21
View File
@@ -96,6 +96,26 @@ BOOK_EXAMPLES = {
"stronger than Cancer. Audit docs/research/shared_engine_reuse_audit_2026_10_03.md N1."),
},
}
# Book software verdicts the general rule does not follow (round-6 ruling
# Q6b, BUG-1226): Table 25 (Krishnaraja Wadiyar IV) takes Gemini over
# Sagittarius and Virgo over Pisces; the seven general levels decide
# Sagittarius / Pisces at level 5 (Mercury 1.3 deg vs Jupiter 10.0 deg). Only
# the book software's Rule 8 at the seventh level separates them that way — a
# software special case. The general result is kept and every output carries
# this note.
BOOK_SOFTWARE_RULE8 = "book_software_rule8_exception"
BOOK_SOFTWARE_DIFFERENCES = {
BOOK_SOFTWARE_RULE8: {
"book_table": "Table 25",
"pairs": [{"book": "Gemini", "general_rule": "Sagittarius", "general_level": 5},
{"book": "Virgo", "general_rule": "Pisces", "general_level": 5}],
"kept": "general_rule",
"note": "原书软件判定差异:原书软件用第 7 级 Rule 8 取双子、处女;通则第 5 级取射手、双鱼,保持通则",
"note_en": ("Book software verdict differs: the book's software used Rule 8 at the seventh level to take "
"Gemini and Virgo; the general rule takes Sagittarius and Pisces at level 5 and is kept"),
"ruling": "astrologer round 6, 2026-10-04, Q6b (p.36, p.95)",
},
}
KETU_FIRST_NOTE = "PyJHora 兼容:计都优先"
KETU_FIRST_NOTE_EN = "PyJHora-compatible: Ketu takes precedence"
SOURCE = {
@@ -461,6 +481,7 @@ def calc_narayana_rath(lagna_sign_idx: int, planet_lons: Dict[str, float],
"source": SOURCE,
"lagna_sign": SIGNS[lagna_sign_idx],
"antardasha_start_rule": "stronger_of_md_sign_and_seventh_then_its_lord_sign (p.48, p.50 note 2)",
"known_book_differences": BOOK_SOFTWARE_DIFFERENCES,
}
if seed_sign_idx is not None:
selection = {"status": "explicit", "selected_sign_idx": seed_sign_idx,
+22 -1
View File
@@ -164,7 +164,9 @@ VERDICT_TABLES = {
# Expected misses, documented: Wadiyar Gemini/Sagittarius and Virgo/Pisces —
# the book takes Gemini and Virgo; the general rule decides at level 5
# (Mercury 1.3 deg vs Jupiter 10.0 deg) for Sagittarius and Pisces. Not
# sensitive to node mode or ayanamsa (audit). Open question to the astrologer.
# sensitive to node mode or ayanamsa (audit). Round-6 ruling Q6b: the book's
# software used Rule 8 at the seventh level — a software special case; keep
# the general result, flagged `book_software_rule8_exception` (BUG-1226).
EXPECTED_SIGN_MISSES = {("Table 25", "Gemini", "Sagittarius"), ("Table 25", "Virgo", "Pisces")}
# Dual lords (Aquarius, Scorpio): book Table 23 Aquarius = Saturn (ours Rahu),
# Table 25 Scorpio = Ketu (ours Mars).
@@ -264,3 +266,22 @@ def test_table25_taurus_md_is_the_saturn_exception_and_carries_the_book_divergen
assert rule["direction"]["direction"] == "zodiacal"
p47, js = rule["direction"]["book_divergence"]
assert (p47["direction"], p47["differs"]) == ("reverse", True) # Taurus is even: p.47 would run the ADs backwards
def test_wadiyar_pairs_are_the_flagged_book_software_rule8_exception() -> None:
"""Round-6 ruling Q6b (BUG-1226): the two Table 25 misses are exactly the
registered `book_software_rule8_exception` pairs; the general result is
kept and the note travels with every Rath output."""
from scripts.narayana_rath import BOOK_SOFTWARE_DIFFERENCES, BOOK_SOFTWARE_RULE8, compare_signs
entry = BOOK_SOFTWARE_DIFFERENCES[BOOK_SOFTWARE_RULE8]
assert BOOK_SOFTWARE_RULE8 == "book_software_rule8_exception"
flagged = {(entry["book_table"], pair["book"], pair["general_rule"]) for pair in entry["pairs"]}
assert flagged == EXPECTED_SIGN_MISSES
assert entry["kept"] == "general_rule" and entry["note"] and entry["note_en"]
chart, asc, *_ = compute_chart_data(*VERDICT_TABLES["Table 25"][0], ayanamsa_name="lahiri")
lons = {name: row["degree"] for name, row in chart["planets"].items()}
for pair in entry["pairs"]:
result = compare_signs(SIGNS.index(pair["book"]), SIGNS.index(pair["general_rule"]), lons)
assert (result["selected_sign"], result["strength_level_decided"]) == (pair["general_rule"], pair["general_level"])
assert calc_narayana_rath(asc, lons, cycle_count=1)["known_book_differences"][BOOK_SOFTWARE_RULE8] == entry