fix(tajika): year lord blocked instead of Muntha-lord stand-in (BUG-1223)
Panchadhikari blocked -> status blocked + reason, year_lord None, sentence 「年主暂无法确定(原因)」 on card and report; Tri-Pataka blocked with it; Mudda unchanged. Aligned with upstream 03bea6ed. Nine public charts: both producers byte-identical before/after. 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:
co-authored by
Claude Opus 5.5
parent
77645ce25f
commit
7dda1b45c3
@@ -277,11 +277,13 @@ def _field_value_is_blocked(value: Any) -> bool:
|
||||
|
||||
def _field_with_conflict_gate(key: str, solar_report: dict[str, Any], tajika_report: dict[str, Any]) -> dict[str, Any]:
|
||||
values = []
|
||||
blocked = []
|
||||
for producer, report in (("solar_return", solar_report), ("tajika", tajika_report)):
|
||||
if not isinstance(report, dict) or report.get(key) is None or report.get("error"):
|
||||
continue
|
||||
value = report[key]
|
||||
if _field_value_is_blocked(value):
|
||||
blocked.append({"producer": producer, "value": value})
|
||||
continue
|
||||
values.append({"producer": producer, "value": value})
|
||||
comparable_values = [_annual_comparable_value(key, item["value"]) for item in values]
|
||||
@@ -299,12 +301,20 @@ def _field_with_conflict_gate(key: str, solar_report: dict[str, Any], tajika_rep
|
||||
"data": values[0]["value"],
|
||||
"producers_checked": values,
|
||||
}
|
||||
return {
|
||||
field = {
|
||||
"status": "blocked",
|
||||
"field": key,
|
||||
"reason": f"{key}_missing",
|
||||
"producers_checked": values,
|
||||
}
|
||||
if blocked:
|
||||
# BUG-1223: keep the producer's own blocked record (reason and the
|
||||
# "年主暂无法确定(原因)" sentence) instead of a bare "<key>_missing".
|
||||
first = blocked[0]["value"]
|
||||
field["reason"] = first.get("reason") or field["reason"]
|
||||
field["blocked_detail"] = first
|
||||
field["producers_blocked"] = blocked
|
||||
return field
|
||||
|
||||
|
||||
def _dasha_field(label: str, solar_report: dict[str, Any], tajika_report: dict[str, Any], key: str) -> dict[str, Any]:
|
||||
@@ -643,6 +653,10 @@ def _field_brief(label: str, field: dict[str, Any] | None) -> str:
|
||||
return "Tajika Yogas: annual candidate structures visible"
|
||||
if label == "Sahams":
|
||||
return "Sahams: annual sensitive points visible"
|
||||
if status == "blocked" and label == "Year Lord":
|
||||
detail = field.get("blocked_detail") if isinstance(field.get("blocked_detail"), dict) else {}
|
||||
if detail.get("selection_basis_text"): # BUG-1223: 「年主暂无法确定(原因)」
|
||||
return f"{label}: {detail['selection_basis_text']}"
|
||||
return f"{label}: {status}"
|
||||
|
||||
|
||||
|
||||
@@ -924,7 +924,12 @@ def build_annual_tajika(birth_payload: dict, chart: dict, *, reference: datetime
|
||||
producers = {key: field.get("status") for key, field in fields.items()}
|
||||
periods = fields["mudda_dasha"].get("periods")
|
||||
required = ("solar_return", "annual_chart", "muntha", "year_lord", "mudda_dasha")
|
||||
unreliable = [key for key in required if producers.get(key) != "partial_verified"]
|
||||
# BUG-1223: a year lord the producers blocked (annual chart incomplete,
|
||||
# no stand-in planet) does not block the card; it shows
|
||||
# "年主暂无法确定(原因)". Mudda does not depend on the year lord.
|
||||
year_lord_blocked = _record(fields["year_lord"].get("blocked_detail")) if producers.get("year_lord") == "blocked" else {}
|
||||
unreliable = [key for key in required
|
||||
if producers.get(key) != "partial_verified" and not (key == "year_lord" and year_lord_blocked)]
|
||||
if start is None or end is None or end <= start:
|
||||
unreliable.append("solar_return_dates")
|
||||
if not isinstance(periods, list) or not periods:
|
||||
@@ -939,7 +944,7 @@ def build_annual_tajika(birth_payload: dict, chart: dict, *, reference: datetime
|
||||
}
|
||||
annual_chart = _record(fields["annual_chart"].get("data"))
|
||||
muntha = _record(fields["muntha"].get("data"))
|
||||
year_lord_data = _record(fields["year_lord"].get("data"))
|
||||
year_lord_data = year_lord_blocked or _record(fields["year_lord"].get("data"))
|
||||
year_lord = year_lord_data.get("year_lord")
|
||||
annual_lagna_idx = _sign_index(annual_chart.get("asc_sign"))
|
||||
focus = {"Sun", "Moon", year_lord}
|
||||
|
||||
+10
-10
@@ -14627,7 +14627,7 @@ def _tajika_annual_core(args, planet_lons, asc_si, age, mode='all'):
|
||||
"""
|
||||
try:
|
||||
sys.path.insert(0, SCRIPT_DIR)
|
||||
from tajika import calc_muntha, calc_year_lord, calc_tri_pataka
|
||||
from tajika import calc_muntha, calc_tri_pataka
|
||||
from tajika_mudda import annual_mudda_dasha
|
||||
except ImportError as e:
|
||||
return {"error": f"tajika模块导入失败: {e}"}
|
||||
@@ -14652,15 +14652,13 @@ def _tajika_annual_core(args, planet_lons, asc_si, age, mode='all'):
|
||||
|
||||
def _year_lord() -> Dict:
|
||||
# Panchadhikari on the solar-return chart, same helper as
|
||||
# solar_return_full_report (BUG-1214); Muntha lord only when the
|
||||
# annual chart cannot be computed (recorded as fallback, not hidden).
|
||||
from tajika_year_lord import select_annual_year_lord
|
||||
# solar_return_full_report (BUG-1214). No annual chart -> blocked, no
|
||||
# stand-in Muntha lord (BUG-1223, aligned with upstream 03bea6ed).
|
||||
from tajika_year_lord import blocked_year_lord, select_annual_year_lord
|
||||
sr = _solar_return()
|
||||
if not _sr_usable(sr):
|
||||
fallback = calc_year_lord(asc_si, age)
|
||||
fallback['selection_basis'] = 'fallback_muntha_sign_lord_after_panchadhikari_block'
|
||||
fallback['fallback_reason'] = (sr or {}).get('error', 'solar_return_chart_unavailable') if isinstance(sr, dict) else 'solar_return_chart_unavailable'
|
||||
return fallback
|
||||
detail = sr.get('error') if isinstance(sr, dict) else None
|
||||
return blocked_year_lord('solar_return_chart_unavailable', detail)
|
||||
return select_annual_year_lord(
|
||||
sr, natal_asc_sign_idx=sr['birth_asc_sign_idx'], completed_years=int(age),
|
||||
ayanamsa_name=sr_cache['ayanamsa'], birth_lat=float(args.lat), birth_lon=float(args.lon), birth_tz=float(args.tz),
|
||||
@@ -14685,9 +14683,11 @@ def _tajika_annual_core(args, planet_lons, asc_si, age, mode='all'):
|
||||
if mode in ('all', 'mudda'):
|
||||
result['mudda_dasha'] = _mudda()
|
||||
if mode in ('all', 'tripataka'):
|
||||
varsha_lord = year_lord.get('year_lord', 'Jupiter')
|
||||
varsha_lord = year_lord.get('year_lord')
|
||||
muntha_si = (asc_si + age) % 12
|
||||
result['tri_pataka'] = calc_tri_pataka(planet_lons, varsha_lord, muntha_si)
|
||||
# BUG-1223: no year lord -> no Tri-Pataka (no stand-in planet).
|
||||
result['tri_pataka'] = (calc_tri_pataka(planet_lons, varsha_lord, muntha_si) if varsha_lord
|
||||
else {'status': 'blocked', 'reason': 'year_lord_blocked'})
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -177,6 +177,13 @@ _PHRASES: dict[str, str] = {
|
||||
",12 等分": ", twelve equal parts",
|
||||
# BUG-1214: tajika_year_lord.selection_basis_text (zh) → its en twin.
|
||||
"年盘不全,暂用 Muntha 主星": "Annual chart incomplete; Muntha sign lord used for now",
|
||||
# BUG-1223: tajika_year_lord.blocked_text (no stand-in year lord).
|
||||
"年主暂无法确定(年盘缺太阳或月亮位置)": "Year lord cannot be determined yet (the annual chart lacks the Sun or Moon position)",
|
||||
"年主暂无法确定(年盘缺行星位置,五分力量无法计算)": "Year lord cannot be determined yet (planet positions are missing from the annual chart, so the five-fold strength cannot be computed)",
|
||||
"年主暂无法确定(五类候选星都不在年盘上)": "Year lord cannot be determined yet (none of the five candidates is in the annual chart)",
|
||||
"年主暂无法确定(年盘无法计算)": "Year lord cannot be determined yet (the annual chart could not be computed)",
|
||||
"年主暂无法确定(缺少 Muntha 星座)": "Year lord cannot be determined yet (the Muntha sign is missing)",
|
||||
"年主暂无法确定(年盘数据不全)": "Year lord cannot be determined yet (the annual chart data are incomplete)",
|
||||
"(Panchavargiya 为本地近似计算)": " (Panchavargiya is a local approximation)",
|
||||
# BUG-1217: tajika_year_lord.ENGINE_NOTE["tajika_native_five_fold"]
|
||||
"(按 Tajika 五分力量)": " (by Tajika five-fold strength)",
|
||||
|
||||
@@ -577,6 +577,10 @@ def render_pl9_parity_markdown(packet: dict) -> str:
|
||||
|
||||
def _annual_selection_basis(year_lord, muntha) -> str:
|
||||
year_lord = _dict(year_lord)
|
||||
blocked_detail = _dict(year_lord.get('blocked_detail'))
|
||||
if year_lord.get('status') == 'blocked' and blocked_detail.get('selection_basis_text'):
|
||||
# BUG-1223: no stand-in year lord; 「年主暂无法确定(原因)」.
|
||||
return _cell(blocked_detail['selection_basis_text'])
|
||||
if year_lord.get('status') == 'conflict':
|
||||
return '两种计算口径给出的年主不同,暂不合并'
|
||||
data = _dict(year_lord.get('data') or year_lord)
|
||||
|
||||
+12
-11
@@ -559,18 +559,15 @@ def solar_return_full_report(
|
||||
result['muntha'] = {'error': str(e)}
|
||||
|
||||
# 3. Year Lord:Panchadhikari 选法(占星师第四轮问 3,BUG-1214)。
|
||||
# 与 cmd_tajika 共用 tajika_year_lord.select_annual_year_lord;年盘不全
|
||||
# (降级模式或 Muntha 缺失)时才用 Muntha 主星,并写明 fallback。
|
||||
# 与 cmd_tajika 共用 tajika_year_lord.select_annual_year_lord;年盘不全时
|
||||
# 返回 blocked 与原因,不用 Muntha 主星顶替(BUG-1223)。
|
||||
try:
|
||||
muntha_data = result.get('muntha', {})
|
||||
muntha_sign_val = muntha_data.get('muntha_sign_idx') if isinstance(muntha_data, dict) else None
|
||||
from tajika_year_lord import blocked_year_lord, select_annual_year_lord
|
||||
if not isinstance(muntha_sign_val, int) or isinstance(muntha_sign_val, bool):
|
||||
result['year_lord'] = {
|
||||
'status': 'blocked',
|
||||
'reason': 'muntha_sign_required_for_year_lord',
|
||||
}
|
||||
result['year_lord'] = blocked_year_lord('muntha_sign_required_for_year_lord')
|
||||
else:
|
||||
from tajika_year_lord import select_annual_year_lord
|
||||
result['year_lord'] = select_annual_year_lord(
|
||||
sr,
|
||||
natal_asc_sign_idx=sr['birth_asc_sign_idx'],
|
||||
@@ -624,11 +621,13 @@ def solar_return_full_report(
|
||||
try:
|
||||
from tajika import calc_tri_pataka
|
||||
yl_data = result.get('year_lord', {})
|
||||
yl = yl_data.get('year_lord', '')
|
||||
yl = yl_data.get('year_lord') if isinstance(yl_data, dict) else None
|
||||
muntha_data = result.get('muntha', {})
|
||||
ms = muntha_data.get('muntha_sign_idx', 0)
|
||||
tri = calc_tri_pataka(planet_lons, yl, ms)
|
||||
result['tri_pataka'] = tri
|
||||
if yl:
|
||||
result['tri_pataka'] = calc_tri_pataka(planet_lons, yl, ms)
|
||||
else: # BUG-1223: no year lord -> no Tri-Pataka (no stand-in planet).
|
||||
result['tri_pataka'] = {'status': 'blocked', 'reason': 'year_lord_blocked'}
|
||||
except Exception as e:
|
||||
result['tri_pataka'] = {'error': str(e)}
|
||||
|
||||
@@ -708,8 +707,10 @@ def _make_sr_summary(result: Dict) -> str:
|
||||
lines.append(f" → {muntha.get('interpretation','')[:60]}")
|
||||
|
||||
yl = result.get('year_lord', {})
|
||||
if 'year_lord' in yl:
|
||||
if yl.get('year_lord'):
|
||||
lines.append(f" Year Lord:{yl['year_lord']}")
|
||||
elif yl.get('selection_basis_text'):
|
||||
lines.append(f" Year Lord:{yl['selection_basis_text']}")
|
||||
|
||||
tp = result.get('tri_pataka', {})
|
||||
if 'verdict' in tp:
|
||||
|
||||
+45
-13
@@ -26,6 +26,13 @@ Drekkana, Navamsa; raw out of 80, score = raw / 4 out of 20;
|
||||
`engine: tajika_native_five_fold`); the dignity proxy no longer selects a year
|
||||
lord. Set JYOTISH_YEAR_LORD_PYJHORA_REFERENCE=1 to pass the annual JD and let
|
||||
the upstream PyJHora lookup run (research comparison only).
|
||||
|
||||
No stand-in year lord (BUG-1223, product owner 2026-10-04, aligned with
|
||||
upstream 03bea6ed `docs/research/tajika_shared_entrypoint_repair_2026_10_03.md`
|
||||
item 3): when Panchadhikari is blocked (annual chart incomplete) the result is
|
||||
`status: "blocked"` with the reason, `year_lord: None`, and the sentence
|
||||
"年主暂无法确定(原因)"; the Muntha sign lord is no longer put in its place.
|
||||
Mudda is independent of the year lord (BUG-1216) and is unaffected.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -36,14 +43,23 @@ import os
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
try: # scripts/ on sys.path
|
||||
from tajika import SIGNS, SIGN_LORDS, calc_panchadhikari_year_lord
|
||||
from tajika import calc_panchadhikari_year_lord
|
||||
except ImportError: # pragma: no cover - package import
|
||||
from scripts.tajika import SIGNS, SIGN_LORDS, calc_panchadhikari_year_lord
|
||||
from scripts.tajika import calc_panchadhikari_year_lord
|
||||
|
||||
PYJHORA_REFERENCE_ENV = "JYOTISH_YEAR_LORD_PYJHORA_REFERENCE"
|
||||
SOURCE = "tajika.calc_panchadhikari_year_lord (upstream yinduzhanxing c7117f92)"
|
||||
MUNTHA_BASIS = "natal_ascendant_plus_completed_years"
|
||||
FALLBACK_BASIS = "fallback_muntha_sign_lord_after_panchadhikari_block"
|
||||
BLOCKED_BASIS = "panchadhikari_blocked_no_year_lord"
|
||||
# Reason token -> (zh, en) for "年主暂无法确定(原因)".
|
||||
BLOCKED_REASON_TEXT = {
|
||||
"sun_or_moon_missing_for_panchadhikari": ("年盘缺太阳或月亮位置", "the annual chart lacks the Sun or Moon position"),
|
||||
"tajika_five_fold_panchavargiya_unavailable": ("年盘缺行星位置,五分力量无法计算", "planet positions are missing from the annual chart, so the five-fold strength cannot be computed"),
|
||||
"no_panchadhikari_candidates_available": ("五类候选星都不在年盘上", "none of the five candidates is in the annual chart"),
|
||||
"solar_return_chart_unavailable": ("年盘无法计算", "the annual chart could not be computed"),
|
||||
"muntha_sign_required_for_year_lord": ("缺少 Muntha 星座", "the Muntha sign is missing"),
|
||||
}
|
||||
BLOCKED_REASON_DEFAULT = ("年盘数据不全", "the annual chart data are incomplete")
|
||||
|
||||
ENGINE_NOTE = {
|
||||
"tajika_native_five_fold": ("按 Tajika 五分力量", "by Tajika five-fold strength"),
|
||||
@@ -61,8 +77,8 @@ def selection_basis_text(data: Dict[str, Any]) -> Tuple[str, str]:
|
||||
"""One sentence (zh, en) saying why this planet is the year lord."""
|
||||
basis = data.get("selection_basis")
|
||||
lord = data.get("year_lord")
|
||||
if basis == FALLBACK_BASIS:
|
||||
return ("年盘不全,暂用 Muntha 主星", "Annual chart incomplete; Muntha sign lord used for now")
|
||||
if basis == BLOCKED_BASIS or data.get("status") == "blocked":
|
||||
return blocked_text(data.get("reason"))
|
||||
candidates = [row for row in data.get("candidates") or [] if isinstance(row, dict)]
|
||||
benefic = [row for row in candidates if row.get("benefic_aspects_asc")]
|
||||
malefic = [row for row in candidates if row.get("malefic_aspects_asc")]
|
||||
@@ -91,6 +107,29 @@ def selection_basis_text(data: Dict[str, Any]) -> Tuple[str, str]:
|
||||
return zh, en
|
||||
|
||||
|
||||
def blocked_text(reason: Any) -> Tuple[str, str]:
|
||||
"""「年主暂无法确定(原因)」 and its English twin."""
|
||||
zh, en = BLOCKED_REASON_TEXT.get(str(reason), BLOCKED_REASON_DEFAULT)
|
||||
return f"年主暂无法确定({zh})", f"Year lord cannot be determined yet ({en})"
|
||||
|
||||
|
||||
def blocked_year_lord(reason: Any, detail: Any = None) -> Dict[str, Any]:
|
||||
"""Blocked year lord: no planet, reason and sentence only (BUG-1223)."""
|
||||
data: Dict[str, Any] = {
|
||||
"status": "blocked",
|
||||
"reason": str(reason or "panchadhikari_blocked"),
|
||||
"year_lord": None,
|
||||
"selection_basis": BLOCKED_BASIS,
|
||||
"muntha_basis": MUNTHA_BASIS,
|
||||
"candidate_list": [],
|
||||
"source": SOURCE,
|
||||
}
|
||||
if detail is not None:
|
||||
data["detail"] = detail
|
||||
data["selection_basis_text"], data["selection_basis_text_en"] = blocked_text(data["reason"])
|
||||
return data
|
||||
|
||||
|
||||
def _candidate_list(candidates: List[Dict[str, Any]], winner: Optional[str]) -> List[Dict[str, Any]]:
|
||||
return [{
|
||||
"source": row.get("source"),
|
||||
@@ -142,14 +181,7 @@ def select_annual_year_lord(
|
||||
data = _select(sr, natal_asc_sign_idx, completed_years, muntha_sign_idx,
|
||||
ayanamsa_name, birth_lat, birth_lon, birth_tz)
|
||||
if data.get("status") != "partial_verified":
|
||||
lord = SIGN_LORDS[SIGNS[muntha_sign_idx]]
|
||||
data = {
|
||||
"year_lord": lord,
|
||||
"year_lord_sign_idx": muntha_sign_idx,
|
||||
"year_lord_sign": SIGNS[muntha_sign_idx],
|
||||
"selection_basis": FALLBACK_BASIS,
|
||||
"fallback_reason": data.get("reason", "panchadhikari_blocked"),
|
||||
}
|
||||
return blocked_year_lord(data.get("reason", "panchadhikari_blocked"), data.get("detail"))
|
||||
data["muntha_basis"] = MUNTHA_BASIS
|
||||
data["candidate_list"] = _candidate_list(data.get("candidates") or [], data.get("year_lord"))
|
||||
data["source"] = SOURCE
|
||||
|
||||
Reference in New Issue
Block a user