fix(qizheng): pass school options into the vendored chart and stabilize gates
Independent Staging Quality Gate / validate (push) Failing after 8m1s
Independent Staging Quality Gate / publish (push) Skipped

BUG-710: call getSevenGovernorsChart with ketuMode/siderealMode so
calculation.ketu_mode is the engine school, not the request echo.
BUG-711: stop forbidding /ephemeris in the sidebar contract.
BUG-712: compare ephemeris event longitudes at 6 decimals and fail
closed when the golden is missing.
This commit is contained in:
jesse-ux
2026-09-15 17:54:20 +08:00
parent 6bb892b826
commit a38a941522
12 changed files with 314 additions and 31 deletions
+35 -14
View File
@@ -169,21 +169,34 @@ def _node_bin() -> str:
return found
def _run_cli(iso: str, lat: float, lon: float, timeout_seconds: int) -> dict[str, Any]:
EVAL_HELPER = REPO_ROOT / "scripts" / "qizheng_seven_governors.js"
def _run_cli(
iso: str,
lat: float,
lon: float,
timeout_seconds: int,
*,
ketu_mode: str,
sidereal_mode: dict[str, Any],
) -> dict[str, Any]:
command = [
_node_bin(),
str(VENDORED_CLI),
"--date",
iso,
"--lat",
str(lat),
"--lng",
str(lon),
"--seven-governors",
"--json",
str(EVAL_HELPER),
]
if FORBIDDEN_CLI_FLAGS.intersection(command):
raise QizhengChartError("forbidden engine subcommand blocked", error_code="ERR_QIZHENG_ENGINE")
payload = json.dumps(
{
"date": iso,
"lat": lat,
"lng": lon,
"ketuMode": ketu_mode,
"siderealMode": sidereal_mode,
},
ensure_ascii=False,
)
try:
completed = subprocess.run(
command,
@@ -192,6 +205,7 @@ def _run_cli(iso: str, lat: float, lon: float, timeout_seconds: int) -> dict[str
capture_output=True,
text=True,
encoding="utf-8",
input=payload,
timeout=timeout_seconds,
)
except FileNotFoundError as exc:
@@ -242,8 +256,8 @@ def _normalize(raw: dict[str, Any], body: dict[str, Any], *, ketu_mode: str, sid
"iso_local": iso,
"latitude": float(body["lat"]),
"longitude": float(body["lon"]),
"ketu_mode": ketu_mode,
"sidereal_mode": sidereal_mode,
"ketu_mode": chart.get("ketuMode") or ketu_mode,
"sidereal_mode": chart.get("siderealMode") or sidereal_mode,
"engine_ketu_mode": chart.get("ketuMode"),
"engine_sidereal_mode": chart.get("siderealMode"),
"timeout_seconds": DEFAULT_TIMEOUT_SECONDS,
@@ -278,14 +292,21 @@ def calculate_qizheng_chart(body: dict[str, Any], *, timeout_seconds: int = DEFA
"""Compute a complete native 七政四余 natal chart from JSON-like input."""
if not isinstance(body, dict):
raise QizhengChartError("request body must be an object", error_code="ERR_QIZHENG_INPUT")
if not VENDORED_CLI.exists():
if not VENDORED_CLI.exists() or not EVAL_HELPER.exists():
raise QizhengChartError("vendored 七政四余 engine is unavailable", error_code="ERR_QIZHENG_CLI_MISSING")
lat = _number(body.get("lat", body.get("latitude")), "lat", -90, 90)
lon = _number(body.get("lon", body.get("longitude", body.get("lng"))), "lon", -180, 180)
iso = _iso_local(body)
ketu_mode = _ketu_mode(body)
sidereal_mode = _sidereal_mode(body)
raw = _run_cli(iso, lat, lon, timeout_seconds)
raw = _run_cli(
iso,
lat,
lon,
timeout_seconds,
ketu_mode=ketu_mode,
sidereal_mode=sidereal_mode,
)
return _normalize(raw, {**body, "lat": lat, "lon": lon}, ketu_mode=ketu_mode, sidereal_mode=sidereal_mode, iso=iso)