refactor strict evidence and extend parity layers

This commit is contained in:
732642856
2026-07-15 19:13:05 +08:00
parent beea0f1996
commit ec9c7cf80a
20 changed files with 600 additions and 42 deletions
+25
View File
@@ -4,6 +4,7 @@ from datetime import datetime, timezone
from pathlib import Path
from benchmarks.jyotish.scripts.run_pyjhora_compare import write_report
from benchmarks.jyotish.scripts.run_pyjhora_compare import compare_one
ROOT = Path(__file__).resolve().parents[1]
@@ -17,6 +18,7 @@ def test_pyjhora_compare_help_is_non_executing():
assert result.returncode == 0
assert "--build-local" in result.stdout
assert "--refresh-local" in result.stdout
assert "--output-prefix" in result.stdout
assert "FileNotFoundError" not in result.stderr
@@ -30,3 +32,26 @@ def test_pyjhora_report_uses_supplied_utc_generation_timestamp():
assert "生成时间:2026-07-15T04:30:00+00:00" in report
assert "生成时间:2026-06-03" not in report
def test_pyjhora_comparison_includes_d2_d4_bav_sav_and_shadbala_rows():
bodies = ["Ascendant", "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu"]
chart = {body: {"sign": "Aries", "degree_in_sign": 1.0} for body in bodies}
planet = {body: {"sign": "Aries", "degree_in_sign": 1.0, "nakshatra": "Ashwini", "nakshatra_pada": 1} for body in bodies[1:]}
local = {
"ascendant": chart["Ascendant"], "planets": planet,
"varga": {"D2": chart, "D4": chart, "D9": chart, "D10": chart},
"dasha": {},
"ashtakavarga": {"bav": {name: [1] * 12 for name in ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Lagna"]}, "sav": [7] * 12},
"shadbala": {name: 100.0 for name in ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]},
}
pyjhora = {
"ascendant": chart["Ascendant"], "planets": planet,
"varga": {"D2": chart, "D4": chart, "D9": chart, "D10": chart},
"dasha": {},
"ashtakavarga": local["ashtakavarga"], "shadbala": local["shadbala"],
}
sections = {row["section"] for row in compare_one("fixture", local, pyjhora)}
assert {"D2", "D4", "Ashtakavarga_BAV", "Ashtakavarga_SAV", "Shadbala"} <= sections
+14
View File
@@ -43,3 +43,17 @@ def test_mcp_docstring_marks_workbuddy_as_distribution_mirror_not_runtime_source
assert "/.workbuddy/skills/jyotish-vedic-astrology/mcp_server.py" not in text
assert "distribution mirror" in text
assert "reference only" in text
def test_engine_does_not_import_mcp_server_for_strict_evidence() -> None:
text = (ROOT / "scripts" / "jyotish_engine.py").read_text(encoding="utf-8", errors="ignore")
assert "from mcp_server import" not in text
assert "mcp_server.py" not in text
assert "strict_evidence_service" in text
def test_mcp_server_does_not_instantiate_api_handler_directly() -> None:
text = (ROOT / "mcp_server.py").read_text(encoding="utf-8", errors="ignore")
assert "from jyotish_api_server import" not in text
assert "JyotishAPIHandler" not in text
assert "consultation_workflow_service" in text
+9 -6
View File
@@ -36,13 +36,15 @@ def test_shadbala_oracle_closure_status_identifies_first_absolute_value_packet()
report = json.loads(completed.stdout)
assert report["scope"] == "shadbala_external_absolute_value_closure_status"
assert report["schema_version"] == 1
assert report["summary"]["shadbala_task_count"] == 4
assert report["summary"]["external_verified_shadbala_tasks"] == 4
assert report["summary"]["can_claim_shadbala_absolute_closure"] is True
assert report["summary"]["shadbala_task_count"] == 2
assert report["summary"]["external_verified_shadbala_tasks"] == 2
assert report["summary"]["external_packet_fields_complete"] is True
assert report["summary"]["same_chart_parity_status"] == "blocked"
assert report["summary"]["can_claim_shadbala_absolute_closure"] is False
assert report["summary"]["required_planets"] == ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
assert report["summary"]["required_components"] == ["sthana", "dig", "kala", "chesta", "naisargika", "drik", "total_rupa"]
assert report["first_priority"] is None
assert report["next_actions"][0] == "Shadbala external absolute-value closure is complete for the current target set."
assert report["next_actions"][0] == "Reconcile Shadbala component-level formulas against PyJHora/JHora same-chart raw values."
def test_shadbala_oracle_closure_status_markdown_can_be_written(tmp_path: Path) -> None:
@@ -53,5 +55,6 @@ def test_shadbala_oracle_closure_status_markdown_can_be_written(tmp_path: Path)
assert output.exists()
markdown = output.read_text(encoding="utf-8")
assert "# Shadbala External Absolute-Value Closure Status" in markdown
assert "can_claim_shadbala_absolute_closure: `true`" in markdown
assert "closure is complete for the current target set" in markdown
assert "same_chart_parity_status: `blocked`" in markdown
assert "can_claim_shadbala_absolute_closure: `false`" in markdown
assert "same-chart parity is still blocked" in markdown
+21
View File
@@ -44,6 +44,13 @@ def drekkana_ref(lon: float) -> int:
return (sign_index + part_index * 4) % 12
def chaturthamsa_ref(lon: float) -> int:
sign_index = int((lon % 360) / 30) % 12
degree_in_sign = (lon % 360) - sign_index * 30
part_index = int(degree_in_sign / (30 / 4))
return (sign_index + part_index * 3) % 12
@given(st.floats(min_value=0, max_value=359.999999, allow_nan=False, allow_infinity=False))
def test_navamsa_matches_bphs_reference(lon: float) -> None:
result = calc_varga(lon, 9)
@@ -66,6 +73,20 @@ def test_drekkana_uses_same_plus_four_plus_eight(lon: float) -> None:
assert result["sign_idx"] == drekkana_ref(lon)
@given(st.floats(min_value=0, max_value=359.999999, allow_nan=False, allow_infinity=False))
def test_chaturthamsa_matches_pyjhora_parashara_reference(lon: float) -> None:
result = calc_varga(lon, 4)
assert result["sign_idx"] == chaturthamsa_ref(lon)
assert result["sign"] == SIGNS[chaturthamsa_ref(lon)]
assert 0 <= result["degree_in_sign"] < 30
def test_chaturthamsa_reference_boundary_examples() -> None:
assert [varga_map(0, part, 4) for part in range(4)] == [0, 3, 6, 9]
assert [varga_map(8, part, 4) for part in range(4)] == [8, 11, 2, 5]
assert [varga_map(11, part, 4) for part in range(4)] == [11, 2, 5, 8]
def test_varga_map_boundary_examples() -> None:
assert varga_map(0, 0, 9) == 0 # Aries Navamsa starts Aries
assert varga_map(1, 0, 9) == 9 # Taurus Navamsa starts Capricorn (9th from sign)
+65
View File
@@ -4,9 +4,15 @@ from __future__ import annotations
from scripts.western_timing_engine import (
build_timing_techniques,
calculate_converse_secondary_progressions,
calculate_converse_solar_arc_directions,
calculate_lunar_return,
calculate_midpoints,
calculate_parans_status,
calculate_secondary_progressions,
calculate_solar_arc_directions,
calculate_solar_return,
calculate_transit_duration_scan,
calculate_transit_to_natal,
)
@@ -57,3 +63,62 @@ def test_solar_arc_uses_secondary_progressed_sun_arc() -> None:
assert directions["method"] == "secondary_progressed_sun_arc"
assert 0 < directions["solar_arc_degrees"] < 40
assert directions["directed_points"]["sun"]["longitude"] != directions["natal_sun_longitude"]
def test_converse_progressions_and_solar_arc_are_auditable() -> None:
progressions = calculate_converse_secondary_progressions(**_BIRTH, target_date="2026-07-09")
assert progressions["technique"] == "converse_secondary_progressions"
assert progressions["progressed_angles"]["status"] == "blocked"
directions = calculate_converse_solar_arc_directions(**_BIRTH, target_date="2026-07-09")
assert directions["technique"] == "converse_solar_arc_directions"
assert 0 < directions["converse_solar_arc_degrees"] < 40
assert directions["directed_points"]["sun"]["longitude"] != directions["natal_sun_longitude"]
def test_midpoints_emit_geometry_and_optional_transit_hits() -> None:
midpoints = calculate_midpoints(**_BIRTH, target_date="2026-07-09")
assert midpoints["technique"] == "midpoints"
assert "sun/moon" in midpoints["natal_midpoints"]
assert isinstance(midpoints["transit_midpoint_hits"], list)
def test_lunar_return_calculates_next_exact_return_chart() -> None:
lunar_return = calculate_lunar_return(**_BIRTH, start_date="2026-07-01")
assert lunar_return["technique"] == "lunar_return"
assert lunar_return["moon_longitude_delta"] < 0.01
assert lunar_return["return_chart"]["natal"]["planets"]["moon"]["sign"]
def test_transit_duration_scan_groups_daily_windows() -> None:
scan = calculate_transit_duration_scan(**_BIRTH, start_date="2026-07-01", end_date="2026-07-03")
assert scan["technique"] == "transit_duration_scan"
assert scan["days_scanned"] == 3
assert len(scan["daily_hits"]) == 3
assert isinstance(scan["windows"], list)
def test_parans_are_explicitly_blocked_until_solver_exists() -> None:
parans = calculate_parans_status(**_BIRTH, target_date="2026-07-09")
assert parans["technique"] == "parans"
assert parans["status"] == "blocked"
def test_timing_builder_can_emit_advanced_layers() -> None:
timing = build_timing_techniques(
**_BIRTH,
converse_secondary_progression_date="2026-07-09",
converse_solar_arc_date="2026-07-09",
midpoint_date="2026-07-09",
lunar_return_start_date="2026-07-01",
duration_scan_start_date="2026-07-01",
duration_scan_end_date="2026-07-02",
parans_date="2026-07-09",
)
assert {
"converse_secondary_progressions",
"converse_solar_arc_directions",
"midpoints",
"lunar_return",
"transit_duration_scan",
"parans",
} <= set(timing)