Files
Jyotisha/tests/test_tajika_kernel.py
ClaudeandClaude Opus 5.5 8de8f3d45c fix(tajika): sync the astrologer's 3509ccd7: oppositions, no factor promotion, annual cases back to template (BUG-1655)
From yinduzhanxing branch codex/local-web-verification-20261026 (remote SHA checked):
- kernel: a 180-degree opposition keeps its 180 target (it was matched as 0);
  exact and equal-speed states are no longer called applying or separating
- named-yoga authority rows carry source_review_status and
  factor_promotion_allowed: false; the annual year-chart layer honours the
  flag, so no Tajika yoga (Ithasala / Easarapha included) becomes a factor
- the five annual oracle cases go back to template_only (archived text kept);
  the dashboards no longer claim annual closure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8
2026-11-06 16:26:46 +08:00

98 lines
3.6 KiB
Python

# Synced from yinduzhanxing 3509ccd7 for BUG-1655.
import pytest
from scripts.tajika_kernel import calculate_tajika_interactions, evaluate_tajika_pair
@pytest.mark.parametrize('longitude,motion', [(179.0, 'applying'), (181.0, 'separating'), (180.0, 'exact')])
def test_opposition_is_retained_instead_of_tested_as_conjunction(longitude, motion):
pair = evaluate_tajika_pair('Sun', 'Moon', {
'Sun': {'longitude': 0.0, 'speed': 1.0},
'Moon': {'longitude': longitude, 'speed': 13.0},
})
assert pair is not None
assert pair['aspect'] == 180.0
assert pair['motion'] == motion
def test_equal_speed_contact_is_not_a_separating_yoga():
planets = _seven()
planets['Moon'] = {'longitude': 59.0, 'speed': 1.0}
result = calculate_tajika_interactions(planets)
pair = next(row for row in result['interactions'] if row['planets'] == ['Sun', 'Moon'])
assert pair['motion'] == 'stationary_relative'
assert not any(row['planets'] == ['Sun', 'Moon'] for row in result['candidate_yogas'])
def test_exact_contact_is_preserved_without_inventing_separation():
planets = _seven()
planets['Moon']['longitude'] = 60.0
result = calculate_tajika_interactions(planets)
pair = next(row for row in result['interactions'] if row['planets'] == ['Sun', 'Moon'])
assert pair['motion'] == 'exact'
assert not any(row['planets'] == ['Sun', 'Moon'] for row in result['candidate_yogas'])
def _seven():
return {
"Sun": {"longitude": 0, "speed": 1.0}, "Moon": {"longitude": 49, "speed": 13.0},
"Mars": {"longitude": 180, "speed": 0.5}, "Mercury": {"longitude": 260, "speed": 1.2},
"Jupiter": {"longitude": 310, "speed": 0.08}, "Venus": {"longitude": 130, "speed": 1.0},
"Saturn": {"longitude": 220, "speed": 0.03}, "Rahu": {"longitude": 60, "speed": -0.05},
}
def test_kernel_detects_cross_sign_aspect_and_excludes_nodes():
result = calculate_tajika_interactions(_seven())
pair = next(row for row in result["interactions"] if row["planets"] == ["Sun", "Moon"])
assert pair["aspect"] == 60.0
assert pair["motion"] == "applying"
candidate = next(row for row in result["candidate_yogas"] if row["planets"] == ["Sun", "Moon"])
assert candidate["name"] == "Ithasala_candidate"
assert candidate["status"] == "partial"
assert result["nodes_excluded"] is True
assert all("Rahu" not in row["planets"] for row in result["interactions"])
def test_kernel_blocks_missing_speed_instead_of_guessing_motion():
planets = _seven()
del planets["Venus"]["speed"]
result = calculate_tajika_interactions(planets)
assert result["status"] == "blocked"
assert "Venus" in result["missing"]
def test_pair_motion_uses_instantaneous_residual_derivative_not_a_one_day_step():
pair = evaluate_tajika_pair(
"Moon",
"Mercury",
{
# The Moon reaches the exact 60-degree aspect in less than a day,
# then overshoots it. It is still applying at this instant.
"Moon": {"longitude": 339.0, "speed": 12.9},
"Mercury": {"longitude": 282.0, "speed": 0.9},
},
)
assert pair is not None
assert pair["residual"] == 3.0
assert pair["motion"] == "applying"
def test_pair_motion_marks_residual_moving_away_as_separating():
pair = evaluate_tajika_pair(
"Mars",
"Saturn",
{
"Mars": {"longitude": 234.3, "speed": 0.48},
"Saturn": {"longitude": 288.57, "speed": 0.01},
},
)
assert pair is not None
assert pair["residual"] < 0
assert pair["motion"] == "separating"