fix(rectification): fold D24 into education refine and score family D3

Window scan now drives d5_refine when D24 changes, family events use D3, and pada/Hora/Ghati are display-only. New cases bind Skill 10.0.7 without unique-minute confirmation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-20 11:04:55 +08:00
parent 9a5cf67209
commit 70efb56926
35 changed files with 894 additions and 72 deletions
+58 -4
View File
@@ -12,6 +12,7 @@ import hashlib
import json
import sys
from datetime import date, datetime, time, timedelta
from functools import lru_cache
from pathlib import Path
from collections.abc import Sequence
from typing import Any, Final, assert_never
@@ -39,7 +40,10 @@ import functional_benefics # noqa: E402
import jaimini # noqa: E402
import shadbala # noqa: E402
import narayana_dasha # noqa: E402
import saham_daynight # noqa: E402
import special_lagnas # noqa: E402
import varga # noqa: E402
from scripts.rectification.refinement_packet import NAKSHATRA_SPAN # noqa: E402
AYANAMSA: Final = "lahiri"
NODE_MODE: Final = "mean"
@@ -53,8 +57,8 @@ DOMAIN_CONFIG: Final[dict[EventDomain, DomainConfig]] = {
"career": (("D10",), (10,)),
"finance": (("D2", "D11"), (2, 11)),
"health_pressure": (("D30",), (6, 8, 12)),
# 六亲: D12 parents, D7 children/spouse detail, plus D1 houses 3/4/5/9.
"family": (("D12", "D7"), (3, 4, 5, 9)),
# 六亲: D12 parents, D7 children/spouse detail, D3 siblings, plus D1 houses 3/4/5/9.
"family": (("D12", "D7", "D3"), (3, 4, 5, 9)),
# Dated appearance/marks: D1 lagna / 1st house only. Not a primary formula.
"appearance": ((), (1,)),
}
@@ -340,6 +344,49 @@ def _arudha_sign(arudha_padas: dict, key: str) -> int | None:
return int(sign_index) if isinstance(sign_index, int) and 0 <= sign_index <= 11 else None
def _pada_index(longitude: float) -> int:
return int((float(longitude) % 360.0) / (NAKSHATRA_SPAN / 4.0)) % 108
@lru_cache(maxsize=64)
def _sunrise_local_naive(date_iso: str, lat: float, lon: float, tz: float) -> datetime | None:
"""Real sunrise only. Polar or unavailable locations omit Hora/Ghati; never invent 06:00."""
try:
import swisseph as swe
noon = datetime.fromisoformat(f"{date_iso}T12:00:00")
row = saham_daynight.determine_daytime(noon, lat=lat, lon=lon, tz=tz)
year, month, day, ut_hours = swe.revjul(float(row["sunrise_jd_ut"]))
return datetime(int(year), int(month), int(day)) + timedelta(hours=float(ut_hours) + float(tz))
except (saham_daynight.SahamDayNightError, TypeError, ValueError, OverflowError, OSError):
return None
def _fine_minute_indices(
*,
ascendant_longitude: float,
candidate_at: datetime,
lat: float,
lon: float,
tz: float,
) -> dict[str, int]:
indices: dict[str, int] = {"pada_index": _pada_index(ascendant_longitude)}
sunrise = _sunrise_local_naive(candidate_at.date().isoformat(), lat, lon, tz)
if sunrise is None:
return indices
calculator = special_lagnas.SpecialLagnasCalculator()
wrapped = float(ascendant_longitude) % 360.0
try:
hora = calculator.calculate_hora_lagna(wrapped, 0.0, candidate_at, sunrise)
ghati = calculator.calculate_ghati_lagna(wrapped, candidate_at, sunrise)
hora_degree = float(hora["degree"])
ghati_degree = float(ghati["degree"])
except (TypeError, ValueError, KeyError, OverflowError):
return indices
indices["hora_sign_index"] = int(hora_degree // 30.0) % 12
indices["ghati_sign_index"] = int(ghati_degree // 30.0) % 12
return indices
def build_candidate_static_context(
request: RectificationEventRequest,
candidate_at: datetime,
@@ -369,12 +416,12 @@ def build_candidate_static_context(
charts = varga.calc_all_vargas(
planet_longitudes,
ascendant_longitude,
divisions=[2, 4, 5, 7, 9, 10, 12, 24, 30],
divisions=[2, 3, 4, 5, 7, 9, 10, 12, 24, 30],
)
d11_chart = _d11_chart(planet_longitudes, ascendant_longitude)
varga_charts = {
prefix: d11_chart if prefix == "D11" else _varga_chart(charts, prefix)
for prefix in ("D2", "D4", "D5", "D7", "D9", "D10", "D11", "D12", "D24", "D30")
for prefix in ("D2", "D3", "D4", "D5", "D7", "D9", "D10", "D11", "D12", "D24", "D30")
}
available_layers = ["D1"]
blocked_layers = ["KP_cusps"]
@@ -417,6 +464,13 @@ def build_candidate_static_context(
"time": candidate_at.strftime("%H:%M"),
"ascendant_degree": ascendant_longitude,
"ascendant_sign_index": ascendant_index,
**_fine_minute_indices(
ascendant_longitude=ascendant_longitude,
candidate_at=candidate_at,
lat=float(request["lat"]),
lon=float(request["lon"]),
tz=float(request["tz"]),
),
"varga_ascendants": varga_ascendants,
"arudha_signs": arudha_signs,
"available_layers": sorted(set(available_layers)),